<?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 on On My Way</title>
    <link>https://tuanzi-wow.cn/categories/langchain/</link>
    <description>Recent content in LangChain on On My Way</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <atom:link href="https://tuanzi-wow.cn/categories/langchain/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>9 月 20 日学习报告：LangChain Agent 的 Tool、Memory 与 Streaming</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/langchain-agent-study-report-2026-09-20/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/langchain-agent-study-report-2026-09-20/</guid>
      <description>按修改时间与源文件结构整理 Tool、short-term memory、Event Streaming 和 Streaming。</description>
      <content:encoded><![CDATA[<h1 id="9-月-20-日学习报告langchain-agent-的-toolmemory-与-streaming">9 月 20 日学习报告：LangChain Agent 的 Tool、Memory 与 Streaming</h1>
<p>仅整理 2026-09-20 修改的 4 个文件。文件按最后修改时间排列；各文件内部保持原文标题顺序与层级，只压缩重复表述和示例，不省略知识点。</p>
<h2 id="140852--toolsmd">14:08:52 — Tools.md</h2>
<h3 id="tools">Tools</h3>
<p>Tools extend what agents can do。</p>
<h3 id="create-tools">Create tools</h3>
<h4 id="basic-tool-definition">Basic tool definition</h4>
<p>定义 Tool 最简单的方式是使用 <code>@tool</code> decorator。函数 docstring 会成为 Tool description；参数 type hints 是必须的，因为它们定义 Tool 的输入 Schema。docstring 应内容丰富且简明扼要。</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></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.tools</span> <span class="kn">import</span> <span class="n">tool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">search_database</span><span class="p">(</span><span class="n">query</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Search the customer database for records matching the query.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">        query: Search terms to look for
</span></span></span><span class="line"><span class="cl"><span class="s2">        limit: Maximum number of results to return
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;Found </span><span class="si">{</span><span class="n">limit</span><span class="si">}</span><span class="s2"> results for &#39;</span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s2">&#39;&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="customize-tool-properties">Customize tool properties</h4>
<h5 id="custom-tool-name">Custom tool name</h5>
<p>Tool name 默认等于函数名，可通过 <code>@tool(&quot;web_search&quot;)</code> 覆盖。</p>
<h5 id="custom-tool-description">Custom tool description</h5>
<p>默认 description 来自 docstring。需要更明确地指导 model 时，使用 <code>description=</code> 覆盖自动生成的 description。</p>
<h4 id="advanced-schema-definition">Advanced schema definition</h4>
<p>复杂输入使用 Pydantic model 或 JSON Schema。<code>Field(description=...)</code> 描述字段，<code>Literal</code> 限定可选值，<code>@tool(args_schema=...)</code> 绑定输入 Schema。</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></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">Literal</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</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">WeatherInput</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">location</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;City name or coordinates&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">units</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;celsius&#34;</span><span class="p">,</span> <span class="s2">&#34;fahrenheit&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">default</span><span class="o">=</span><span class="s2">&#34;celsius&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">description</span><span class="o">=</span><span class="s2">&#34;Temperature unit preference&#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="n">include_forecast</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span><span class="p">(</span><span class="n">args_schema</span><span class="o">=</span><span class="n">WeatherInput</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">location</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">units</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&#34;celsius&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">include_forecast</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get current weather and optional forecast.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="reserved-argument-names">Reserved argument names</h4>
<ul>
<li><code>config</code> 保留给 <code>RunnableConfig</code>；</li>
<li><code>runtime</code> 保留给 <code>ToolRuntime</code>。</li>
</ul>
<p>二者不能用作普通业务参数，否则会产生 runtime error。</p>
<h3 id="access-context">Access context</h3>
<p>Tool 通过 <code>ToolRuntime</code> 访问 conversation history、user data、persistent memory 和执行信息。</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Component</th>
					<th style="text-align: left">访问方式</th>
					<th style="text-align: left">含义与用途</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left">State</td>
					<td style="text-align: left"><code>runtime.state</code></td>
					<td style="text-align: left">当前 conversation 的可变 short-term memory；messages、计数器、自定义字段</td>
			</tr>
			<tr>
					<td style="text-align: left">Context</td>
					<td style="text-align: left"><code>runtime.context</code></td>
					<td style="text-align: left">invocation 时传入的不可变配置；user ID、session info</td>
			</tr>
			<tr>
					<td style="text-align: left">Store</td>
					<td style="text-align: left"><code>runtime.store</code></td>
					<td style="text-align: left">跨 conversation 持久化的 long-term memory</td>
			</tr>
			<tr>
					<td style="text-align: left">Stream Writer</td>
					<td style="text-align: left"><code>runtime.stream_writer</code></td>
					<td style="text-align: left">Tool 执行时发送实时 custom updates</td>
			</tr>
			<tr>
					<td style="text-align: left">Execution Info</td>
					<td style="text-align: left"><code>runtime.execution_info</code></td>
					<td style="text-align: left">thread ID、run ID、attempt number</td>
			</tr>
			<tr>
					<td style="text-align: left">Server Info</td>
					<td style="text-align: left"><code>runtime.server_info</code></td>
					<td style="text-align: left">assistant ID、graph ID、authenticated user</td>
			</tr>
			<tr>
					<td style="text-align: left">Config</td>
					<td style="text-align: left"><code>runtime.config</code></td>
					<td style="text-align: left"><code>RunnableConfig</code> 的 callbacks、tags、metadata</td>
			</tr>
			<tr>
					<td style="text-align: left">Tool Call ID</td>
					<td style="text-align: left"><code>runtime.tool_call_id</code></td>
					<td style="text-align: left">当前 Tool invocation ID，用于匹配 <code>ToolMessage</code></td>
			</tr>
	</tbody>
</table>
<h4 id="short-term-memorystate">Short-term memory(State)</h4>
<p>State 包含 message history 和 graph state 中定义的自定义字段。</p>
<h5 id="access-state">Access state</h5>
<p>在 Tool 签名中声明 <code>runtime: ToolRuntime</code>。<code>ToolNode</code> 会自动注入它，但它不会出现在发送给 model 的 Tool Schema 中。Tool 通过 <code>runtime.state</code> 读取 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><span class="lnt">14
</span><span class="lnt">15
</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.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.tools</span> <span class="kn">import</span> <span class="n">ToolRuntime</span><span class="p">,</span> <span class="n">tool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_last_user_message</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get the most recent message from the user.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="n">runtime</span><span class="o">.</span><span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">HumanMessage</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">message</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;No user messages found&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_preference</span><span class="p">(</span><span class="n">pref_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get a user preference value.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">runtime</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;user_preferences&#34;</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pref_name</span><span class="p">,</span> <span class="s2">&#34;Not set&#34;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>第二个 Tool 的 model-visible Schema 只有 <code>pref_name</code>，没有 <code>runtime</code>。</p>
<h5 id="update-state">Update state</h5>
<p>Tool 更新 State 时返回 <code>Command</code>。当目标是当前 graph 时：</p>
<ol>
<li>在 <code>Command.update</code> 写入 State 字段；</li>
<li>在 <code>Command.update[&quot;messages&quot;]</code> 中加入 <code>ToolMessage</code>；</li>
<li>结果写入 <code>ToolMessage.content</code>；</li>
<li><code>ToolMessage.tool_call_id</code> 必须等于 <code>runtime.tool_call_id</code>。</li>
</ol>
<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></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</span> <span class="kn">import</span> <span class="n">AgentState</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</span> <span class="kn">import</span> <span class="n">ToolRuntime</span><span class="p">,</span> <span class="n">tool</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 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">user_name</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">set_user_name</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">[</span><span class="kc">None</span><span class="p">,</span> <span class="n">CustomState</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">Command</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Set the user&#39;s name in the conversation state.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">Command</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">update</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;user_name&#34;</span><span class="p">:</span> <span class="n">new_name</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></span><span class="line"><span class="cl">                <span class="n">ToolMessage</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">content</span><span class="o">=</span><span class="sa">f</span><span class="s2">&#34;User name set to </span><span class="si">{</span><span class="n">new_name</span><span class="si">}</span><span class="s2">.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">tool_call_id</span><span class="o">=</span><span class="n">runtime</span><span class="o">.</span><span class="n">tool_call_id</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 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>AIMessage</code> tool call 都必须有对应的 <code>ToolMessage</code>。消息缺失或 ID 不匹配时，<code>ToolNode</code> 抛出 <code>ValueError</code>。多个 Tool 并行更新同一 State 字段时，应定义 reducer，明确覆盖、追加或其他合并规则。</p>
<h4 id="context">Context</h4>
<p>Context 是 invocation 时传入的不可变配置，包括 user ID、session details 和 application settings。使用 <code>context_schema</code> 声明结构，通过 <code>context=</code> 传入，Tool 从 <code>runtime.context</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></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">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserContext</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="nb">str</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_account_info</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">[</span><span class="n">UserContext</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get the current user&#39;s account information.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">USER_DATABASE</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">user_id</span><span class="p">,</span> <span class="s2">&#34;User not found&#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;openai: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">get_account_info</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">context_schema</span><span class="o">=</span><span class="n">UserContext</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="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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s my balance?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;thread-1&#34;</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">    <span class="n">context</span><span class="o">=</span><span class="n">UserContext</span><span class="p">(</span><span class="n">user_id</span><span class="o">=</span><span class="s2">&#34;user123&#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><p><code>thread_id</code> 标识 conversation；<code>user_id</code> 标识业务用户，二者不同。</p>
<h4 id="long-term-memorystore">Long-term memory(Store)</h4>
<p><code>BaseStore</code> 提供跨 conversation 持久化的数据。使用 <code>runtime.store</code> 访问，以 namespace/key 组织记录。</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></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="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_info</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Look up user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">item</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">get</span><span class="p">((</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="k">if</span> <span class="n">item</span> <span class="k">else</span> <span class="s2">&#34;Unknown user&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">save_user_info</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="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">age</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">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Save user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">put</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="n">user_id</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span> <span class="s2">&#34;age&#34;</span><span class="p">:</span> <span class="n">age</span><span class="p">,</span> <span class="s2">&#34;email&#34;</span><span class="p">:</span> <span class="n">email</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="k">return</span> <span class="s2">&#34;Successfully saved user info.&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>把同一 Store 传给 <code>create_agent(store=store)</code> 后，可在一个 session 保存、另一个 session 读取。</p>
<h4 id="streaming-writer">Streaming writer</h4>
<p><code>runtime.stream_writer</code> 可在 Tool 执行中发送实时 custom updates，适合长任务进度；Tool 最终仍用 <code>return</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></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="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a given city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="o">.</span><span class="n">stream_writer</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Looking up data for city: </span><span class="si">{</span><span class="n">city</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="o">.</span><span class="n">stream_writer</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Acquired data for city: </span><span class="si">{</span><span class="n">city</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">return</span> <span class="sa">f</span><span class="s2">&#34;It&#39;s always sunny in </span><span class="si">{</span><span class="n">city</span><span class="si">}</span><span class="s2">!&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="execution-info">Execution info</h4>
<p><code>runtime.execution_info</code> 提供 <code>thread_id</code>、<code>run_id</code> 和 <code>node_attempt</code>，用于日志关联和重试判断。</p>
<h4 id="server-info">Server info</h4>
<p>在 LangGraph Server 上，<code>runtime.server_info</code> 提供 <code>assistant_id</code>、<code>graph_id</code> 和 authenticated user；非 Server 环境下为 <code>None</code>。</p>
<h3 id="tool-execution">Tool execution</h3>
<p>Tool 由 Agent 使用；Tool error handling 由 middleware 配置。</p>
<h4 id="tool-return-values">Tool return values</h4>
<h5 id="return-a-string">Return a string</h5>
<p>返回 human-readable <code>str</code>，供 model 下一步读取；框架自动将其转换为 <code>ToolMessage</code>。</p>
<h5 id="return-an-object">Return an object</h5>
<p>返回结构化 <code>object</code>，供 model 读取字段并推理。对象会序列化为 Tool output，但不会直接更新 graph State。</p>
<h5 id="return-multimodal-content">Return multimodal content</h5>
<p>多模态结果使用 standard content blocks，例如 text block 和 image block。结果转换成带 multimodal <code>content</code> 的 <code>ToolMessage</code>；可用 <code>message.content_blocks</code> 读取归一化 blocks。model 必须支持所返回的 image、audio 或 video modality。</p>
<h5 id="return-a-command">Return a Command</h5>
<p>需要更新 graph State 时返回 <code>Command</code>。若目标是当前 graph，必须显式提供与当前 tool call ID 匹配的 <code>ToolMessage</code>。普通 <code>str</code>/<code>object</code> 会自动包装为 <code>ToolMessage</code>，<code>Command</code> 不会。</p>
<h5 id="return-directly-from-a-tool">Return directly from a tool</h5>
<p><code>@tool(return_direct=True)</code> 使 agent loop 短路：Tool 正常执行，输出包装成 <code>ToolMessage</code> 后直接返回 caller，不再追加 model call。</p>
<p>适用于：Tool output 已是完整答案；希望避免额外 model call；需要 deterministic、unmodified output。</p>
<p>同一步包含多个并行 tool calls 时，会先执行整个批次；仅当每个 Tool 都设置 <code>return_direct=True</code> 时才路由到 <code>END</code>，最终响应包含全部 Tool outputs。</p>
<h5 id="return-a-command-with-return_direct">Return a Command with return_direct</h5>
<p><code>return_direct=True</code> 的 Tool 可返回 <code>Command</code>，在退出前更新 State。<code>Command</code> 仍不会自动生成 <code>ToolMessage</code>，必须手动加入 <code>tool_call_id=runtime.tool_call_id</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></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="nd">@tool</span><span class="p">(</span><span class="n">return_direct</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_and_store_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Command</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch order status and store it in state.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">status</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;Order </span><span class="si">{</span><span class="n">order_id</span><span class="si">}</span><span class="s2"> is shipped.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">Command</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">update</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;last_order_status&#34;</span><span class="p">:</span> <span class="n">status</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></span><span class="line"><span class="cl">                <span class="n">ToolMessage</span><span class="p">(</span><span class="n">content</span><span class="o">=</span><span class="n">status</span><span class="p">,</span> <span class="n">tool_call_id</span><span class="o">=</span><span class="n">runtime</span><span class="o">.</span><span class="n">tool_call_id</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 class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="error-handing">Error handing</h4>
<p>使用 middleware 重试失败 Tool，或把异常转换为 model 可处理的自定义 <code>ToolMessage</code>。异常结果的 <code>tool_call_id</code> 必须取原调用的 <code>request.tool_call[&quot;id&quot;]</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></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_tool_call</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></span><span class="line"><span class="cl"><span class="nd">@wrap_tool_call</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_tool_errors</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">handler</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ToolMessage</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">error</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">ToolMessage</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">content</span><span class="o">=</span><span class="sa">f</span><span class="s2">&#34;Tool error: Please check your input. (</span><span class="si">{</span><span class="n">error</span><span class="si">}</span><span class="s2">)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">tool_call_id</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">tool_call</span><span class="p">[</span><span class="s2">&#34;id&#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><h4 id="state-injection">State injection</h4>
<p>Tool 通过框架注入且对 model 隐藏的 <code>ToolRuntime</code> 访问 State：读取用 <code>runtime.state</code>，更新用带匹配 <code>ToolMessage</code> 的 <code>Command</code>。</p>
<h3 id="dynamic-tool-section">Dynamic tool section</h3>
<p>Tool 太多会占用 context，太少会限制能力。动态 Tool 根据启动时是否已知分为两类。</p>
<h5 id="filtering-pre-registered-tools">Filtering pre-registered tools</h5>
<p>全部 Tool 在创建 Agent 时已知：先预注册，再依据 State、permissions 或 Context 动态过滤暴露给 model 的集合。适合 Tool 静态、availability 动态的场景。</p>
<h5 id="runtime-tool-register">Runtime tool register</h5>
<p>Tool 在 runtime 才发现或创建时，需要同时处理：</p>
<ol>
<li><code>wrap_model_call</code>：用 <code>request.override(tools=[...])</code> 加入本次 model request；</li>
<li><code>wrap_tool_call</code>：识别对应 name，用 <code>request.override(tool=...)</code> 提供执行对象。</li>
</ol>
<p>只让 model 看见而不提供执行处理器，runtime 无法完成调用。此方式适合 MCP server、database、user configuration 和 external registry。</p>
<h3 id="headless-tools">Headless tools</h3>
<p>服务端只注册 name、description 和 parameter Schema，实现只在客户端。model 可正常调用，但执行通过 interrupt/resume 在进程外完成。</p>
<h4 id="when-to-use">when to use</h4>
<p>用于依赖客户端 environment、device 或 UI 的工作：Browser APIs、privacy/locality、latency、structured safe effects。</p>
<h4 id="how-it-work">How it work</h4>
<ol>
<li>定义 Tool Schema，不提供 in-process implementation；</li>
<li>将定义注册给 <code>create_agent</code>；</li>
<li>调用时 graph 暂停，返回 <code>{&quot;type&quot;:&quot;tool&quot;,&quot;tool_call&quot;:{&quot;id&quot;,&quot;name&quot;,&quot;args&quot;}}</code>；</li>
<li>客户端执行，再 resume graph。</li>
</ol>
<p><code>onTool</code> callback 可观察 <code>start</code>、<code>success</code>、<code>error</code>，用于 spinner 或 toast。</p>
<h3 id="prebuilt-tools">Prebuilt tools</h3>
<p>LangChain 提供 web search、code interpretation、database access 等 prebuilt tools/toolkits，可直接集成，无需自行实现。</p>
<h3 id="tools-from-mcp">Tools from MCP</h3>
<p>MCP 统一应用向 model 暴露 Tools 的方式。<code>MCPAdapter</code> 发现 MCP server 的 Tools，将 <code>list_tools()</code> 结果转换为 LangChain Tools，再传给 <code>create_agent</code>。</p>
<h3 id="server-side-tool-use">Server-side tool use</h3>
<p>部分 chat models 提供由 provider 执行的 built-in Tools，例如 web search 和 code interpreter，无需自行定义或托管 Tool logic；具体配置取决于 model integration。</p>
<h2 id="153837--short-term_memorymd">15:38:37 — Short-term_memory.md</h2>
<p>Conversation history 是最常见的 short-term memory；Message 包含 instructions 和 inputs。</p>
<h3 id="usage">Usage</h3>
<p>为 Agent 添加 short-term memory，需要在 <code>create_agent</code> 时指定 <code>checkpointer</code>。Short-term memory 是 Agent State 的一部分；checkpointer 按 thread 持久化 State，使 conversation 可恢复。</p>
<p>State 在 Agent invocation 或 tool call 等 step 完成时更新，在每个 step 开始时读取。相同 <code>thread_id</code> 恢复相同 conversation；不同 <code>thread_id</code> 使用独立 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><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-python" data-lang="python"><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">langgraph.checkpoint.memory</span> <span class="kn">import</span> <span class="n">InMemorySaver</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;openai: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">get_user_info</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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="n">config</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#34;</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"><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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Hi! My name is Bob.&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</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">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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s my name?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</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><h4 id="in-production">In production</h4>
<p>生产环境使用 database-backed checkpointer；<code>InMemorySaver</code> 只适合本地示例和测试。</p>
<h3 id="customizing-agent-memory">Customizing agent memory</h3>
<p>Agent 使用 <code>AgentState</code> 管理 short-term memory，其中 <code>messages</code> key 保存 conversation history。需要额外字段时继承 <code>AgentState</code>，通过 <code>state_schema=</code> 传给 <code>create_agent</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></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</span> <span class="kn">import</span> <span class="n">AgentState</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">CustomAgentState</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">user_id</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">preferences</span><span class="p">:</span> <span class="nb">dict</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;openai: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">get_user_info</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">state_schema</span><span class="o">=</span><span class="n">CustomAgentState</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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="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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</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;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="s2">&#34;preferences&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;theme&#34;</span><span class="p">:</span> <span class="s2">&#34;dark&#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 class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#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><p><code>state_schema</code> 定义 State 结构；<code>checkpointer</code> 负责按 thread 保存它。</p>
<h3 id="common-patterns">Common patterns</h3>
<p>以下模式用于处理 conversation history 超过 LLM context window。</p>
<h4 id="trim-message截断消息">Trim message(截断消息)</h4>
<p>接近 token limit 时，根据 token count 截断，并指定 boundary strategy。也可在 <code>@before_model</code> 中清空旧 messages，再写回首条与最近 messages。</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></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">before_model</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">RemoveMessage</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.graph.message</span> <span class="kn">import</span> <span class="n">REMOVE_ALL_MESSAGES</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@before_model</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">trim_messages</span><span class="p">(</span><span class="n">state</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="o">|</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Keep only the first and most recent messages.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">messages</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">3</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 class="n">first_message</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">recent_messages</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">:]</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">messages</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</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></span><span class="line"><span class="cl">            <span class="n">RemoveMessage</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">REMOVE_ALL_MESSAGES</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">first_message</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="o">*</span><span class="n">recent_messages</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></code></pre></td></tr></table>
</div>
</div><p><code>None</code> 表示不更新 State。选择最近 3 或 4 条是为了尽量维持有效的 request/response 边界。</p>
<h4 id="delete-message">Delete message</h4>
<p>使用 <code>RemoveMessage</code> 删除指定 message 或清空全部 history。Messages State key 必须使用 <code>add_messages</code> reducer；默认 <code>AgentState</code> 已提供。</p>
<h6 id="to-remove-specific-messages">To remove specific messages:</h6>
<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></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.messages</span> <span class="kn">import</span> <span class="n">RemoveMessage</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">delete_messages</span><span class="p">(</span><span class="n">state</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">messages</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</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">RemoveMessage</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">message</span><span class="o">.</span><span class="n">id</span><span class="p">)</span> <span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">messages</span><span class="p">[:</span><span class="mi">2</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><h6 id="to-remove-all-messages">To remove all messages:</h6>
<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></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.messages</span> <span class="kn">import</span> <span class="n">RemoveMessage</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.graph.message</span> <span class="kn">import</span> <span class="n">REMOVE_ALL_MESSAGES</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">delete_messages</span><span class="p">(</span><span class="n">state</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;messages&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">RemoveMessage</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">REMOVE_ALL_MESSAGES</span><span class="p">)]}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>删除后必须保持有效消息历史：部分 provider 要求以 <code>user</code> message 开始；多数 provider 要求带 tool call 的 <code>assistant</code> message 后紧跟对应的 <code>tool</code> result，不能只删除配对的一侧。</p>
<h4 id="summarize-messages">Summarize messages</h4>
<p>Trim 和 Delete 会丢失信息。需要保留旧语义时，使用内置 <code>SummarizationMiddleware</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></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">SummarizationMiddleware</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;openai: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="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></span><span class="line"><span class="cl">        <span class="n">SummarizationMiddleware</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;openai:gpt-5.4-mini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">trigger</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;tokens&#34;</span><span class="p">,</span> <span class="mi">4000</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">keep</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="mi">20</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 class="n">checkpointer</span><span class="o">=</span><span class="n">checkpointer</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>示例在 4000 tokens 时触发摘要，保留最近 20 条 messages。</p>
<h3 id="access-memory">Access memory</h3>
<h4 id="tools-1">tools</h4>
<h5 id="read-short-term-memory-in-a-tool">Read short-term memory in a tool</h5>
<p>Tool 使用对 model 隐藏的 <code>runtime: ToolRuntime</code>，通过 <code>runtime.state</code> 读取 short-term memory。</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></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="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_info</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Look up user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_id</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">state</span><span class="p">[</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;User is John Smith&#34;</span> <span class="k">if</span> <span class="n">user_id</span> <span class="o">==</span> <span class="s2">&#34;user_123&#34;</span> <span class="k">else</span> <span class="s2">&#34;Unknown user&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h5 id="write-short-term-memory-form-tools">write short-term memory form tools</h5>
<p>Tool 写 short-term memory 时返回 <code>Command</code>，并在 <code>Command.update[&quot;messages&quot;]</code> 中加入 <code>tool_call_id=runtime.tool_call_id</code> 的 <code>ToolMessage</code>。</p>
<h4 id="prompt">Prompt</h4>
<p>使用 middleware 根据 conversation history 或 custom State fields 创建 dynamic prompt。<code>@dynamic_prompt</code> 可通过 <code>request.runtime.context</code> 读取 invocation Context。</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></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">TypedDict</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">ModelRequest</span><span class="p">,</span> <span class="n">dynamic_prompt</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">CustomContext</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_name</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@dynamic_prompt</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">dynamic_system_prompt</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_name</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="p">[</span><span class="s2">&#34;user_name&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;You are a helpful assistant. Address the user as </span><span class="si">{</span><span class="n">user_name</span><span class="si">}</span><span class="s2">.&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>创建 Agent 时传入 <code>context_schema=CustomContext</code>，调用时通过 <code>context=CustomContext(...)</code> 提供值。</p>
<h4 id="before-model">Before model</h4>
<p><code>@before_model</code> 在每次 model call 前处理 State，适合 trim、删除、注入或重排 messages。返回 <code>None</code> 表示无修改，返回 <code>dict</code> 表示要合并进 State 的更新。</p>
<p>After model</p>
<p><code>@after_model</code> 在 model call 后检查 State。可读取最后一条 response，用 <code>RemoveMessage(id=last_message.id)</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></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">after_model</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">RemoveMessage</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@after_model</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">validate_response</span><span class="p">(</span><span class="n">state</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="o">|</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Remove messages containing sensitive words.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">stop_words</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;password&#34;</span><span class="p">,</span> <span class="s2">&#34;secret&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_message</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</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 class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">word</span> <span class="ow">in</span> <span class="n">last_message</span><span class="o">.</span><span class="n">content</span> <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">stop_words</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;messages&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">RemoveMessage</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">last_message</span><span class="o">.</span><span class="n">id</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></code></pre></td></tr></table>
</div>
</div><p>该 hook 修改最终 State；对于实时 Stream，只在 <code>after_model</code> 删除可能太晚，流式输出需在离开 run 前过滤。</p>
<h2 id="162701--event_streamingmd">16:27:01 — Event_Streaming.md</h2>
<p>LangChain Agents 构建在 LangGraph 上，因此支持相同的 streaming stack，并提供 messages、tool calls、State 和 custom updates 等 Agent-focused projections。</p>
<p>多数场景使用 <code>stream_events(..., version=&quot;v3&quot;)</code>。它返回带 typed projections 的 run object，各 projection 可独立消费，无需解析 stream-mode tuples。</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></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="n">stream</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream_events</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What is the weather in SF?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v3&#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></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">messages</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">delta</span> <span class="ow">in</span> <span class="n">message</span><span class="o">.</span><span class="n">text</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">delta</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">final_state</span> <span class="o">=</span> <span class="n">stream</span><span class="o">.</span><span class="n">output</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="what-you-can-stream">What you can stream</h3>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Projection</th>
					<th style="text-align: left">内容</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>for event in stream</code></td>
					<td style="text-align: left">带完整 envelope、可访问全部 channel 的 raw protocol events</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.messages</code></td>
					<td style="text-align: left">每次 LLM call 的 model message streams</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.text</code></td>
					<td style="text-align: left">Text deltas 与 final text</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.reasoning</code></td>
					<td style="text-align: left">Model 支持时的 reasoning deltas</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.tool_calls</code></td>
					<td style="text-align: left">Tool-call argument chunks 与 finalized tool calls</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.output</code></td>
					<td style="text-align: left">本次 model call 的 final message object</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.values</code></td>
					<td style="text-align: left">Agent State snapshots</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.output</code></td>
					<td style="text-align: left">Final Agent State</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.subgraphs</code></td>
					<td style="text-align: left">Nested graph runs，包括 sub-agents 和普通 subgraphs</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.extensions</code></td>
					<td style="text-align: left">Custom transformer projections</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.tool_calls</code></td>
					<td style="text-align: left">Tool lifecycle、inputs、output deltas、final output、errors</td>
			</tr>
	</tbody>
</table>
<p><code>stream.messages</code> 产生 <code>ChatModelStream</code>。每个 message stream 暴露 <code>.text</code>、<code>.reasoning</code>、<code>.tool_calls</code>、<code>.output</code>。同步 projection 可实时迭代，也可排空后读取最终值：<code>str(message.text)</code> 得到最终文本，<code>message.tool_calls.get()</code> 得到完整 Tool calls。</p>
<h3 id="agent-messages">Agent messages</h3>
<p>需要每次 LLM call 的 model output 时使用 <code>stream.messages</code>。<code>message.output</code> 是 finalized AI message，包含 provider-specific content blocks；token usage 位于 <code>message.output.usage_metadata</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></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="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">messages</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;[</span><span class="si">{</span><span class="n">message</span><span class="o">.</span><span class="n">node</span><span class="si">}</span><span class="s2">] &#34;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">delta</span> <span class="ow">in</span> <span class="n">message</span><span class="o">.</span><span class="n">text</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">delta</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">message</span><span class="o">.</span><span class="n">output</span><span class="o">.</span><span class="n">usage_metadata</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">message</span><span class="o">.</span><span class="n">output</span><span class="o">.</span><span class="n">usage_metadata</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="reasoning-content">Reasoning content</h3>
<p><code>message.reasoning</code> 与 <code>.text</code> 用法相同，但只有 model 发出 reasoning blocks 时才有内容。</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></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="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">messages</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">delta</span> <span class="ow">in</span> <span class="n">message</span><span class="o">.</span><span class="n">reasoning</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;[thinking] </span><span class="si">{</span><span class="n">delta</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">delta</span> <span class="ow">in</span> <span class="n">message</span><span class="o">.</span><span class="n">text</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">delta</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="tool-calls">Tool calls</h3>
<p>Tool calls 有两种 projection：</p>
<ul>
<li><code>message.tool_calls</code>：model 生成 Tool call 时，流式输出参数 chunks 和 finalized call；</li>
<li><code>stream.tool_calls</code>：Tool 开始执行后，流式输出 lifecycle、input、output deltas、final output、error。</li>
</ul>
<p>前者观察 model 正在构造什么调用，后者观察 Tool 实际如何执行。</p>
<h3 id="streaming-sub-agent">Streaming sub-agent</h3>
<p>当一个 <code>create_agent</code> 调用另一个已命名的 <code>create_agent</code> 时，内部 Agent 事件位于嵌套 namespace。传给 <code>create_agent</code> 的 <code>name=</code> 用于标识内部 Agent。</p>
<p>命名 sub-agent 出现在 <code>stream.subagents</code> projection；每个 handle 暴露 <code>.messages</code>、<code>.values</code>、<code>.tool_calls</code>、<code>.output</code>，以及 <code>.name</code>、<code>.cause</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></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="k">for</span> <span class="n">subagent</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">subagents</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;</span><span class="si">{</span><span class="n">subagent</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">: &#34;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">subagent</span><span class="o">.</span><span class="n">messages</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">token</span> <span class="ow">in</span> <span class="n">message</span><span class="o">.</span><span class="n">text</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">token</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="state-and-final-output">state and final output</h3>
<p><code>stream.values</code> 获取运行过程中的 State snapshots；<code>stream.output</code> 获取最终 Agent 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></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="k">for</span> <span class="n">snapshot</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">values</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">snapshot</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">final_state</span> <span class="o">=</span> <span class="n">stream</span><span class="o">.</span><span class="n">output</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>message.output</code> 是单次 model call 的最终 AI message；<code>stream.output</code> 是整个 Agent run 的最终 State。</p>
<h3 id="multiple-projections">Multiple projections</h3>
<p>Async code 使用 <code>astream_events</code> 与 <code>asyncio.gather</code> 并发消费多个 projections。</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-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">asyncio</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">stream</span> <span class="o">=</span> <span class="k">await</span> <span class="n">agent</span><span class="o">.</span><span class="n">astream_events</span><span class="p">(</span><span class="nb">input</span><span class="p">,</span> <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v3&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">consume_messages</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">messages</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="k">await</span> <span class="n">message</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">async</span> <span class="k">def</span> <span class="nf">consume_tool_calls</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">async</span> <span class="k">for</span> <span class="n">call</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">tool_calls</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">call</span><span class="o">.</span><span class="n">tool_name</span><span class="p">,</span> <span class="n">call</span><span class="o">.</span><span class="n">input</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="n">consume_messages</span><span class="p">(),</span> <span class="n">consume_tool_calls</span><span class="p">())</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Synchronous code 使用 <code>stream.interleave(...)</code>，返回 <code>(name, item)</code> 区分 projection。</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></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="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">interleave</span><span class="p">(</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="s2">&#34;tool_calls&#34;</span><span class="p">,</span> <span class="s2">&#34;values&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;messages&#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="n">item</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;tool_calls&#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="n">item</span><span class="o">.</span><span class="n">tool_name</span><span class="p">,</span> <span class="n">item</span><span class="o">.</span><span class="n">input</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;values&#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="n">item</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="custom-updates">Custom updates</h3>
<p>内置 projection 不够时，使用 custom stream transformer，并从 <code>stream.extensions</code> 读取扩展 projection。</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></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="n">stream</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream_events</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nb">input</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v3&#34;</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 class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">activity</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">extensions</span><span class="p">[</span><span class="s2">&#34;tool_activity&#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="n">activity</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="register-transformers-on-middleware">Register transformers on middleware</h4>
<p>Middleware 可在 hooks 和 Tools 旁声明 transformer factories。将 <code>AgentMiddleware.transformers</code> 设置为 factories 序列；每个 factory 形如 <code>Callable[[tuple[str, ...]], StreamTransformer]</code>，框架调用 <code>factory(scope)</code>。</p>
<p>Root mux 的 <code>scope</code> 为 <code>()</code>，subgraph 为非空 tuple。每次返回新的 transformer，使各 subgraph 隔离。</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></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">AgentMiddleware</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">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></code></pre></td></tr></table>
</div>
</div><p><code>create_agent</code> 编译时合并 middleware factories 与自身 <code>transformers=</code>，顺序为：</p>
<ol>
<li>内置 <code>ToolCallTransformer</code>；</li>
<li>Middleware factories，按 middleware 顺序；</li>
<li>调用方传给 <code>create_agent(transformers=...)</code> 的 factories。</li>
</ol>
<p>这让内置 Tool call projection 位于消费者 transformer 之前，并让调用方条目拥有最终处理权。</p>
<p><code>PIIMiddleware(..., apply_to_output=True)</code> 会在 text deltas、tool call arguments、tool outputs、State snapshots 离开 run 前清除 PII。</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></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="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;openai: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></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">PIIMiddleware</span><span class="p">(</span><span class="s2">&#34;email&#34;</span><span class="p">,</span> <span class="n">strategy</span><span class="o">=</span><span class="s2">&#34;redact&#34;</span><span class="p">,</span> <span class="n">apply_to_output</span><span class="o">=</span><span class="kc">True</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></code></pre></td></tr></table>
</div>
</div><p>若只依赖 <code>after_model</code> 做 State-level filtering，原始 PII 可能已通过 <code>stream_events(version=&quot;v3&quot;)</code> 实时发送给 reader。</p>
<h2 id="165227--streamingmd">16:52:27 — Streaming.md</h2>
<h3 id="overview">Overview</h3>
<p>Streaming 支持：</p>
<ul>
<li>Stream Agent progress；</li>
<li>Stream LLM tokens；</li>
<li>Streaming thinking/reasoning tokens；</li>
<li>Stream custom updates；</li>
<li>从 <code>updates</code>、<code>messages</code>、<code>custom</code> 中选择一个或多个 modes。</li>
</ul>
<h3 id="supported-stream-modes">Supported stream modes</h3>
<p>将一个 mode 或多个 modes 的 list 传给 <code>stream</code> / <code>astream</code>。</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Mode</th>
					<th style="text-align: left">Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>updates</code></td>
					<td style="text-align: left">每个 Agent step 后发送 State updates；同一步运行多个 nodes 时分别发送</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>messages</code></td>
					<td style="text-align: left">从所有调用 LLM 的 graph nodes 发送 <code>(token, metadata)</code> tuples</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>custom</code></td>
					<td style="text-align: left">发送 graph nodes 内通过 stream writer 写出的 custom data</td>
			</tr>
	</tbody>
</table>
<h3 id="agent-progress">Agent progress</h3>
<p>使用 <code>stream_mode=&quot;updates&quot;</code> 的 <code>stream</code> 或 <code>astream</code>，在每个 Agent step 后接收事件。一次 Tool call 的典型顺序是：</p>
<ol>
<li><strong>LLM node</strong>：输出带 Tool call requests 的 <code>AIMessage</code>；</li>
<li><strong>Tool node</strong>：输出带 execution result 的 <code>ToolMessage</code>；</li>
<li><strong>LLM node</strong>：输出 final AI response。</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>LangChain MCP Overview</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/mcp/overview/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/mcp/overview/</guid>
      <description>LangChain MCP Overview相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p><a href="https://modelcontextprotocol.io/">Model Context Protocol (MCP)</a> is an open protocol that standardizes how applications provide tools and context to language models.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Event Streaming</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/event-streaming/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/event-streaming/</guid>
      <description>Event Streaming相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>LangChain agents are built on LangGraph, so they support the same streaming stack with agent-focused projections for messages, tool calls, state, and custom updates.</p>
<p>For most situation,use Event Streaming through<code>stream_event(...,version=&quot;v3&quot;)</code>
Event Streaming returns a run object with typed projections, so each projection can be consumed independently instead of parsing stream-mode tuples</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">stream = agent.stream_events({
</span></span><span class="line"><span class="cl">    &#34;messages&#34;: [{&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: &#34;What is the weather in SF?&#34;}],
</span></span><span class="line"><span class="cl">}, version=&#34;v3&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">for message in stream.messages:
</span></span><span class="line"><span class="cl">    for delta in message.text:
</span></span><span class="line"><span class="cl">        print(delta, end=&#34;&#34;, flush=True)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">final_state = stream.output
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="what-you-can-stream">What you can stream</h1>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Projection</th>
					<th style="text-align: left">Use</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>for event in stream</code></td>
					<td style="text-align: left">Raw protocol events with full envelope and access to every channel.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.messages</code></td>
					<td style="text-align: left">Model message streams, one per LLM call.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.text</code></td>
					<td style="text-align: left">Text deltas and final text for a message.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.reasoning</code></td>
					<td style="text-align: left">Reasoning deltas for models that expose reasoning content.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.tool_calls</code></td>
					<td style="text-align: left">Tool-call argument chunks and finalized tool calls.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>message.output</code></td>
					<td style="text-align: left">Final message object after the model call completes.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.values</code></td>
					<td style="text-align: left">Agent state snapshots.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.output</code></td>
					<td style="text-align: left">Final agent state.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.subgraphs</code></td>
					<td style="text-align: left">Nested graph runs (sub-agents and plain subgraphs).</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.extensions</code></td>
					<td style="text-align: left">Custom transformer projections.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>stream.tool_calls</code></td>
					<td style="text-align: left">Tool execution lifecycle, inputs, output deltas, final output, and errors.</td>
			</tr>
	</tbody>
</table>
<p><code>stream.messages</code> 会生成 <code>ChatModelStream</code> 对象。每个消息流都暴露了 <code>.text</code>、<code>.reasoning</code>、<code>.tool_calls</code> 和 <code>.output</code>。同步投影可迭代以获取实时增量，也可排空以获取最终值：使用 <code>str(message.text)</code> 获取最终文本，使用 <code>message.tool_calls.get()</code> 获取已完成的工具调用</p>
<h1 id="agent-messages">Agent messages</h1>
<p>Use <code>stream.message</code> when you want model output from each LLM call</p>
<p><code>message.output</code> gives you the finalized AI message,including previder-specific content blocks
read usage from <code>message.output.usage_metadata</code>.(If you want token counts)</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">stream = agent.stream_events(input, version=&#34;v3&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">for message in stream.messages:
</span></span><span class="line"><span class="cl">    print(f&#34;[{message.node}] &#34;, end=&#34;&#34;)
</span></span><span class="line"><span class="cl">    for delta in message.text:
</span></span><span class="line"><span class="cl">        print(delta, end=&#34;&#34;, flush=True)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    full_message = message.output
</span></span><span class="line"><span class="cl">    usage = full_message.usage_metadata
</span></span><span class="line"><span class="cl">    if usage:
</span></span><span class="line"><span class="cl">        print(usage)
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="reasoning-content">Reasoning content</h1>
<p>the same shape as text ,but only available when the model emits reasoning blocks</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">stream = agent.stream_events(input, version=&#34;v3&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">for message in stream.messages:
</span></span><span class="line"><span class="cl">    for delta in message.reasoning:
</span></span><span class="line"><span class="cl">        print(f&#34;[thinking] {delta}&#34;, end=&#34;&#34;, flush=True)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    for delta in message.text:
</span></span><span class="line"><span class="cl">        print(delta, end=&#34;&#34;, flush=True)
</span></span></code></pre></td></tr></table>
</div>
</div><p>将上面的message.text更换为message.reasoning</p>
<h1 id="tool-calls">Tool calls</h1>
<p>有两种投影</p>
<ul>
<li>message.tool_calls在模型生成工具调用的时候,会流式传输工具调用参数块</li>
<li>stream.tool_calls会在工具调用开始后流式传输工具执行的生命周期</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">stream = agent.stream_events(input, version=&#34;v3&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">for message in stream.messages:
</span></span><span class="line"><span class="cl">    for delta in message.reasoning:
</span></span><span class="line"><span class="cl">        print(f&#34;[thinking] {delta}&#34;, end=&#34;&#34;, flush=True)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    for delta in message.text:
</span></span><span class="line"><span class="cl">        print(delta, end=&#34;&#34;, flush=True)
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="streaming-sub-agent">Streaming sub-agent</h1>
<p>当一个create_agent调用另一个已命名的create_agent时,内部智能体的时间会在一个内嵌的namespace中流动,你传递给create_agent的name= 会在stream中标识该内部智能体</p>
<p>命名的子智能体会在主智能体的stream.subagents投影上呈现,而每个句柄都暴露出该内部智能体的.message,.value,.tool_calls和.output加上你传递的name和.cause</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></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">upervisor</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="n">init_chat_model</span><span class="p">(</span><span class="s2">&#34;openai: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">call_weather</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="o">=</span><span class="s2">&#34;supervisor&#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></span><span class="line"><span class="cl"><span class="n">stream</span> <span class="o">=</span> <span class="n">supervisor</span><span class="o">.</span><span class="n">stream_events</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s the weather in Boston?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v3&#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></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">subagent</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">subagents</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;{subagent.name}: &#34;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">subagent</span><span class="o">.</span><span class="n">messages</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">token</span> <span class="ow">in</span> <span class="n">message</span><span class="o">.</span><span class="n">text</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">token</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">flush</span><span class="o">=</span><span class="n">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="state-and-final-output">state and final output</h1>
<p>用<code>stream.value</code>来获取state的快照
用<code>stream.output</code>来获取最终的agent 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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">stream = agent.stream_events(input, version=&#34;v3&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">for snapshot in stream.values:
</span></span><span class="line"><span class="cl">    print(snapshot)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">final_state = stream.output
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="multiple-projections">Multiple projections</h1>
<p>对于 async code中的并发消费,请使用 <code>astream_events</code> with <code>asyncio.gather</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-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">import</span> <span class="n">asyncio</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">stream</span> <span class="o">=</span> <span class="n">await</span> <span class="n">agent</span><span class="o">.</span><span class="n">astream_events</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v3&#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">async</span> <span class="k">def</span> <span class="nf">consume_messages</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">async</span> <span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">messages</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">await</span> <span class="n">message</span><span class="o">.</span><span class="n">text</span><span class="p">)</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">consume_tool_calls</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">async</span> <span class="k">for</span> <span class="n">call</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">tool_calls</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">call</span><span class="o">.</span><span class="n">tool_name</span><span class="p">,</span> <span class="n">call</span><span class="o">.</span><span class="n">input</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="n">consume_messages</span><span class="p">(),</span> <span class="n">consume_tool_calls</span><span class="p">())</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>对于synchronous code,使用stream.interleave(&hellip;)</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></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">stream</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream_events</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v3&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">interleave</span><span class="p">(</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="s2">&#34;tool_calls&#34;</span><span class="p">,</span> <span class="s2">&#34;values&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;messages&#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="n">item</span><span class="o">.</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;tool_calls&#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="n">item</span><span class="o">.</span><span class="n">tool_name</span><span class="p">,</span> <span class="n">item</span><span class="o">.</span><span class="n">input</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;values&#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="n">item</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="custom-updates">Custom updates</h1>
<p>使用custom stream transformer当你需要内置未提供的投影</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></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">stream</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream_events</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">input</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v3&#34;</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 class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">activity</span> <span class="ow">in</span> <span class="n">stream</span><span class="o">.</span><span class="n">extensions</span><span class="p">[</span><span class="s2">&#34;tool_activity&#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="n">activity</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="register-transformers-on-middleware">Register transformers on middleware</h2>
<p>中间件可以在其钩子和工具旁边声明流转换器工厂。工厂的形态在不同语言中有所不同：将 <code>AgentMiddleware</code> 子类上的 <code>transformers</code> 属性设置为一系列工厂。每个工厂的形态为 <code>Callable[[tuple[str, ...]], StreamTransformer]</code>，调用方式为 <code>factory(scope)</code>，其中 <code>scope</code> 是 mini-mux 作用域元组（根 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-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></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">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="n">get_weather</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><p>在编译时，<code>create_agent</code> 会将中间件注册的工厂与传递给其自身的 <code>transformers=</code> 参数的内容进行合并。编译后图上的最终顺序为：</p>
<ol>
<li>内置的 <code>ToolCallTransformer</code>。</li>
<li>中间件注册的工厂，按中间件顺序排列。</li>
<li>由调用方通过 <code>create_agent</code> 提供的 <code>transformers=</code>。</li>
</ol>
<p>这使得内置的工具调用投影排在消费者转换器之前，并让调用方提供的条目拥有最终决定权。内置的 <code>PIIMiddleware</code> 使用此钩子从流式传输的线路输出中过滤 PII。通过设置 <code>apply_to_output=True</code>，其注册的转换器会在文本增量、工具调用参数、工具输出和状态快照离开运行之前，清除其中检测到的 PII，从而关闭了一个窗口——在该窗口中，<code>after_model</code> 状态级别的过滤原本会让原始 PII 透传给 <code>stream_events(version=&quot;v3&quot;)</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></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">PIIMiddleware</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-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></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">PIIMiddleware</span><span class="p">(</span><span class="s2">&#34;email&#34;</span><span class="p">,</span> <span class="n">strategy</span><span class="o">=</span><span class="s2">&#34;redact&#34;</span><span class="p">,</span> <span class="n">apply_to_output</span><span class="o">=</span><span class="n">True</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></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Context Engineering</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/context-engineering/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/context-engineering/</guid>
      <description>Context Engineering相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="overview">Overview</h1>
<p>构建agent的难点在于使他们足够可靠
即时他们在原型阶段往往会表现得很好</p>
<h2 id="why-do-agent-fails">Why do agent fails</h2>
<p>LLM调用采取了错误的措施或者没有按照预期执行,原因:</p>
<ol>
<li>LLM能力不够</li>
<li>正确的context没有传递给LLM</li>
</ol>
<p>更多情况是后者</p>
<h2 id="the-agent-loop">The agent loop</h2>
<p>two main step2:</p>
<ol>
<li>Model call: calls the LLM with a prompt and available tools, returns either a  response or a request to execute tools</li>
<li>Tool execution: execute the tools that LLM requested,returns tool results</li>
</ol>
<p><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>What can you control:</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Context Type</th>
					<th style="text-align: left">What You Control</th>
					<th style="text-align: left">Transient or Persistent</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong><a href="https://docs.langchain.com/oss/python/langchain/context-engineering#model-context">Model Context</a></strong></td>
					<td style="text-align: left">What goes into model calls (instructions, message history, tools, response format)</td>
					<td style="text-align: left">Transient</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong><a href="https://docs.langchain.com/oss/python/langchain/context-engineering#tool-context">Tool Context</a></strong></td>
					<td style="text-align: left">What tools can access and produce (reads/writes to state, store, runtime context)</td>
					<td style="text-align: left">Persistent</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong><a href="https://docs.langchain.com/oss/python/langchain/context-engineering#life-cycle-context">Life-cycle Context</a></strong></td>
					<td style="text-align: left">What happens between model and tool calls (summarization, guardrails, logging, etc.)</td>
					<td style="text-align: left">Persistent</td>
			</tr>
	</tbody>
</table>
<h2 id="data-sources">Data sources</h2>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Data Source</th>
					<th style="text-align: left">Also Known As</th>
					<th style="text-align: left">Scope</th>
					<th style="text-align: left">Examples</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>Runtime Context</strong></td>
					<td style="text-align: left">Static configuration</td>
					<td style="text-align: left">Conversation-scoped</td>
					<td style="text-align: left">User ID, API keys, database connections, permissions, environment settings</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>State</strong></td>
					<td style="text-align: left">Short-term memory</td>
					<td style="text-align: left">Conversation-scoped</td>
					<td style="text-align: left">Current messages, uploaded files, authentication status, tool results</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Store</strong></td>
					<td style="text-align: left">Long-term memory</td>
					<td style="text-align: left">Cross-conversation</td>
					<td style="text-align: left">User preferences, extracted insights, memories, historical data</td>
			</tr>
	</tbody>
</table>
<h2 id="how-it-works">How it works</h2>
<p>Using middleware:</p>
<ul>
<li>update context</li>
<li>Jump to a different step in the agent lifecycle</li>
</ul>
<h1 id="model-context">Model context</h1>
<p>Instructions,available tools,which model to use,output format.</p>
<ul>
<li>System prompt</li>
<li>Messages:conversation history</li>
<li>Tools</li>
<li>Model</li>
<li>Response format</li>
</ul>
<h2 id="system-prompt">System prompt</h2>
<p>Successful agents draw on memories, preferences, and configuration to provide the right instructions for the current state of the conversation.</p>
<ul>
<li>Access message count or conversation context from state</li>
<li>Access user preferences from long-term memory</li>
<li>Access user ID or configuration from Runtime Context</li>
</ul>
<h2 id="message">Message</h2>
<p>Make up the prompt that is sent to the LLM</p>
<p>分为state,store,Runtime context</p>
<h2 id="tools">Tools</h2>
<h3 id="defining-tools">Defining tools</h3>
<p>name,description(docstring),argument name, argument description</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></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.tools</span> <span class="kn">import</span> <span class="n">tool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span><span class="p">(</span><span class="n">parse_docstring</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">search_orders</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="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">status</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Search for user orders by status.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Use this when the user asks about order history or wants to check
</span></span></span><span class="line"><span class="cl"><span class="s2">    order status. Always filter by the provided status.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">        user_id: Unique identifier for the user
</span></span></span><span class="line"><span class="cl"><span class="s2">        status: Order status: &#39;pending&#39;, &#39;shipped&#39;, or &#39;delivered&#39;
</span></span></span><span class="line"><span class="cl"><span class="s2">        limit: Maximum number of results to return
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Implementation here</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="selecting-tools">Selecting tools</h3>
<p>从分为从三个数据源来组织上下文</p>
<h2 id="model">Model</h2>
<p>根据三种数据源中的各种数据来选择合适的模型</p>
<h2 id="response-format">Response format</h2>
<h3 id="defining-formats">defining formats</h3>
<p>Schema definitions guide the model. Field names, types, and descriptions specify exactly what format the output should adhere to.</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></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 pydantic import BaseModel, Field
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class CustomerSupportTicket(BaseModel):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Structured ticket information extracted from customer message.&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    category: str = Field(
</span></span><span class="line"><span class="cl">        description=&#34;Issue category: &#39;billing&#39;, &#39;technical&#39;, &#39;account&#39;, or &#39;product&#39;&#34;
</span></span><span class="line"><span class="cl">    )
</span></span><span class="line"><span class="cl">    priority: str = Field(
</span></span><span class="line"><span class="cl">        description=&#34;Urgency level: &#39;low&#39;, &#39;medium&#39;, &#39;high&#39;, or &#39;critical&#39;&#34;
</span></span><span class="line"><span class="cl">    )
</span></span><span class="line"><span class="cl">    summary: str = Field(
</span></span><span class="line"><span class="cl">        description=&#34;One-sentence summary of the customer&#39;s issue&#34;
</span></span><span class="line"><span class="cl">    )
</span></span><span class="line"><span class="cl">    customer_sentiment: str = Field(
</span></span><span class="line"><span class="cl">        description=&#34;Customer&#39;s emotional tone: &#39;frustrated&#39;, &#39;neutral&#39;, or &#39;satisfied&#39;&#34;
</span></span><span class="line"><span class="cl">    )
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="selecting-format">selecting format</h3>
<h1 id="tool-context">Tool context</h1>
<p>Both read and write context</p>
<h2 id="reads">Reads</h2>
<p>大多数的工具不只是需要LLM传入的参数,需要从其他的数据源获得,主要是state,store,Runtime context</p>
<h2 id="writes">Writes</h2>
<p>update the memory</p>
<ul>
<li>Write to State to track session-specific information using Command:</li>
<li>Write to Store to persist data across sessions:</li>
</ul>
<h1 id="life-cycle-context">Life-cycle context</h1>
<p>控制核心agent步骤之间发生的情况:拦截数据流以实现总结,安全护栏,日志记录等横切关注点</p>
<p>使用middleware:</p>
<ul>
<li>update context:修改state和存储以持久化更改,更新对话历史或者保存insights</li>
<li>Jump in the lifecycle:根据上下文移动到Agent loop中的不同步骤(例如如果满足条件就跳过工具执行,或使用修改后的上下文重复model call)</li>
</ul>
<h2 id="example-summarization">Example: Summarization</h2>
<p>automatically condensing conversation history</p>
<p>LangChain offers built-in middleware for this:</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></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">SummarizationMiddleware</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="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></span><span class="line"><span class="cl">        <span class="n">SummarizationMiddleware</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.4-mini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">trigger</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;tokens&#34;</span><span class="p">:</span> <span class="mi">4000</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="n">keep</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="mi">20</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 class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>When the conversation exceeds the token limit, <code>SummarizationMiddleware</code> automatically:</p>
<ol>
<li>Summarizes older messages using a separate LLM call</li>
<li>Replaces them with a summary message in State (permanently)</li>
<li>Keeps recent messages intact for context</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>LangChain A Quick Start</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/langchain-a-quick-start/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/langchain-a-quick-start/</guid>
      <description>LangChain A Quick Start相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>一个简单的示例</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-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></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a given city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;It&#39;s always sunny in {city}!&#34;</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">tools</span><span class="o">=</span><span class="p">[</span><span class="n">get_weather</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span><span class="o">=</span><span class="s2">&#34;You are a helpful assistant&#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></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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s the weather in San Francisco?&#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="nb">print</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">content_blocks</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="build-a-real-world-agent">Build a real-world agent:</h1>
<ol>
<li>Detailed System Prompt</li>
<li>Create Tools</li>
<li>Model Configuration</li>
<li>Conversational memory</li>
<li>Deep agents</li>
<li>Testing</li>
</ol>
<h3 id="1define-the-system-prompt">1.Define the system prompt</h3>
<p>defines your agent&rsquo;s role and behavior.
Keep it specific and actionable.</p>
<h3 id="2craete-tools">2.Craete tools</h3>
<p>Tools let a model interact with external systems by calling functions you define. Tools can depend on runtime context and also interact with agent memory.</p>
<p>Tools should be well-documented: their name,description,parameters will become part of the model&rsquo;s prompt. LangChain&rsquo;s tool decorator adds metadata and enables runtime injection with the <code>ToolRunTime</code> paramete.</p>
<h3 id="3configure-your-model">3.Configure your model</h3>
<p>Set up your language model with the right parameters foe your use case</p>
<h3 id="4add-memory">4.Add memory</h3>
<p>Add memory to your agent to maintain state across interactions. This allows the agent to remember previous conversations and context.</p>
<h3 id="5create-and-run-the-agent">5.Create and run the agent</h3>
<p>Tow different Frameworks for creating agents:
LangChain agents and deep agents
Use deep agents when you want maximum with minimal setup;
Choose LangChain agents when you need fine-grained contorl.</p>
<h3 id="6review-the-results">6.Review the results</h3>
<p>相较于LangChain, deep agent拥有更多定义好的功能:</p>
<ol>
<li>使用内置的 <a href="https://docs.langchain.org.cn/oss/python/deepagents/harness#task-planning"><code>write_todos</code></a> 工具<strong>规划其方法</strong>，以分解研究任务。</li>
<li>通过调用 <code>fetch_text_from_url</code> 工具<strong>加载文件</strong>以收集信息。</li>
<li>通过使用文件系统工具（<a href="https://docs.langchain.org.cn/oss/python/deepagents/harness#virtual-filesystem-access"><code>grep</code></a> 和 <a href="https://docs.langchain.org.cn/oss/python/deepagents/harness#virtual-filesystem-access"><code>read_file</code></a>）来<strong>管理上下文</strong>。</li>
<li><strong>生成子智能体</strong>：根据需要将复杂的子任务委派给专门的子智能体。</li>
</ol>
<p>For LangChain agents, you must implement more capabilities to get a similar lever of service and can customize them along the way as needed</p>
<p>In short,deep agent is well-organized and provides many integrated tools
and LangChain can fit custom needs better, but requires implementing more functionalities.</p>
<h2 id="trace-agent-calls">Trace agent calls</h2>
<p>Use LangSmith to trace LLM calls and inspect what is going on inside your agent.</p>
<p>Emmm, you should sign up for  a LangSmith account first.
Besides,you can also set up LangSmith Engine which monitors your traces,detects issues ,and preposes fixes.</p>
]]></content:encoded>
    </item>
    <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>Guardrails</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/guardrails/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/guardrails/</guid>
      <description>Guardrails相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>GuardRails通过validating和flitering content来帮助你构建安全合规的agent</p>
<p>常见的应用场景:</p>
<ul>
<li>预防个人信息的泄露</li>
<li>检测和阻止提示词注入攻击</li>
<li>阻止不恰当和有害的攻击</li>
<li>执行业务规则和合规性要求</li>
<li>验证输出重量和正确性</li>
</ul>
<p>你可以在使用middleware在关键节点拦截执行
before the agent starts, after it completes, or around model and tool calls.</p>
<p>两种互补的办法实现 Guardrails:
1.Deterministic guardrails:
使用规则的逻辑比如正则表达式,关键词匹配或者显式检查
优点:快且省 缺点:可能漏掉细微的违规情况</p>
<p>2.Model-based guradrails
使用LLM或者classifiers进行语义分析
优点:效果好 缺点:延迟高,成本更高</p>
<h1 id="built-in-guardrail">Built-in guardrail</h1>
<h2 id="pii-detection">PII detection</h2>
<p>可以检测emails,credit cards,IP addresses和其他的PII
提供了几种解决PII检测的策略:</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Strategy</th>
					<th style="text-align: left">Description</th>
					<th style="text-align: left">Example</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>redact</code></td>
					<td style="text-align: left">Replace with <code>[REDACTED_{PII_TYPE}]</code></td>
					<td style="text-align: left"><code>[REDACTED_EMAIL]</code></td>
			</tr>
			<tr>
					<td style="text-align: left"><code>mask</code></td>
					<td style="text-align: left">Partially obscure (e.g., last 4 digits)</td>
					<td style="text-align: left"><code>****-****-****-1234</code></td>
			</tr>
			<tr>
					<td style="text-align: left"><code>hash</code></td>
					<td style="text-align: left">Replace with deterministic hash</td>
					<td style="text-align: left"><code>a8f5f167...</code></td>
			</tr>
			<tr>
					<td style="text-align: left"><code>block</code></td>
					<td style="text-align: left">Raise exception when detected</td>
					<td style="text-align: left">Error thrown</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><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></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">PIIMiddleware</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">customer_service_tool</span><span class="p">,</span> <span class="n">email_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"># Redact emails in user input before sending to model</span>
</span></span><span class="line"><span class="cl">        <span class="n">PIIMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;email&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">strategy</span><span class="o">=</span><span class="s2">&#34;redact&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">apply_to_input</span><span class="o">=</span><span class="kc">True</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="c1"># Mask credit cards in user input</span>
</span></span><span class="line"><span class="cl">        <span class="n">PIIMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;credit_card&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">strategy</span><span class="o">=</span><span class="s2">&#34;mask&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">apply_to_input</span><span class="o">=</span><span class="kc">True</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="c1"># Block API keys - raise error if detected</span>
</span></span><span class="line"><span class="cl">        <span class="n">PIIMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;api_key&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">detector</span><span class="o">=</span><span class="sa">r</span><span class="s2">&#34;sk-[a-zA-Z0-9]</span><span class="si">{32}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">strategy</span><span class="o">=</span><span class="s2">&#34;block&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">apply_to_input</span><span class="o">=</span><span class="kc">True</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 class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># When user provides PII, it will be handled according to the strategy</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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;My email is john.doe@example.com and card is 5105-1051-0510-5100&#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><h2 id="human-in-the-loop">Human-in-the-loop</h2>
<p>执行敏感操作之前征求人工的approval</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-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">HumanInTheLoopMiddleware</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.checkpoint.memory</span> <span class="kn">import</span> <span class="n">InMemorySaver</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="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">send_email_tool</span><span class="p">,</span> <span class="n">delete_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="n">HumanInTheLoopMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">interrupt_on</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1"># Require approval for sensitive operations</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;send_email&#34;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;delete_database&#34;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="c1"># Auto-approve safe operations</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;search&#34;</span><span class="p">:</span> <span class="kc">False</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 class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Persist the state across interrupts</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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"># Human-in-the-loop requires a thread ID for persistence</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;some_id&#34;</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Agent will pause and wait for approval before executing sensitive tools</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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Send an email to the team&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">=</span><span class="n">config</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="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="n">Command</span><span class="p">(</span><span class="n">resume</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;decisions&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;approve&#34;</span><span class="p">}]}),</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">=</span><span class="n">config</span>  <span class="c1"># Same thread ID to resume the paused conversation</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="custom-guardrail">Custom guardrail</h1>
<p>对于更加复杂的需求,可以自定义在agent执行之前和之后的middleware</p>
<h2 id="before-agent-guardrail">Before agent guardrail</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><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></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">Any</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 class="p">,</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">hook_config</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 class="k">class</span> <span class="nc">ContentFilterMiddleware</span><span class="p">(</span><span class="n">AgentMiddleware</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Deterministic guardrail: Block requests containing banned keywords.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">banned_keywords</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">banned_keywords</span> <span class="o">=</span> <span class="p">[</span><span class="n">kw</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">kw</span> <span class="ow">in</span> <span class="n">banned_keywords</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@hook_config</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">before_agent</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="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="c1"># Get the first user message</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#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 class="n">first_message</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#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">first_message</span><span class="o">.</span><span class="n">type</span> <span class="o">!=</span> <span class="s2">&#34;human&#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 class="n">content</span> <span class="o">=</span> <span class="n">first_message</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Check for banned keywords</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">banned_keywords</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">keyword</span> <span class="ow">in</span> <span class="n">content</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="c1"># Block execution before any processing</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</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></span><span class="line"><span class="cl">                        <span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;I cannot process requests containing inappropriate content. Please rephrase your request.&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}],</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;jump_to&#34;</span><span class="p">:</span> <span class="s2">&#34;end&#34;</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="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 class="c1"># Use the custom guardrail</span>
</span></span><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></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">calculator_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="n">ContentFilterMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">banned_keywords</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;hack&#34;</span><span class="p">,</span> <span class="s2">&#34;exploit&#34;</span><span class="p">,</span> <span class="s2">&#34;malware&#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 class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># This request will be blocked before any processing</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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;How do I hack into a database?&#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><p>Use &ldquo;before agent&rdquo; hook 来验证
适用于会话级别的检查(例如身份验证,速率限制,拦截不安全的请求)</p>
<h2 id="after-agent-guardrail">After agent guardrail</h2>
<p>使用&quot;after agent&quot; 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><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></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.middleware</span> <span class="kn">import</span> <span class="n">AgentMiddleware</span><span class="p">,</span> <span class="n">AgentState</span><span class="p">,</span> <span class="n">hook_config</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 class="kn">from</span> <span class="nn">langchain.messages</span> <span class="kn">import</span> <span class="n">AIMessage</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.chat_models</span> <span class="kn">import</span> <span class="n">init_chat_model</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></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SafetyGuardrailMiddleware</span><span class="p">(</span><span class="n">AgentMiddleware</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Model-based guardrail: Use an LLM to evaluate response safety.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">safety_model</span> <span class="o">=</span> <span class="n">init_chat_model</span><span class="p">(</span><span class="s2">&#34;gpt-5.4-mini&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@hook_config</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">after_agent</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="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="c1"># Get the final AI response</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#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 class="n">last_message</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</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 class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">last_message</span><span class="p">,</span> <span class="n">AIMessage</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 class="c1"># Use a model to evaluate safety</span>
</span></span><span class="line"><span class="cl">        <span class="n">safety_prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;&#34;&#34;Evaluate if this response is safe and appropriate.
</span></span></span><span class="line"><span class="cl"><span class="s2">        Respond with only &#39;SAFE&#39; or &#39;UNSAFE&#39;.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">        Response: </span><span class="si">{</span><span class="n">last_message</span><span class="o">.</span><span class="n">content</span><span class="si">}</span><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">safety_model</span><span class="o">.</span><span class="n">invoke</span><span class="p">([{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="n">safety_prompt</span><span class="p">}])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="s2">&#34;UNSAFE&#34;</span> <span class="ow">in</span> <span class="n">result</span><span class="o">.</span><span class="n">content</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">last_message</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="s2">&#34;I cannot provide that response. Please rephrase your request.&#34;</span>
</span></span><span class="line"><span class="cl">
</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 class="c1"># Use the safety guardrail</span>
</span></span><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></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">calculator_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">SafetyGuardrailMiddleware</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;How do I make explosives?&#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><h2 id="combine-multiple-guardrails">Combine multiple guardrails</h2>
<p>你可以将多个安全护栏加入middleware数组来堆叠他们,他们将按顺序执行,并且允许你构建层级保护</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-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">PIIMiddleware</span><span class="p">,</span> <span class="n">HumanInTheLoopMiddleware</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="p">[</span><span class="n">search_tool</span><span class="p">,</span> <span class="n">send_email_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"># Layer 1: Deterministic input filter (before agent)</span>
</span></span><span class="line"><span class="cl">        <span class="n">ContentFilterMiddleware</span><span class="p">(</span><span class="n">banned_keywords</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;hack&#34;</span><span class="p">,</span> <span class="s2">&#34;exploit&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Layer 2: PII protection (before and after model)</span>
</span></span><span class="line"><span class="cl">        <span class="n">PIIMiddleware</span><span class="p">(</span><span class="s2">&#34;email&#34;</span><span class="p">,</span> <span class="n">strategy</span><span class="o">=</span><span class="s2">&#34;redact&#34;</span><span class="p">,</span> <span class="n">apply_to_input</span><span class="o">=</span><span class="kc">True</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">PIIMiddleware</span><span class="p">(</span><span class="s2">&#34;email&#34;</span><span class="p">,</span> <span class="n">strategy</span><span class="o">=</span><span class="s2">&#34;redact&#34;</span><span class="p">,</span> <span class="n">apply_to_output</span><span class="o">=</span><span class="kc">True</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Layer 3: Human approval for sensitive tools</span>
</span></span><span class="line"><span class="cl">        <span class="n">HumanInTheLoopMiddleware</span><span class="p">(</span><span class="n">interrupt_on</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;send_email&#34;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Layer 4: Model-based safety check (after agent)</span>
</span></span><span class="line"><span class="cl">        <span class="n">SafetyGuardrailMiddleware</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></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    <item>
      <title>LangChain Philosophy</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/langchain-philosophy/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/langchain-philosophy/</guid>
      <description>LangChain Philosophy相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h3 id="langchain-is-driven-by-a-few-core-beliefs">LangChain is driven by a few core beliefs:</h3>
<ul>
<li>Large Language Models (LLMs) are great, powerful new technology.</li>
<li>LLMs are even better when you combine them with external sources of data.</li>
<li>LLMs will transform what the applications of the future look like. Specifically, the applications of the future will look more and more agentic.</li>
<li>It is still very early on in that transformation.</li>
<li>While it’s easy to build a prototype of those agentic applications, it’s still really hard to build agents that are reliable enough to put into production.</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>DeepFlow</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/%E9%A1%B9%E7%9B%AE%E9%98%85%E8%AF%BB%E6%89%80%E5%BE%97/deepflow/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/%E9%A1%B9%E7%9B%AE%E9%98%85%E8%AF%BB%E6%89%80%E5%BE%97/deepflow/</guid>
      <description>DeepFlow相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="1langgraphjson文件的作用">1.langgraph.json文件的作用</h1>
<p>when:</p>
<ol>
<li>使用Langgraph cli部署本地API服务</li>
<li>部署到langgraph cloud/langsmith deployment</li>
<li>使用langgraph studio及逆行调试</li>
</ol>
<p>不需要:
使用传统的后端API服务;纯脚本/嵌入式开发</p>
<p>作用:
指定graph入口;生命项目依赖;配置环境变量等等</p>
<h1 id="2sandbox的作用">2.Sandbox的作用</h1>
<p>提供隔离的安全执行环境</p>
<ol>
<li>
<p>安全隔离(防止恶意代码和误操作):主要是限制一些危险操作,比如<code>rm</code>等删除操作,防止访问敏感数据,防止向外发送未经授权的请求等等</p>
</li>
<li>
<p>环境独立和资源限制:</p>
<ol>
<li>资源配额:CPU,内存,磁盘限额</li>
<li>隔离环境:每一个session都有干净的上下文</li>
</ol>
</li>
<li>
<p>支持复杂工具运行:</p>
<ol>
<li>提供工具:文件系统操作工具</li>
<li>提供无头(Headless)浏览器环境</li>
</ol>
</li>
</ol>
<h1 id="3技能加载的递归实现">3.技能加载的递归实现:</h1>
<p>递归发现skills下嵌套的SKILL.md文件</p>
<h1 id="4subagent的sse事件">4.Subagent的SSE事件</h1>
<p>SSE(Server sent evnet):一种基于HTTP协议的单向实时通信技术,创建HTTP连接后,主动,持续地向客户端推送数据</p>
<h2 id="subagent的sse事件">subagent的SSE事件</h2>
<p>subagent持续向用户端暴露该agent的执行情况
可视化的多agent协作;支持中途打断和交互</p>
<h1 id="5常见的工具生态系统">5.常见的工具生态系统</h1>
<p>沙箱;内置工具(自己编写的);社区工具;MCP;Skill</p>
]]></content:encoded>
    </item>
    <item>
      <title>Human-in-the-loop</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/human-in-the-loop/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/human-in-the-loop/</guid>
      <description>Human-in-the-loop相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>By checking each tool call against a configurable policy
如果需要暂停,middleware将会issue an interrupt来暂停执行
图状态将会使用LangGraph的persistence layer来保存,因此可以pause and resume later</p>
<h1 id="interrupt-decision-types">Interrupt decision types</h1>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Decision Type</th>
					<th style="text-align: left">Description</th>
					<th style="text-align: left">Example Use Case</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left">✅ <code>approve</code></td>
					<td style="text-align: left">Execute the tool with the original arguments as proposed by the agent.</td>
					<td style="text-align: left">Send an email draft exactly as written</td>
			</tr>
			<tr>
					<td style="text-align: left">✏️ <code>edit</code></td>
					<td style="text-align: left">Modify the tool arguments before execution.</td>
					<td style="text-align: left">Change the recipient before sending an email</td>
			</tr>
			<tr>
					<td style="text-align: left">❌ <code>reject</code></td>
					<td style="text-align: left">Skip executing this tool call entirely and return rejection feedback to the agent.</td>
					<td style="text-align: left">Deny file deletion and explain why</td>
			</tr>
			<tr>
					<td style="text-align: left">💬 <code>respond</code></td>
					<td style="text-align: left">Return the human’s message directly as a synthetic tool result, skipping execution, for “ask user” style tools.</td>
					<td style="text-align: left">Answer an <code>&quot;ask_user&quot;</code> prompt with a direct reply</td>
			</tr>
	</tbody>
</table>
<p>每个工具可用的决策类型取决于你在<code>interrupt_on</code>中配置的策略
使用场景:
Use <code>reject</code> when the human is denying the requested action. Use <code>respond</code> only when the human is acting as the tool, such as answering an <code>ask_user</code> prompt. Do not use <code>respond</code> to deny side-effecting tools, because its message is treated as a successful tool result.</p>
<h1 id="configurable-interrupt">Configurable interrupt</h1>
<p>要使用HITL,在<code>create_agent</code>时把中间件添加到agent的middleware列表
你可以使用tool actions到允许的决策类型的映射来配置他</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></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">HumanInTheLoopMiddleware</span> 
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.checkpoint.memory</span> <span class="kn">import</span> <span class="n">InMemorySaver</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">write_file</span><span class="p">,</span> <span class="n">execute_sql</span><span class="p">,</span> <span class="n">read_data</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">HumanInTheLoopMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">interrupt_on</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;write_file&#34;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>  <span class="c1"># All decisions (approve, edit, reject, respond) allowed</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;execute_sql&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;allowed_decisions&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;approve&#34;</span><span class="p">,</span> <span class="s2">&#34;reject&#34;</span><span class="p">]},</span>  <span class="c1"># No editing allowed</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;read_data&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="c1"># Safe operation, no approval needed</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Prefix for interrupt messages - combined with tool name and args to form the full message</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># e.g., &#34;Tool execution pending approval: execute_sql with query=&#39;DELETE FROM...&#39;&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Individual tools can override this by specifying a &#34;description&#34; in their interrupt config</span>
</span></span><span class="line"><span class="cl">            <span class="n">description_prefix</span><span class="o">=</span><span class="s2">&#34;Tool execution pending approval&#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 class="c1"># Human-in-the-loop requires checkpointing to handle interrupts.</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># In production, use a persistent checkpointer like AsyncPostgresSaver or MongoDBSaver.</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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>ps:你必须配置一个checkpointer来持久化graph state,
在生产环境中,使用持久性的checkpointerSaver,如<a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.postgres.aio.AsyncPostgresSaver"><code>AsyncPostgresSaver</code></a> 或 <a href="https://pypi.ac.cn/project/langgraph-checkpoint-mongodb/"><code>MongoDBSaver</code></a>,对于测试和原型设计,使用<a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.memory.InMemorySaver"><code>InMemorySaver</code></a>。</p>
<h1 id="conditional-interrupts">Conditional interrupts</h1>
<p>默认情况下,<code>interrupt_on</code>中列出的tools都会暂停以供审查,如果要仅暂停某些调用,在tool的<code>InterruptConfig</code>中添加<code>when</code>谓词,该谓词接受一个<code>ToolCallRequest</code>来返回True以中断或者False以自动批准</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></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">HumanInTheLoopMiddleware</span><span class="p">,</span> <span class="n">ToolCallRequest</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.checkpoint.memory</span> <span class="kn">import</span> <span class="n">InMemorySaver</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">writes_outside_workspace</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">bool</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Pause writes to paths outside the workspace directory.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">path</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">tool_call</span><span class="p">[</span><span class="s2">&#34;args&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;path&#34;</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="ow">not</span> <span class="n">path</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;/workspace/&#34;</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="k">def</span> <span class="nf">is_write_query</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">bool</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Pause SQL that isn&#39;t a read-only SELECT.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">query</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">tool_call</span><span class="p">[</span><span class="s2">&#34;args&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;query&#34;</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="ow">not</span> <span class="n">query</span><span class="o">.</span><span class="n">lstrip</span><span class="p">()</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;SELECT&#34;</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">tools</span><span class="o">=</span><span class="p">[</span><span class="n">write_file</span><span class="p">,</span> <span class="n">execute_sql</span><span class="p">,</span> <span class="n">read_data</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">HumanInTheLoopMiddleware</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">interrupt_on</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;write_file&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;allowed_decisions&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;approve&#34;</span><span class="p">,</span> <span class="s2">&#34;edit&#34;</span><span class="p">,</span> <span class="s2">&#34;reject&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;when&#34;</span><span class="p">:</span> <span class="n">writes_outside_workspace</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="s2">&#34;execute_sql&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;allowed_decisions&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;approve&#34;</span><span class="p">,</span> <span class="s2">&#34;reject&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;when&#34;</span><span class="p">:</span> <span class="n">is_write_query</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 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">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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>评估为False的tool不会pause</p>
<h1 id="responding-to-interrupts">Responding to interrupts</h1>
<p>在<code>version=v2</code>中,结果时一个带有interrupts属性的<code>GraphOutput</code>,该属性包含需要审查的动作,你可以将这些动作提交给审查者,并在提供决策后恢复执行</p>
<h2 id="decision-types">Decision types</h2>
<p>同上</p>
<h2 id="multiple-types">Multiple types</h2>
<p>当出现多个actions时,根据中断触发的顺序为每个动作提供一个决策</p>
<h1 id="streaming-with-human-in-the-loop">Streaming with human-in-the-loop</h1>
<p>使用stream_event()在agent运行和中断时流式传输实时更新;
stream.message流式传输LLM令牌,使用stream.value检查agent 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><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-fallback" data-lang="fallback"><span class="line"><span class="cl">from langgraph.types import Command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">config = {&#34;configurable&#34;: {&#34;thread_id&#34;: &#34;some_id&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Stream agent progress and LLM tokens until interrupt
</span></span><span class="line"><span class="cl">stream = agent.stream_events(
</span></span><span class="line"><span class="cl">    {&#34;messages&#34;: [{&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: &#34;Delete old records from the database&#34;}]},
</span></span><span class="line"><span class="cl">    config=config,
</span></span><span class="line"><span class="cl">    version=&#34;v3&#34;,
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">for message in stream.messages:
</span></span><span class="line"><span class="cl">    for token in message.text:
</span></span><span class="line"><span class="cl">        print(token, end=&#34;&#34;, flush=True)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Check whether the run paused for human input
</span></span><span class="line"><span class="cl">if stream.interrupted:
</span></span><span class="line"><span class="cl">    print(f&#34;\n\nInterrupt: {stream.interrupts}&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Resume with streaming after human decision
</span></span><span class="line"><span class="cl">stream = agent.stream_events(
</span></span><span class="line"><span class="cl">    Command(resume={&#34;decisions&#34;: [{&#34;type&#34;: &#34;approve&#34;}]}),
</span></span><span class="line"><span class="cl">    config=config,
</span></span><span class="line"><span class="cl">    version=&#34;v3&#34;,
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">for message in stream.messages:
</span></span><span class="line"><span class="cl">    for token in message.text:
</span></span><span class="line"><span class="cl">        print(token, end=&#34;&#34;, flush=True)
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="execution-lifecycle">Execution lifecycle</h1>
<p>middleware定义了一个在模型产生response之后但是在任何的tool call之前的hook</p>
<ol>
<li>The agent invokes the model to generate a response.</li>
<li>The middleware inspects the response for tool calls.</li>
<li>If any calls require human input, the middleware builds a <a href="https://reference.langchain.com/python/langchain/agents/middleware/human_in_the_loop/HITLRequest">HITLRequest</a> with <code>action_requests</code> and <code>review_configs</code> and calls <a href="https://reference.langchain.com/python/langgraph/types/interrupt">interrupt</a>.</li>
<li>The agent waits for human decisions.</li>
<li>Based on the <code>HITLResponse</code> decisions, the middleware executes approved or edited calls, synthesizes <a href="https://reference.langchain.com/python/langchain-core/messages/tool/ToolMessage">ToolMessage</a>’s for rejected calls, returns human replies directly as <a href="https://reference.langchain.com/python/langchain-core/messages/tool/ToolMessage">ToolMessage</a>’s for <code>respond</code> decisions, and resumes execution.</li>
</ol>
<p>Custom HITL logic
对于更加复杂的工作流,你可以构建自定义的中断原语哥中间件来实现</p>
]]></content:encoded>
    </item>
    <item>
      <title>Message</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/message/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/message/</guid>
      <description>Message相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="message">Message</h1>
<p>Carrying both the content and metadata which represent the state</p>
<p>Messages contain:</p>
<ul>
<li>Role: identifies the message type(system,user)</li>
<li>Content: the actual content</li>
<li>Metadata: optional fields: such as information,message IDs,and token usage</li>
</ul>
<p>This is the standard message type</p>
<h2 id="basic-usage">Basic usage</h2>
<p>The easiest way to use message is to <strong>create message objects</strong> and pass them to a model when invoking</p>
<h2 id="text-prompts">Text prompts</h2>
<p>String
Ideal for generation tasks where you don&rsquo;t need to retain.</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">response = model.invoke(&#34;Write a haiku about spring&#34;)
</span></span></code></pre></td></tr></table>
</div>
</div><p>When to use:</p>
<ul>
<li>a single ,standalone request</li>
<li>no need for conversation history</li>
<li>minimal code complexity</li>
</ul>
<h2 id="message-prompts">Message prompts</h2>
<p>Providing  a list of messages</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></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.messages import SystemMessage, HumanMessage, AIMessage
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">messages = [
</span></span><span class="line"><span class="cl">    SystemMessage(&#34;You are a poetry expert&#34;),
</span></span><span class="line"><span class="cl">    HumanMessage(&#34;Write a haiku about spring&#34;),
</span></span><span class="line"><span class="cl">    AIMessage(&#34;Cherry blossoms bloom...&#34;)
</span></span><span class="line"><span class="cl">]
</span></span><span class="line"><span class="cl">response = model.invoke(messages)
</span></span></code></pre></td></tr></table>
</div>
</div><p>When:</p>
<ul>
<li>Managing multi-turn conversations</li>
<li>Working with multimodal content (images, audio, files)</li>
<li>Including system instructions</li>
</ul>
<h2 id="dictionary-format">Dictionary format</h2>
<p>Specify message in OpenAI chat completions format:</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">messages = [
</span></span><span class="line"><span class="cl">    {&#34;role&#34;: &#34;system&#34;, &#34;content&#34;: &#34;You are a poetry expert&#34;},
</span></span><span class="line"><span class="cl">    {&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: &#34;Write a haiku about spring&#34;},
</span></span><span class="line"><span class="cl">    {&#34;role&#34;: &#34;assistant&#34;, &#34;content&#34;: &#34;Cherry blossoms bloom...&#34;}
</span></span><span class="line"><span class="cl">]
</span></span><span class="line"><span class="cl">response = model.invoke(messages)
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="message-types">Message types</h2>
<ul>
<li>System messages</li>
<li>Human messages</li>
<li>Al messages:</li>
<li>Tool messages:</li>
</ul>
<h3 id="system-message">System message</h3>
<p>An initial set of instructions that primes the model&rsquo;s behavior</p>
<h3 id="human-message">Human message</h3>
<p>user input and interactions</p>
<h3 id="ai-message">AI message</h3>
<p>The LLM sometimes treat the message differently, so sometimes we can make up a AI message manually.</p>
<h4 id="tool-calls">Tool calls</h4>
<p>are included in AI messages</p>
<h4 id="token-usage">Token usage</h4>
<p>in AI message in usage_metadata field</p>
<h4 id="streaming-and-chunks">Streaming and chunks</h4>
<p>When streaming, u can receive <code>AIMessageChunk</code> objects and it can be combined into a full message by summing</p>
<h3 id="tool-message">Tool message</h3>
<p>tool result in <code>ToolMessage</code> object</p>
<h2 id="message-content">Message content</h2>
<p>An loosely-typed attribute,supporting strings and lists if untyped objects</p>
<p>Beside, LangChain provides dedicated content types for text, reasoning, citations, multi-modal data, server-side tool calls, and other message content</p>
<p>This may contain either:</p>
<ol>
<li>A string</li>
<li>A list of content blocks in a provider-native format</li>
<li>A list of <a href="https://docs.langchain.com/oss/python/langchain/messages#standard-content-blocks">LangChain’s standard content blocks</a></li>
</ol>
<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">from langchain.messages import HumanMessage
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># String content
</span></span><span class="line"><span class="cl">human_message = HumanMessage(&#34;Hello, how are you?&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Provider-native format (e.g., OpenAI)
</span></span><span class="line"><span class="cl">human_message = HumanMessage(content=[
</span></span><span class="line"><span class="cl">    {&#34;type&#34;: &#34;text&#34;, 
</span></span><span class="line"><span class="cl">    &#34;text&#34;: &#34;Hello, how are you?&#34;},
</span></span><span class="line"><span class="cl">    {&#34;type&#34;: &#34;image_url&#34;, 
</span></span><span class="line"><span class="cl">    &#34;image_url&#34;: {&#34;url&#34;: &#34;https://example.com/image.jpg&#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"># List of standard content blocks
</span></span><span class="line"><span class="cl">human_message = HumanMessage(content_blocks=[
</span></span><span class="line"><span class="cl">    {&#34;type&#34;: &#34;text&#34;, &#34;text&#34;: &#34;Hello, how are you?&#34;},
</span></span><span class="line"><span class="cl">    {&#34;type&#34;: &#34;image&#34;, &#34;url&#34;: &#34;https://example.com/image.jpg&#34;},
</span></span><span class="line"><span class="cl">])
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="standard-content-blocks">Standard content blocks</h2>
<p>LangChain provides a standard representation for message content that works across providers.</p>
<p>消息对象实现了一个 <code>content_blocks</code> 属性，该属性会延迟将 <code>content</code> 属性解析为标准的、类型安全的表示形式。例如，从 <a href="https://docs.langchain.org.cn/oss/python/integrations/chat/anthropic"><code>ChatAnthropic</code></a> 或 <a href="https://docs.langchain.org.cn/oss/python/integrations/chat/openai"><code>ChatOpenAI</code></a> 生成的消息将包含各自提供商格式的 <code>thinking</code> 或 <code>reasoning</code> 块，但可以延迟解析为一致的 <a href="https://docs.langchain.org.cn/oss/python/langchain/messages#content-block-reference"><code>ReasoningContentBlock</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></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.messages import AIMessage
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">message = AIMessage(
</span></span><span class="line"><span class="cl">    content=[
</span></span><span class="line"><span class="cl">        {
</span></span><span class="line"><span class="cl">            &#34;type&#34;: &#34;reasoning&#34;,
</span></span><span class="line"><span class="cl">            &#34;id&#34;: &#34;rs_abc123&#34;,
</span></span><span class="line"><span class="cl">            &#34;summary&#34;: [
</span></span><span class="line"><span class="cl">                {&#34;type&#34;: &#34;summary_text&#34;, &#34;text&#34;: &#34;summary 1&#34;},
</span></span><span class="line"><span class="cl">                {&#34;type&#34;: &#34;summary_text&#34;, &#34;text&#34;: &#34;summary 2&#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">        {&#34;type&#34;: &#34;text&#34;, &#34;text&#34;: &#34;...&#34;, &#34;id&#34;: &#34;msg_abc123&#34;},
</span></span><span class="line"><span class="cl">    ],
</span></span><span class="line"><span class="cl">    response_metadata={&#34;model_provider&#34;: &#34;openai&#34;}
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">message.content_blocks
</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[{&#39;type&#39;: &#39;reasoning&#39;, &#39;id&#39;: &#39;rs_abc123&#39;, &#39;reasoning&#39;: &#39;summary 1&#39;},
</span></span><span class="line"><span class="cl"> {&#39;type&#39;: &#39;reasoning&#39;, &#39;id&#39;: &#39;rs_abc123&#39;, &#39;reasoning&#39;: &#39;summary 2&#39;},
</span></span><span class="line"><span class="cl"> {&#39;type&#39;: &#39;text&#39;, &#39;text&#39;: &#39;...&#39;, &#39;id&#39;: &#39;msg_abc123&#39;}]
</span></span></code></pre></td></tr></table>
</div>
</div><p>将不同的provider的统一接口,是通过content_blocks属性来实现的</p>
<h2 id="multimodal">Multimodal</h2>
<p><strong>Multimodality</strong> refers to the ability to work with data that comes in different forms, such as text, audio, images, and video</p>
<p><a href="https://docs.langchain.com/oss/python/langchain/models">Chat models</a> can accept multimodal data as input and generate it as output. Below we show short examples of input messages featuring multimodal data.</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></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 URL
</span></span><span class="line"><span class="cl">message = {
</span></span><span class="line"><span class="cl">    &#34;role&#34;: &#34;user&#34;,
</span></span><span class="line"><span class="cl">    &#34;content&#34;: [
</span></span><span class="line"><span class="cl">        {&#34;type&#34;: &#34;text&#34;, &#34;text&#34;: &#34;Describe the content of this image.&#34;},
</span></span><span class="line"><span class="cl">        {&#34;type&#34;: &#34;image&#34;, &#34;url&#34;: &#34;https://example.com/path/to/image.jpg&#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"># From base64 data
</span></span><span class="line"><span class="cl">message = {
</span></span><span class="line"><span class="cl">    &#34;role&#34;: &#34;user&#34;,
</span></span><span class="line"><span class="cl">    &#34;content&#34;: [
</span></span><span class="line"><span class="cl">        {&#34;type&#34;: &#34;text&#34;, &#34;text&#34;: &#34;Describe the content of this image.&#34;},
</span></span><span class="line"><span class="cl">        {
</span></span><span class="line"><span class="cl">            &#34;type&#34;: &#34;image&#34;,
</span></span><span class="line"><span class="cl">            &#34;base64&#34;: &#34;AAAAIGZ0eXBtcDQyAAAAAGlzb21tcDQyAAACAGlzb2...&#34;,
</span></span><span class="line"><span class="cl">            &#34;mime_type&#34;: &#34;image/jpeg&#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"># From provider-managed File ID
</span></span><span class="line"><span class="cl">message = {
</span></span><span class="line"><span class="cl">    &#34;role&#34;: &#34;user&#34;,
</span></span><span class="line"><span class="cl">    &#34;content&#34;: [
</span></span><span class="line"><span class="cl">        {&#34;type&#34;: &#34;text&#34;, &#34;text&#34;: &#34;Describe the content of this image.&#34;},
</span></span><span class="line"><span class="cl">        {&#34;type&#34;: &#34;image&#34;, &#34;file_id&#34;: &#34;file-abc123&#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><p>Content blocks are represented (either when creating a message or accessing the <code>content_blocks</code> property) as <strong>a list of typed dictionaries</strong></p>
<h2 id="serialization">Serialization</h2>
<p>You can serialize messages to plain objects for storage and deserialize back to message instances. This is useful for persisting conversation history and resuming sessions.</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></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">messages</span> <span class="n">import</span> <span class="n">HumanMessage</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain_core</span><span class="o">.</span><span class="n">load</span> <span class="n">import</span> <span class="n">dumpd</span><span class="p">,</span> <span class="nb">load</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">message</span> <span class="o">=</span> <span class="n">HumanMessage</span><span class="p">(</span><span class="s2">&#34;What is the capital of France?&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Serialize to a plain dict</span>
</span></span><span class="line"><span class="cl"><span class="n">serialized</span> <span class="o">=</span> <span class="n">dumpd</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Deserialize back to a message object</span>
</span></span><span class="line"><span class="cl"><span class="n">restored</span> <span class="o">=</span> <span class="nb">load</span><span class="p">(</span><span class="n">serialized</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></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>Long-term-memory</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/long-term-memory/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/long-term-memory/</guid>
      <description>Long-term-memory相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>Across sessions and conversations</p>
<p>Long-term memory 建立在Lang Graph stores,存储的形式:JSON documents organized by namespace and key</p>
<h1 id="usage">Usage</h1>
<p>为了给一个agent添加memory功能,在使用<code>create_agent</code>时create a store and pass it to agent</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></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_core.runnables</span> <span class="kn">import</span> <span class="n">Runnable</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.store.memory</span> <span class="kn">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="c1"># InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use.</span>
</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="p">:</span> <span class="n">Runnable</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="s2">&#34;claude-sonnet-4-6&#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></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="p">)</span>
</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><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_core.runnables</span> <span class="kn">import</span> <span class="n">Runnable</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.store.postgres</span> <span class="kn">import</span> <span class="n">PostgresStore</span>  <span class="c1"># type: ignore[import-not-found]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DB_URI</span> <span class="o">=</span> <span class="s2">&#34;postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="n">PostgresStore</span><span class="o">.</span><span class="n">from_conn_string</span><span class="p">(</span><span class="n">DB_URI</span><span class="p">)</span> <span class="k">as</span> <span class="n">store</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">setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">agent</span><span class="p">:</span> <span class="n">Runnable</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="s2">&#34;claude-sonnet-4-6&#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></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="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>tool可以通过<code>runtime.store</code>来访问并且写入store</p>
<h1 id="memory-storage">Memory storage</h1>
<p>每个记忆都被组织在自定义的<code>namespace</code>(类似于文件夹)和一个独特的<code>key</code>(类似于文件名)之下
随后可以通过内容过滤器实现跨命名空间的搜索
InmemoryStore</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></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">Sequence</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">langgraph.store.base</span> <span class="kn">import</span> <span class="n">IndexConfig</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.store.memory</span> <span class="kn">import</span> <span class="n">InMemoryStore</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">embed</span><span class="p">(</span><span class="n">texts</span><span class="p">:</span> <span class="n">Sequence</span><span class="p">[</span><span class="nb">str</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="nb">float</span><span class="p">]]:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Replace with an actual embedding function or LangChain embeddings object</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[[</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">texts</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="c1"># InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use.</span>
</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 class="n">index</span><span class="o">=</span><span class="n">IndexConfig</span><span class="p">(</span><span class="n">embed</span><span class="o">=</span><span class="n">embed</span><span class="p">,</span> <span class="n">dims</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">user_id</span> <span class="o">=</span> <span class="s2">&#34;my-user&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">application_context</span> <span class="o">=</span> <span class="s2">&#34;chitchat&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">namespace</span> <span class="o">=</span> <span class="p">(</span><span class="n">user_id</span><span class="p">,</span> <span class="n">application_context</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">put</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">namespace</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;a-memory&#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="s2">&#34;rules&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;User likes short, direct language&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;User only speaks English &amp; python&#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="s2">&#34;my-key&#34;</span><span class="p">:</span> <span class="s2">&#34;my-value&#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 class="c1"># get the &#34;memory&#34; by ID</span>
</span></span><span class="line"><span class="cl"><span class="n">item</span> <span class="o">=</span> <span class="n">store</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">namespace</span><span class="p">,</span> <span class="s2">&#34;a-memory&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># search for &#34;memories&#34; within this namespace, filtering on content equivalence, sorted by vector similarity</span>
</span></span><span class="line"><span class="cl"><span class="n">items</span> <span class="o">=</span> <span class="n">store</span><span class="o">.</span><span class="n">search</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">namespace</span><span class="p">,</span> <span class="nb">filter</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;my-key&#34;</span><span class="p">:</span> <span class="s2">&#34;my-value&#34;</span><span class="p">},</span> <span class="n">query</span><span class="o">=</span><span class="s2">&#34;language preferences&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>PostgreSQL</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></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 Sequence
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">from langgraph.store.base import IndexConfig
</span></span><span class="line"><span class="cl">from langgraph.store.postgres import PostgresStore  # type: ignore[import-not-found]
</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 embed(texts: Sequence[str]) -&gt; list[list[float]]:
</span></span><span class="line"><span class="cl">    # Replace with an actual embedding function or LangChain embeddings object
</span></span><span class="line"><span class="cl">    return [[1.0, 2.0] for _ in texts]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">DB_URI = &#34;postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">with PostgresStore.from_conn_string(
</span></span><span class="line"><span class="cl">    DB_URI,
</span></span><span class="line"><span class="cl">    index=IndexConfig(embed=embed, dims=2),  # type: ignore[arg-type]
</span></span><span class="line"><span class="cl">) as store:
</span></span><span class="line"><span class="cl">    store.setup()
</span></span><span class="line"><span class="cl">    user_id = &#34;my-user&#34;
</span></span><span class="line"><span class="cl">    application_context = &#34;chitchat&#34;
</span></span><span class="line"><span class="cl">    namespace = (user_id, application_context)
</span></span><span class="line"><span class="cl">    store.put(
</span></span><span class="line"><span class="cl">        namespace,
</span></span><span class="line"><span class="cl">        &#34;a-memory&#34;,
</span></span><span class="line"><span class="cl">        {
</span></span><span class="line"><span class="cl">            &#34;rules&#34;: [
</span></span><span class="line"><span class="cl">                &#34;User likes short, direct language&#34;,
</span></span><span class="line"><span class="cl">                &#34;User only speaks English &amp; python&#34;,
</span></span><span class="line"><span class="cl">            ],
</span></span><span class="line"><span class="cl">            &#34;my-key&#34;: &#34;my-value&#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">    item = store.get(namespace, &#34;a-memory&#34;)
</span></span><span class="line"><span class="cl">    items = store.search(
</span></span><span class="line"><span class="cl">        namespace, filter={&#34;my-key&#34;: &#34;my-value&#34;}, query=&#34;language preferences&#34;
</span></span><span class="line"><span class="cl">    )
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="read-long-term-memory-in-tools">Read long-term memory in tools</h1>
<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-py" data-lang="py"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</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</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.tools</span> <span class="kn">import</span> <span class="n">ToolRuntime</span><span class="p">,</span> <span class="n">tool</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain_core.runnables</span> <span class="kn">import</span> <span class="n">Runnable</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.store.postgres</span> <span class="kn">import</span> <span class="n">PostgresStore</span>  <span class="c1"># type: ignore[import-not-found]</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">@dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Context</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="nb">str</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">DB_URI</span> <span class="o">=</span> <span class="s2">&#34;postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="n">PostgresStore</span><span class="o">.</span><span class="n">from_conn_string</span><span class="p">(</span><span class="n">DB_URI</span><span class="p">)</span> <span class="k">as</span> <span class="n">store</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">setup</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">put</span><span class="p">((</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="s2">&#34;user_123&#34;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;John Smith&#34;</span><span class="p">,</span> <span class="s2">&#34;language&#34;</span><span class="p">:</span> <span class="s2">&#34;English&#34;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@tool</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_user_info</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">[</span><span class="n">Context</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Look up user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">assert</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="n">user_info</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">get</span><span class="p">((</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">user_info</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="k">if</span> <span class="n">user_info</span> <span class="k">else</span> <span class="s2">&#34;Unknown user&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">agent</span><span class="p">:</span> <span class="n">Runnable</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="s2">&#34;claude-sonnet-4-6&#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">get_user_info</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">context_schema</span><span class="o">=</span><span class="n">Context</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="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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;look up user information&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">        <span class="n">context</span><span class="o">=</span><span class="n">Context</span><span class="p">(</span><span class="n">user_id</span><span class="o">=</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><h1 id="write-long-term-memory-from-tools">Write long-term memory from tools</h1>
<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></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">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</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</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.tools</span> <span class="kn">import</span> <span class="n">ToolRuntime</span><span class="p">,</span> <span class="n">tool</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain_core.runnables</span> <span class="kn">import</span> <span class="n">Runnable</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langgraph.store.postgres</span> <span class="kn">import</span> <span class="n">PostgresStore</span>  <span class="c1"># type: ignore[import-not-found]</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">TypedDict</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">@dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Context</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="nb">str</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">UserInfo</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="p">:</span> <span class="nb">str</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">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">save_user_info</span><span class="p">(</span><span class="n">user_info</span><span class="p">:</span> <span class="n">UserInfo</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">[</span><span class="n">Context</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Save user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">assert</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">put</span><span class="p">((</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">user_id</span><span class="p">,</span> <span class="nb">dict</span><span class="p">(</span><span class="n">user_info</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;Successfully saved user info.&#34;</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">DB_URI</span> <span class="o">=</span> <span class="s2">&#34;postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="n">PostgresStore</span><span class="o">.</span><span class="n">from_conn_string</span><span class="p">(</span><span class="n">DB_URI</span><span class="p">)</span> <span class="k">as</span> <span class="n">store</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">setup</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">agent</span><span class="p">:</span> <span class="n">Runnable</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="s2">&#34;claude-sonnet-4-6&#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">save_user_info</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">context_schema</span><span class="o">=</span><span class="n">Context</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="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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;My name is John Smith&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">        <span class="n">context</span><span class="o">=</span><span class="n">Context</span><span class="p">(</span><span class="n">user_id</span><span class="o">=</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>]]></content:encoded>
    </item>
    <item>
      <title>Models</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/models/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/models/</guid>
      <description>Models相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>In addition to text generation,many LLMs provide :tool calling,Structured output,Multimodality,Reasoning
different model excel at different tasks</p>
<p>LangChain has  Standard model interface,which allow you to experiment with and switch models</p>
<h1 id="basic-usage">Basic usage</h1>
<p>1.with agents 2.Standalone(outside the agent loop)</p>
<h2 id="initialize-a-model">Initialize a model</h2>
<p>the easiest way: use <code>init_chat_model</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">import os
</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">os.environ[&#34;OPENAI_API_KEY&#34;] = &#34;sk-...&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">model = init_chat_model(&#34;gpt-5.5&#34;)
</span></span></code></pre></td></tr></table>
</div>
</div><p>key method:
invoke,streaming.patch(批量处理)</p>
<h2 id="parameters">Parameters</h2>
<p>model: string (required)
api_key: string
temperature: number
max_tokens: number
timeout: number
max_retries: number (default: 6)</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">model = init_chat_model(
</span></span><span class="line"><span class="cl">    &#34;claude-sonnet-4-6&#34;,
</span></span><span class="line"><span class="cl">    # Kwargs passed to the model:
</span></span><span class="line"><span class="cl">    temperature=0.7,
</span></span><span class="line"><span class="cl">    timeout=30,
</span></span><span class="line"><span class="cl">    max_tokens=1000,
</span></span><span class="line"><span class="cl">    max_retries=6,  # Default; increase for unreliable networks
</span></span><span class="line"><span class="cl">)
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="connection-resilience连接韧性">Connection resilience(连接韧性?)</h2>
<p>LangChain chat models automatically retry failed API requests with exponential backoff</p>
<p>By default, models retry up to <strong>6 times</strong> for network errors, rate limits (429), and server errors (5xx). Client errors like 401 (unauthorized) or 404 are not retried.</p>
<p>Use <strong>max_retries</strong> and <strong>timeout</strong></p>
<h3 id="invocation">Invocation</h3>
<h3 id="invoke">invoke:</h3>
<p>with a message along with a thread_id
role of a message: models use it to verify who sent the message</p>
<h3 id="streaming">Streaming:</h3>
<p>Calling <a href="https://reference.langchain.com/python/langchain-core/language_models/chat_models/BaseChatModel/stream"><code>stream()</code></a> returns an iterator that yields output chunks as they are produced. You can use a loop to process each chunk in real-time:</p>
<p>Return <code>AIMessageChunk</code>objects ,each chunk in a stream is designed to be gathered into a full message via summation</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">full = None  # None | AIMessageChunk
</span></span><span class="line"><span class="cl">for chunk in model.stream(&#34;What color is the sky?&#34;):
</span></span><span class="line"><span class="cl">    full = chunk if full is None else full + chunk
</span></span><span class="line"><span class="cl">    print(full.text)
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="batch">Batch</h3>
<p>Batching a collection of independent requests to a model can significantly improve performance and reduce costs, as the processing can be done in parallel:</p>
<p>Use <code>batch_as_completed()</code> to stream result,but the response is out of order</p>
<h2 id="tool-calling">Tool calling</h2>
<ol>
<li>A schema, including the name of the tool, a description, and/or argument definitions (often a JSON schema)</li>
<li>A function or coroutine to execute.</li>
</ol>
<p>Basic tool calling flow:</p>
<p><img alt="image-20260919161339819" loading="lazy" src="c:\\Users\\14152\\AppData\\Roaming\\Typora\\typora-user-images\\image-20260919161339819.png"></p>
<p>Use <code>bind_tools</code> to make tools you have defined available</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></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">tools</span> <span class="n">import</span> <span class="k">tool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather</span><span class="p">(</span><span class="n">location</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get the weather at a location.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;It&#39;s sunny in {location}.&#34;</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">model_with_tools</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">bind_tools</span><span class="p">([</span><span class="n">get_weather</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="common-way-to-use-tool-calling">Common way to use tool calling</h4>
<ul>
<li>Tool execution loop</li>
<li>Forcing tool calls</li>
<li>Parallel tool calls</li>
<li>Streaming tool calls:
tool calls are progressively built through <a href="https://reference.langchain.com/python/langchain-core/messages/tool/ToolCallChunk"><code>ToolCallChunk</code></a>
You can accumulate chunks to build complete tool calls</li>
</ul>
<h2 id="structured-output">Structured output</h2>
<p>Provide their response in a format matching a given schema
LangChain supports multiple schema types and methods for enforcing structured output.</p>
<p><a href="https://docs.pydantic.dev/latest/concepts/models/#basic-model-usage">Pydantic models</a> provide the richest feature set with field validation, descriptions, and nested structures.</p>
<p>Python’s <code>TypedDict</code> provides a simpler alternative to Pydantic models, ideal when you don’t need runtime validation.</p>
<p>Provide a <a href="https://json-schema.org/understanding-json-schema/about">JSON Schema</a> for maximum control and interoperability.</p>
<p>1.raw message和parsed message并存 2.nested structure</p>
<h2 id="multimodal">Multimodal</h2>
<p>You can use content block to provide ono-text data to the model
If the response contains multimodal data,the resulting AIMessage</p>
<p>will have content blocks with multimodal types</p>
<h2 id="reasoning">Reasoning</h2>
<p>Many models are capable of performing multi-step reasoning to arrive at a conclusion. This involves breaking down complex problems into smaller, more</p>
<p>manageable steps.<strong>If supported by the underlying model,</strong> you can surface this reasoning process to better understand how the model arrived at its final answer.</p>
<p>Depending on the model,you can sometimes specify the level of effort it should put into reasoning. Using reasoning_effort parameter when invoking.</p>
<h2 id="local-models">Local models</h2>
<h2 id="prompt-caching">Prompt caching</h2>
<p>ou can engage caching at three levels:</p>
<ul>
<li>
<p>Implicit provider caching:</p>
</li>
<li>
<p>Provider-level explicit controls(显式):
providers let you manually indicate cache points for greater control or to guarantee cost savings. These mirror the underlying provider/API behavior. Examples:</p>
<ul>
<li><a href="https://reference.langchain.com/python/langchain-openai/chat_models/base/ChatOpenAI"><code>ChatOpenAI</code></a> (via <code>prompt_cache_key</code>)</li>
<li>Anthropic content-block <a href="https://docs.langchain.com/oss/python/integrations/chat/anthropic#prompt-caching"><code>cache_control</code></a></li>
<li><a href="https://reference.langchain.com/python/integrations/langchain_google_genai/">Gemini</a>.</li>
<li>AWS Bedrock <a href="https://docs.langchain.com/oss/python/integrations/chat/bedrock#prompt-caching"><code>cachePoint</code></a> blocks</li>
</ul>
</li>
<li>
<p>LangChain middleware:</p>
<p>for agents, middleware lets LangChain optimize caching of stable system prompt and tool content. Examples:</p>
<ul>
<li>Anthropic’s <a href="https://docs.langchain.com/oss/python/integrations/middleware/anthropic#prompt-caching"><code>AnthropicPromptCachingMiddleware</code></a></li>
<li>AWS Bedrock’s <a href="https://docs.langchain.com/oss/python/integrations/middleware/aws#prompt-caching"><code>BedrockPromptCachingMiddleware</code></a></li>
</ul>
</li>
</ul>
<p>Cache usage will be reflected in the <a href="https://docs.langchain.com/oss/python/langchain/messages#token-usage">usage metadata</a> of the model response.</p>
<h2 id="server-side-tool-use">Server-side tool use</h2>
<p>Some providers support server-side <a href="https://docs.langchain.com/oss/python/langchain/models#tool-calling">tool-calling</a> loops: models can interact with web search, code interpreters, and other tools and analyze the results in a single conversational turn.</p>
<p>If a model invokes a tool server-side, the content of the response message will include content representing the invocation and result of the tool. Accessing the <a href="https://docs.langchain.com/oss/python/langchain/messages#standard-content-blocks">content blocks</a> of the response will return the server-side tool calls and results in a provider-agnostic format:</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></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">chat_models</span> <span class="n">import</span> <span class="n">init_chat_model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">model</span> <span class="o">=</span> <span class="n">init_chat_model</span><span class="p">(</span><span class="s2">&#34;gpt-5.4-mini&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">tool</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;web_search&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">model_with_tools</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">bind_tools</span><span class="p">([</span><span class="k">tool</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">model_with_tools</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="s2">&#34;What was a positive news story from today?&#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="n">response</span><span class="o">.</span><span class="n">content_blocks</span><span class="p">)</span>
</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><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></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="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;server_tool_call&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;web_search&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;args&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;query&#34;</span><span class="p">:</span> <span class="s2">&#34;positive news stories today&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;search&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;ws_abc123&#34;</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="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;server_tool_result&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tool_call_id&#34;</span><span class="p">:</span> <span class="s2">&#34;ws_abc123&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;success&#34;</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="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;text&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Here are some positive news stories from today...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;annotations&#34;</span><span class="p">:</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="s2">&#34;end_index&#34;</span><span class="p">:</span> <span class="mi">410</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;start_index&#34;</span><span class="p">:</span> <span class="mi">337</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;article title&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;citation&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;...&#34;</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><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>This represents a single conversational turn; there are no associated <a href="https://docs.langchain.com/oss/python/langchain/messages#tool-message">ToolMessage</a> objects that need to be passed in as in client-side <a href="https://docs.langchain.com/oss/python/langchain/models#tool-calling">tool-calling</a>.</p>
<h2 id="model-exceptions">Model exceptions</h2>
<p>Major integration packages raise standard exception types from <code>langchain_core.exceptions</code> for common model failures such as authentication errors, rate limits, and timeouts. These exceptions inherit from both the LangChain base type and the provider SDK’s own exception type, so you can catch either one:</p>
<h2 id="rate-limiting">Rate limiting</h2>
<p>To control your call frequency ,and i you hit it, you will receive a rate limit error</p>
<p>To help manage rate limits, chat model integrations accept a <code>rate_limiter</code> parameter that can be provided during initialization to control the rate at which requests are made.</p>
<h2 id="base-url-and-proxy-settings">Base URL and proxy settings</h2>
<p>You can configure a custom base URL for providers that implement the OpenAI Chat Completions API.</p>
<p><strong>Custom base URL:</strong></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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">model = init_chat_model(
</span></span><span class="line"><span class="cl">    model=&#34;MODEL_NAME&#34;,
</span></span><span class="line"><span class="cl">    model_provider=&#34;openai&#34;,
</span></span><span class="line"><span class="cl">    base_url=&#34;BASE_URL&#34;,
</span></span><span class="line"><span class="cl">    api_key=&#34;YOUR_API_KEY&#34;,
</span></span><span class="line"><span class="cl">)
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>HTTP proxy configuration:</strong></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></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_openai import ChatOpenAI
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">model = ChatOpenAI(
</span></span><span class="line"><span class="cl">    model=&#34;gpt-5.5&#34;,
</span></span><span class="line"><span class="cl">    openai_proxy=&#34;http://proxy.example.com:8080&#34;
</span></span><span class="line"><span class="cl">)
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="log-probabilities">Log probabilities</h2>
<h2 id="token-usage">Token usage</h2>
<p>A number of model providers return token usage information as part of the invocation response.</p>
<p>You can track aggregate token counts across models in an application using either a callback or context manager, as shown below:</p>
<p><strong>Callback:</strong></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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">You can track aggregate token counts across models in an application using either a callback or context manager, as shown below:
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>Context manager:</strong></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></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.chat_models import init_chat_model
</span></span><span class="line"><span class="cl">from langchain_core.callbacks import get_usage_metadata_callback
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">model_1 = init_chat_model(model=&#34;gpt-5.4-mini&#34;)
</span></span><span class="line"><span class="cl">model_2 = init_chat_model(model=&#34;claude-haiku-4-5-20251001&#34;)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">with get_usage_metadata_callback() as cb:
</span></span><span class="line"><span class="cl">    model_1.invoke(&#34;Hello&#34;)
</span></span><span class="line"><span class="cl">    model_2.invoke(&#34;Hello&#34;)
</span></span><span class="line"><span class="cl">    print(cb.usage_metadata)
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="invocation-config">Invocation config</h2>
<p>When invoking a model, you can pass additional configuration through the <code>config</code> parameter using a <a href="https://reference.langchain.com/python/langchain-core/runnables/config/RunnableConfig"><code>RunnableConfig</code></a> dictionary. This provides run-time control over execution behavior, callbacks, and metadata tracking.</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">response = model.invoke(
</span></span><span class="line"><span class="cl">    &#34;Tell me a joke&#34;,
</span></span><span class="line"><span class="cl">    config={
</span></span><span class="line"><span class="cl">        &#34;run_name&#34;: &#34;joke_generation&#34;,      # Custom name for this run
</span></span><span class="line"><span class="cl">        &#34;tags&#34;: [&#34;humor&#34;, &#34;demo&#34;],          # Tags for categorization
</span></span><span class="line"><span class="cl">        &#34;metadata&#34;: {&#34;user_id&#34;: &#34;123&#34;},     # Custom metadata
</span></span><span class="line"><span class="cl">        &#34;callbacks&#34;: [my_callback_handler], # Callback handlers
</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><p>Key configuration attributes
run_name: string : identifier
tags: string[]
metadata: object
max_concurrency: number
callbacks: array
recursion_limit: number</p>
<p>Configurable models:
You can also create a runtime-configurable model by specifying <a href="https://reference.langchain.com/python/langchain_core/language_models/#langchain_core.language_models.chat_models.BaseChatModel.configurable_fields"><code>configurable_fields</code></a>. If you don’t specify a model value, then <code>'model'</code> and <code>'model_provider'</code> will be configurable by default.</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></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.chat_models import init_chat_model
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">configurable_model = init_chat_model(temperature=0)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">configurable_model.invoke(
</span></span><span class="line"><span class="cl">    &#34;what&#39;s your name&#34;,
</span></span><span class="line"><span class="cl">    config={&#34;configurable&#34;: {&#34;model&#34;: &#34;gpt-5-nano&#34;}},  # Run with GPT-5-Nano
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">configurable_model.invoke(
</span></span><span class="line"><span class="cl">    &#34;what&#39;s your name&#34;,
</span></span><span class="line"><span class="cl">    config={&#34;configurable&#34;: {&#34;model&#34;: &#34;claude-sonnet-4-6&#34;}},  # Run with Claude
</span></span><span class="line"><span class="cl">)
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="dynamic-model-selection">Dynamic model selection</h2>
<p>Dynamic models are selected at runtime based on the current state and context. This enables sophisticated routing logic and cost optimization.</p>
<p>To use a dynamic model, create middleware using the <a href="https://reference.langchain.com/python/langchain/agents/middleware/types/wrap_model_call"><code>@wrap_model_call</code></a> decorator that modifies the model in the request:</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></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_openai</span> <span class="n">import</span> <span class="n">ChatOpenAI</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">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">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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">basic_model</span> <span class="o">=</span> <span class="n">ChatOpenAI</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-5.4-mini&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">advanced_model</span> <span class="o">=</span> <span class="n">ChatOpenAI</span><span class="p">(</span><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></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">dynamic_model_selection</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">,</span> <span class="n">handler</span><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;Choose model based on conversation complexity.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">message_count</span> <span class="o">=</span> <span class="n">len</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="s2">&#34;messages&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">message_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="c1"># Use an advanced model for longer conversations</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span> <span class="o">=</span> <span class="n">advanced_model</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span> <span class="o">=</span> <span class="n">basic_model</span>
</span></span><span class="line"><span class="cl">
</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">model</span><span class="o">=</span><span class="n">model</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="n">basic_model</span><span class="p">,</span>  <span class="c1"># Default model</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="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">dynamic_model_selection</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>Pre-bound models (models with <a href="https://reference.langchain.com/python/langchain-core/language_models/chat_models/BaseChatModel/bind_tools"><code>bind_tools</code></a> already called) are not supported when using structured output. If you need dynamic model selection with structured output, ensure the models passed to the middleware are not pre-bound.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Open-SWE</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/%E9%A1%B9%E7%9B%AE%E9%98%85%E8%AF%BB%E6%89%80%E5%BE%97/open-swe/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/%E9%A1%B9%E7%9B%AE%E9%98%85%E8%AF%BB%E6%89%80%E5%BE%97/open-swe/</guid>
      <description>Open-SWE相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="convention-part">Convention part</h1>
<h2 id="1使用async方法只在必须的时候才使用sync方法不是通用的设计范式">1.使用async方法,只在必须的时候才使用sync方法(不是通用的设计范式):</h2>
<p>约束之一为:Use async-only implementations. Add a sync method only when an interface requires it, and then raise <code>NotImplementedError</code>.</p>
<p>1.使用async方法优化并发性能 2.使用NotImplementedError显式告知为提供async实现,采用同步方法</p>
<h2 id="2use-strong-types-everywhere">2.Use strong types everywhere</h2>
<ol>
<li>类型安全:防止隐式转换带来的诸多问题</li>
<li>代码即文档(Self-Documenting Code)(可读性)与可维护性:该可读性不只是对于人,也可以是LLM,agent等等</li>
</ol>
<p>常见的情况是:
Never use Any</p>
<h2 id="3use-absolute-imports-across-packages-same-package-imports-may-start-with-one-dot-never-use-parent-relative-imports">3.Use absolute imports across packages; same-package imports may start with one dot. Never use parent-relative imports.</h2>
<p>解释:1.使用绝对路径而非相对路径2.同一个package内部的导入可以使用.
3.但是禁止使用多<code>.</code>来向上级或者祖先目录寻找模块</p>
<p>作用:
1.编码&quot;跨层级重构&quot;引入的导入崩溃
2.增强代码的可读性
3.允许单点<code>.</code>的折中考虑:既保留了同模块的内聚性,又省略了编码的繁琐性</p>
<h2 id="4use-structured-logging-with-a-static-message-and-values-in-extra-never-interpolate-values-into-log-messages-avoid-standard-logrecord-field-names-in-extra">4.Use structured logging with a static message and values in extra; never interpolate values into log messages. Avoid standard LogRecord field names in extra.</h2>
<p>使用结构化日志,保留固定的静态信息,将动态变量放入extra属性中
1.优化了日志聚合和分析系统
2.防止破坏日志结构的内部状态
3.增加日志的安全性,防止个人私密信息的泄露</p>
<h2 id="5never-discard-an-error-every-except-either-propagates-re-raise-or-raise-a-more-useful-error-or-logs-what-it-swallowed--a-bare-except--return-none--pass-hides-the-failure-from-everyone-debugging-it-later">5.Never discard an error. Every <code>except</code> either propagates (re-raise, or raise a more useful error) or logs what it swallowed — a bare <code>except ...: return None</code> / <code>pass</code> hides the failure from everyone debugging it later.</h2>
<p>1.向上抛出更具业务语义的异常
2.记录日志后重新抛出
3.吞掉异常,但必须记录日志</p>
<h1 id="test-part">Test Part</h1>
<p>运行测试:
切勿在本地运行完整测试套件；仅运行与本次变更相关的测试。
添加测试:
仅当测试能有意义地保护可观察行为时才添加测试。不要添加仅简单重复常量、映射、提示词文本、源码结构或内部调用顺序等非本质交互的“变更检测（change-detector）”测试。
覆盖有意义的边缘情况，并保持测试的确定性（deterministic）。</p>
<h1 id="codes">Codes</h1>
<h2 id="1显式导出接口">1.显式导出接口:</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></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="c1"># 1. 显式引入（Re-export / Forwarding）：</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 从子模块/深层模块 agent.server 中导入 get_agent 和 traced_agent</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">agent.server</span> <span class="kn">import</span> <span class="n">get_agent</span><span class="p">,</span> <span class="n">traced_agent</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 导出控制（Public API Definition）：</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 定义该模块对外的公开 API 白名单</span>
</span></span><span class="line"><span class="cl"><span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;get_agent&#34;</span><span class="p">,</span> <span class="s2">&#34;traced_agent&#34;</span><span class="p">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>1.精简包的导出路径
2.控制from module import * 只有在__all__中的对象才会被导
3.静态类型检查:将&ndash;明确识别为公开给外部调用的Pubilc API</p>
<h2 id="2_function_namefunction_name的关系">2._function_name(),function_name()的关系</h2>
<p>OOP中的封装和面向接口编程的思想
在Python中,函数前带有_下划线的通常被认为是私有函数(认为,这是大家的一种约定俗称,但是并没有真正意义上的private函数),
而通过这个方法,将function_name()暴露为接口,将__function_name()封装为内部实现</p>
<h2 id="3日志的冒泡传递机制">3.日志的冒泡传递机制</h2>
<p>当当前目录的logger是一个没有绑定任何输出目的地(Handler)的节点时,他会把日志信息抛给父级logger</p>
<h1 id="serverpy中关于subagent的创建部分">Server.py中关于subagent的创建部分</h1>
<h2 id="1显式沙盒隔离">1.显式沙盒隔离</h2>
<p>对于敏感工具的调用,不是使用prompt劝导不要调用敏感工具,而是直接使用<code>_SubagentToolGuard</code> 和 <code>is_subagent_excluded_tool</code>的MIddleware进行了切面阻断
且拦截后不抛出异常导致崩溃,而是优雅的返回结构化的ToolMessage进行告知</p>
<h2 id="2纵深防御的中间件分层">2.纵深防御的中间件分层:</h2>
<p>代码将中间件按职责严格分层装配：</p>
<ul>
<li><strong>模型层</strong> (<code>_subagent_model_middleware</code>)：拦截 LLM 调用的基础异常（超时、格式清洗、报错捕捉）。</li>
<li><strong>运行期层</strong> (<code>_subagent_middleware</code>)：工作流推送保护、动态工具注入。</li>
<li><strong>环境守卫层</strong> (<code>_subagent_guard_middleware</code>)：区分 <code>local_run</code> 环境（桌面端自由执行 vs 云端环境 PR 拦截）。 这种模块化的 <code>list</code> 解包组合（<code>*</code> 语法），极大地提升了中间件复用率。</li>
</ul>
<h2 id="3显式继承覆盖机制">3.显式继承覆盖机制</h2>
<p>通过 <code>_DisableInheritedMiddleware</code> 解决了子图继承父图中间件时的冲突问题。子 Agent 作为独立图编译，有时继承父级的某些中间件（如重复检查消息队列）会导致死锁或逻辑冗余，显式声明 <code>inherited_middleware_exclusions</code> 实现了精细的取消重写。</p>
<h2 id="4提示词动态增强">4.提示词动态增强</h2>
<p>SubAgent 的 <code>description</code> 决定了 Parent Agent 能否准确路由任务。源码不仅使用基础描述，还动态追加了 <code>load_prompt('system/general-purpose-subagent-suffix.md')</code>
这个技巧在返回敏感工具调用的ToolMessage也有使用</p>
<h1 id="5创建subagent的标准通用流程">5.创建Subagent的标准通用流程</h1>
<h2 id="1确定运行模式和分叉策略">1.确定运行模式和分叉策略</h2>
<p>确定Subagent的隔离级别</p>
<h2 id="2裁剪工具集和构建安全工具防护网">2.裁剪工具集和构建安全工具防护网</h2>
<p>列出Subagent可用的工具,并使用切面Middleware横切阻断违规操作</p>
<h2 id="3组装中间件栈">3.组装中间件栈</h2>
<p>按顺序:
权限拦截 -&gt; 日志/轨迹记录 -&gt; 动态工具 -&gt; 模型超时/容错控制 -&gt; 上下文卸载</p>
<h2 id="4根据运行环境注入安全守卫">4.根据运行环境注入安全守卫</h2>
<p>根据部署目标（如本地 CLI、Docker 容器或 LangGraph Cloud）注入环境特定的守卫，如禁用高危 Shell 命令或 PR 自动提交</p>
<h2 id="5拼接路由-prompt-与定义能力边界">5.拼接路由 Prompt 与定义能力边界</h2>
<p>为 SubAgent 编写明确的 <code>name</code> 和 <code>description</code>，并附加标准化的系统后缀说明（Suffix），帮助主 Agent 准确判断何种子任务需要委派（Delegate）。</p>
<h2 id="6打包工厂函数并编译子图">6.打包工厂函数并编译子图</h2>
<p>将上述组件封装工厂函数，返回符合框架规范的 SubAgent 配置结构供图引擎（Graph Engine）统一编译。</p>
<h1 id="6海象运算符">6.海象运算符</h1>
<p>:= 含义为赋值并返回赋值之后的值</p>
<h1 id="7创建fastapi应用总结apiapppy文件">7.创建FastAPI应用总结(api/app.py文件)</h1>
<p><strong>配置校验前置</strong>：配置、数据库、云端沙箱和本地 LLM 的校验（<code>validate_*</code>）全部在 <code>lifespan</code> 启动阶段完成，遵循 <strong>Fail-Fast（快速失败）</strong> 原则[cite: 1]。</p>
<p><strong>数据迁移幂等设计</strong>：版本升级时的存储迁移代码应当做成 <strong><code>no-op</code> 驱动的防重入函数</strong>，并容忍局部失败[cite: 1]。</p>
<p><strong>资源清理彻底</strong>：在 <code>lifespan</code> 的 <code>finally</code> 块中集中释放 DB 连接、HTTP Client 和 Worker 句柄，防止 Docker 容器优雅停机（SIGTERM）时出现资源泄漏或数据损坏[cite: 1]。</p>
<h1 id="8版本向下兼容和容错包的导入技巧">8.版本向下兼容和容错包的导入技巧</h1>
<p>try-except Import Error组合
优先尝试符合新规范的导入方式;导入失败自动降级(fallback)到旧版的导入路径</p>
<h1 id="9dispatchpy的思想">9.dispatch.py的思想</h1>
<p>对于不同的data source,将其进行归一化然后在传入进行处理,提高了代码的可阅读性和可复用性,而不是建立对于每个data source都进行独特的处理</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>
    <item>
      <title>Retrieval</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/retrieval/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/retrieval/</guid>
      <description>Retrieval相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>Two limitations of LLM:</p>
<ol>
<li>finite context</li>
<li>Static Knowledge</li>
</ol>
<p>RAG(检索增强生成)</p>
<h1 id="building-a-knowledge-base">Building a knowledge base</h1>
<p>A repository of documents of structured data
使用LangChain&rsquo;s document loaders and vector stores 来建立你自己的知识库
如果你已经有知识库,你不需要重建,你只需要</p>
<ul>
<li>Connect it as a tool for agent in agentic RAG</li>
<li>Query it and supply the retrieved content as context to the LLM <a href="https://docs.langchain.com/oss/python/deepagents/retrieval#2-step-rag">(2-Step RAG)</a></li>
</ul>
<h2 id="from-retrieval-to-rag">From retrieval to RAG</h2>
<h2 id="retrieval-pipeline">Retrieval pipeline</h2>
<p><img alt="image-20260922210616005" loading="lazy" src="c:\\Users\\14152\\AppData\\Roaming\\Typora\\typora-user-images\\image-20260922210616005.png"></p>
<p>Document loaders,text splitters,Embedding models,vector stores,retrievers</p>
<h1 id="rag-architectures">RAG architectures</h1>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Architecture</th>
					<th style="text-align: left">Description</th>
					<th style="text-align: left">Control</th>
					<th style="text-align: left">Flexibility</th>
					<th style="text-align: left">Latency</th>
					<th style="text-align: left">Example Use Case</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>2-Step RAG</strong></td>
					<td style="text-align: left">Retrieval always happens before generation. Simple and predictable</td>
					<td style="text-align: left">✅ High</td>
					<td style="text-align: left">❌ Low</td>
					<td style="text-align: left">⚡ Fast</td>
					<td style="text-align: left">FAQs, documentation bots</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Agentic RAG</strong></td>
					<td style="text-align: left">An LLM-powered agent decides <em>when</em> and <em>how</em> to retrieve during reasoning</td>
					<td style="text-align: left">❌ Low</td>
					<td style="text-align: left">✅ High</td>
					<td style="text-align: left">⏳ Variable</td>
					<td style="text-align: left">Research assistants with access to multiple tools</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Hybrid</strong></td>
					<td style="text-align: left">Combines characteristics of both approaches with validation steps</td>
					<td style="text-align: left">⚖️ Medium</td>
					<td style="text-align: left">⚖️ Medium</td>
					<td style="text-align: left">⏳ Variable</td>
					<td style="text-align: left">Domain-specific Q&amp;A with quality validation</td>
			</tr>
	</tbody>
</table>
<h2 id="2-step-rag">2-step RAG</h2>
<p><img alt="image-20260922210932476" loading="lazy" src="c:\\Users\\14152\\AppData\\Roaming\\Typora\\typora-user-images\\image-20260922210932476.png"></p>
<h2 id="agentic-rag">Agentic RAG</h2>
<p><img alt="image-20260922211016040" loading="lazy" src="c:\\Users\\14152\\AppData\\Roaming\\Typora\\typora-user-images\\image-20260922211016040.png"></p>
<p>由LLM来决定是否需要检索和如何检索信息</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></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">import</span> <span class="nn">requests</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.tools</span> <span class="kn">import</span> <span class="n">tool</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.chat_models</span> <span class="kn">import</span> <span class="n">init_chat_model</span>
</span></span><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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_url</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch text content from a URL&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">10.0</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">raise_for_status</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">text</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">system_prompt</span> <span class="o">=</span> <span class="s2">&#34;&#34;&#34;</span><span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="s2">Use fetch_url when you need to fetch information from a web-page; quote relevant snippets.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</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">tools</span><span class="o">=</span><span class="p">[</span><span class="n">fetch_url</span><span class="p">],</span> <span class="c1"># A tool for retrieval</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span><span class="o">=</span><span class="n">system_prompt</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="hybrid-rag">Hybrid RAG</h2>
<p><img alt="image-20260922211229661" loading="lazy" src="c:\\Users\\14152\\AppData\\Roaming\\Typora\\typora-user-images\\image-20260922211229661.png"></p>
<p>结合了前两者的优点,核心组件包括:</p>
<p>查询增强:修改question来提高检索质量,这可能包括重写不清晰的查询、生成多个变体，或使用其他上下文扩展查询。</p>
<p>检索验证:评估检索到的资料是否充足</p>
<p>答案验证:检查生成的答案准确性,完整性和于源文件的一致性</p>
<p>此架构适用于</p>
<ul>
<li>存在模糊或描述不详的查询的应用场景</li>
<li>需要验证或质量控制步骤的系统</li>
<li>涉及多个数据源或迭代优化的工作流</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Short-term Memory</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/short-term-memory/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/short-term-memory/</guid>
      <description>Short-term Memory相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>Conversation history is the most common form of short-term memory</p>
<p>Fact1: message includes instructions and inpus</p>
<h1 id="usage">Usage</h1>
<p>To add short-term memory to an agent ,you need to specify a <code>checkpointer</code> when creating an agent
note: short-term memory is part of your agent&rsquo;s state. State is persisted in a database using a checkpointer so thread can resume it an any time.
Short-term memory updates when the agent is invoked or a step (like a tool call) is completed, and the state is read at the start of each step.</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></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">langgraph</span><span class="o">.</span><span class="n">checkpoint</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemorySaver</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">get_user_info</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Look up information about the current user.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;No user profile on file.&#34;</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;openai: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">get_user_info</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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="n">thread_config</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#34;</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">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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Hi! My name is Bob.&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">thread_config</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>  <span class="c1"># &#34;Hi Bob! Nice to see you here. How are you doing?&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">response</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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s my name?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">thread_config</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>  <span class="c1"># &#34;You are Bob!&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="in-production">In production</h2>
<p>use a checkpointer backed by a database</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></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">langgraph</span><span class="o">.</span><span class="n">checkpoint</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemorySaver</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">get_user_info</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Look up information about the current user.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;No user profile on file.&#34;</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;openai: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">get_user_info</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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="n">thread_config</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#34;</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">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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Hi! My name is Bob.&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">thread_config</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>  <span class="c1"># &#34;Hi Bob! Nice to see you here. How are you doing?&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">response</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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s my name?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">thread_config</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>  <span class="c1"># &#34;You are Bob!&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="customizing-agent-memory">Customizing agent memory</h1>
<p>Agents use <code>AgentState</code> to manage short-term memory, specifically the
conversation history via a <code>message</code> key</p>
<p>You can extend <code>AgentState</code> to ad additional fields
Custom state schema are passed to <code>create_agent</code> using the <code>state_scheam</code> parameter</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></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 class="p">,</span> <span class="n">AgentState</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">checkpoint</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemorySaver</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">CustomAgentState</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">user_id</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">    <span class="n">preferences</span><span class="p">:</span> <span class="n">dict</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="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">get_user_info</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">state_schema</span><span class="o">=</span><span class="n">CustomAgentState</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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"># Custom state can be passed in invoke</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</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;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="s2">&#34;preferences&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;theme&#34;</span><span class="p">:</span> <span class="s2">&#34;dark&#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 class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#34;</span><span class="p">}})</span>
</span></span><span class="line"><span class="cl">    
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="common-patterns">Common patterns</h1>
<p>Common solutions to exceed the LLM&rsquo;s context window</p>
<h2 id="trim-message截断消息">Trim message(截断消息)</h2>
<p>A way: keep track of the token count and truncate when it approaches that limit</p>
<p>Using trim messages utility and specify the number if tokens to keep ang the <code>strategy</code> to use for handing the boundary</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><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></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></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">before_mfrom</span> <span class="n">langchain</span><span class="o">.</span><span class="n">messages</span> <span class="n">import</span> <span class="n">RemoveMessage</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">graph</span><span class="o">.</span><span class="n">message</span> <span class="n">import</span> <span class="n">REMOVE_ALL_MESSAGES</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">checkpoint</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemorySaver</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 class="p">,</span> <span class="n">AgentState</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">before_model</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">langchain_core</span><span class="o">.</span><span class="n">runnables</span> <span class="n">import</span> <span class="n">RunnableConfig</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></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">trim_messages</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="s2">&#34;&#34;&#34;Keep only the last few messages to fit context window.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">messages</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">len</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">None</span>  <span class="c1"># No changes needed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">first_msg</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">recent_messages</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">:]</span> <span class="k">if</span> <span class="n">len</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">messages</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_messages</span> <span class="o">=</span> <span class="p">[</span><span class="n">first_msg</span><span class="p">]</span> <span class="o">+</span> <span class="n">recent_messages</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</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></span><span class="line"><span class="cl">            <span class="n">RemoveMessage</span><span class="p">(</span><span class="n">id</span><span class="o">=</span><span class="n">REMOVE_ALL_MESSAGES</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="o">*</span><span class="n">new_messages</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="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="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="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">trim_messages</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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="n">config</span><span class="p">:</span> <span class="n">RunnableConfig</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#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">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;hi, my name is bob&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">agent</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;write a short poem about cats&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">agent</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;now do the same but for dogs&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">final_response</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;what&#39;s my name?&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">final_response</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">pretty_print</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">================================== Ai Message ==================================
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Your name is Bob. You told me that earlier.
</span></span></span><span class="line"><span class="cl"><span class="s2">If you&#39;d like me to call you a nickname or use a different name, just say the word.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="delete-message">Delete message</h2>
<p>Useful when you want to remove specific message or clear the entire message history
Use <code>RemoveMessage</code> to delete message from the graph state
and you need to use a state key with <code>add_messages</code> reducer and  default the<code>AgentState</code> provides it</p>
<h4 id="to-remove-specific-messages">To remove specific messages:</h4>
<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></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.messages import RemoveMessage  
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">def delete_messages(state):
</span></span><span class="line"><span class="cl">    messages = state[&#34;messages&#34;]
</span></span><span class="line"><span class="cl">    if len(messages) &gt; 2:
</span></span><span class="line"><span class="cl">        # remove the earliest two messages
</span></span><span class="line"><span class="cl">        return {&#34;messages&#34;: [RemoveMessage(id=m.id) for m in messages[:2]]}
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="to-remove-all-messages">To remove all messages:</h4>
<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">from langgraph.graph.message import REMOVE_ALL_MESSAGES  
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">def delete_messages(state):
</span></span><span class="line"><span class="cl">    return {&#34;messages&#34;: [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}
</span></span></code></pre></td></tr></table>
</div>
</div><p>ps:
删除消息时，<strong>请确保</strong>生成的消息历史有效。检查你正在使用的 LLM 提供商的限制。例如：</p>
<ul>
<li>某些提供程序期望消息历史记录以 <code>user</code> 消息开头</li>
<li>大多数提供程序要求带有工具调用的 <code>assistant</code> 消息后跟相应的 <code>tool</code> 结果消息。</li>
</ul>
<h2 id="summarize-messages">Summarize messages</h2>
<p>above pattern will lose information
Summarizing message with a LLM and use the built-in <code>SUmmarizationMiddleware</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></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">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="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></span><span class="line"><span class="cl">        <span class="n">SummarizationMiddleware</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.4-mini&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">trigger</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;tokens&#34;</span><span class="p">,</span> <span class="mi">4000</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">keep</span><span class="o">=</span><span class="p">(</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="mi">20</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 class="n">checkpointer</span><span class="o">=</span><span class="n">checkpointer</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="access-memory">Access memory</h1>
<h2 id="tools">tools</h2>
<h3 id="read-short-term-memory-in-a-tool">Read short-term memory in a tool</h3>
<p>Using <code>runtime</code> parameter(typed as <code>ToolRuntime</code>)
runtime parameter can not be seen by model but the tool can access the state through it</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></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="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_info</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Look up user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_id</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">state</span><span class="p">[</span><span class="s2">&#34;user_id&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;User is John Smith&#34;</span> <span class="k">if</span> <span class="n">user_id</span> <span class="o">==</span> <span class="s2">&#34;user_123&#34;</span> <span class="k">else</span> <span class="s2">&#34;Unknown user&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="write-short-term-memory-form-tools">write short-term memory form tools</h3>
<p>Using return Command with a <code>ToolMessage</code></p>
<h2 id="prompt">Prompt</h2>
<p>Using middleware to access to ~ to create dynamic prompts on conversation history or custom state fields</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></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">typing</span> <span class="n">import</span> <span class="n">TypedDict</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">dynamic_prompt</span><span class="p">,</span> <span class="n">ModelRequest</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">CustomContext</span><span class="p">(</span><span class="n">TypedDict</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_name</span><span class="p">:</span> <span class="nb">str</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">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get the weather in a city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;The weather in {city} is always sunny!&#34;</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">dynamic_prompt</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">dynamic_system_prompt</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_name</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="p">[</span><span class="s2">&#34;user_name&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span> <span class="o">=</span> <span class="n">f</span><span class="s2">&#34;You are a helpful assistant. Address the user as {user_name}.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">system_prompt</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="n">get_weather</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">dynamic_system_prompt</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">context_schema</span><span class="o">=</span><span class="n">CustomContext</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="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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What is the weather in SF?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">context</span><span class="o">=</span><span class="n">CustomContext</span><span class="p">(</span><span class="n">user_name</span><span class="o">=</span><span class="s2">&#34;John Smith&#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="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">result</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">msg</span><span class="o">.</span><span class="n">pretty_print</span><span class="p">()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="before-model">Before model</h2>
<p>Using @before_model middleware to process message before model calls</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><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></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">messages</span> <span class="n">import</span> <span class="n">RemoveMessage</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">graph</span><span class="o">.</span><span class="n">message</span> <span class="n">import</span> <span class="n">REMOVE_ALL_MESSAGES</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">checkpoint</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemorySaver</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 class="p">,</span> <span class="n">AgentState</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">before_model</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain_core</span><span class="o">.</span><span class="n">runnables</span> <span class="n">import</span> <span class="n">RunnableConfig</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></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">trim_messages</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="s2">&#34;&#34;&#34;Keep only the last few messages to fit context window.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">messages</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">len</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">None</span>  <span class="c1"># No changes needed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">first_msg</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">recent_messages</span> <span class="o">=</span> <span class="n">messages</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">:]</span> <span class="k">if</span> <span class="n">len</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">messages</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_messages</span> <span class="o">=</span> <span class="p">[</span><span class="n">first_msg</span><span class="p">]</span> <span class="o">+</span> <span class="n">recent_messages</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</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></span><span class="line"><span class="cl">            <span class="n">RemoveMessage</span><span class="p">(</span><span class="n">id</span><span class="o">=</span><span class="n">REMOVE_ALL_MESSAGES</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="o">*</span><span class="n">new_messages</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></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="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></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">trim_messages</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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="n">config</span><span class="p">:</span> <span class="n">RunnableConfig</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;1&#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">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;hi, my name is bob&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">agent</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;write a short poem about cats&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">agent</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;now do the same but for dogs&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">final_response</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="s2">&#34;what&#39;s my name?&#34;</span><span class="p">},</span> <span class="n">config</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">final_response</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">pretty_print</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">================================== Ai Message ==================================
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Your name is Bob. You told me that earlier.
</span></span></span><span class="line"><span class="cl"><span class="s2">If you&#39;d like me to call you a nickname or use a different name, just say the word.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>After model
Using @after_model</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></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">messages</span> <span class="n">import</span> <span class="n">RemoveMessage</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">checkpoint</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemorySaver</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 class="p">,</span> <span class="n">AgentState</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">after_model</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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">after_model</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">validate_response</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="o">|</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Remove messages containing sensitive words.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">STOP_WORDS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;password&#34;</span><span class="p">,</span> <span class="s2">&#34;secret&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_message</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</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 class="k">if</span> <span class="n">any</span><span class="p">(</span><span class="n">word</span> <span class="ow">in</span> <span class="n">last_message</span><span class="o">.</span><span class="n">content</span> <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">STOP_WORDS</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;messages&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">RemoveMessage</span><span class="p">(</span><span class="n">id</span><span class="o">=</span><span class="n">last_message</span><span class="o">.</span><span class="n">id</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">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></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">validate_response</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpointer</span><span class="o">=</span><span class="n">InMemorySaver</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>]]></content:encoded>
    </item>
    <item>
      <title>Runtime</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/runtime/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/advanced-usage/runtime/</guid>
      <description>Runtime相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>LangChain&rsquo;s <code>create_agent</code>的底层运行在LangGraph&rsquo;s runtime
LangGraph暴露一个Runtime object,提供以下的信息:
1.context:静态信息:user id,数据库连接和其他的依赖
2.Store:用于长期记忆的BaseStore实例
3.Stream writer:通过&quot;custom&quot;流式传输模式来硫化信息的object
4.Execution info: identity and retry information for the current execution(thread ID, run ID ,attmpt number)
5.Server info:在 LangGraph Server 上运行时的特定于服务器的元数据(assistant ID,graph ID, authenticated user
)
You can access the runtime information within tools and middleware</p>
<h1 id="access">Access</h1>
<p>当创建agent with <code>create_agent</code>时,可以传入一个context_schema来定义存储在agent的<code>Runtime</code>中的context的结构</p>
<p>当调用agent是,则可以传入<code>context</code> with 相关的参数</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></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">dataclasses</span> <span class="n">import</span> <span class="n">dataclass</span>
</span></span><span class="line"><span class="cl">
</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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">Context</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_name</span><span class="p">:</span> <span class="nb">str</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-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">context_schema</span><span class="o">=</span><span class="n">Context</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="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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s my name?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">context</span><span class="o">=</span><span class="n">Context</span><span class="p">(</span><span class="n">user_name</span><span class="o">=</span><span class="s2">&#34;John Smith&#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><h2 id="inside-tools">Inside tools</h2>
<p>你可以在tool中访问runtime来:</p>
<ul>
<li>Access the context</li>
<li>Read or write long-term memory</li>
<li>Wtrie tp the custom stream</li>
</ul>
<p>通过<code>ToolRuntime</code>参数来访问<code>Runtime</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></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">dataclasses</span> <span class="n">import</span> <span class="n">dataclass</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">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</span>  
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">Context</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="nb">str</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_user_email_preferences</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">[</span><span class="n">Context</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch the user&#39;s email preferences from the store.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_id</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">user_id</span>  
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">preferences</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&#34;The user prefers you to write a brief and polite email.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">memory</span> <span class="p">:</span><span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">get</span><span class="p">((</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="n">user_id</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">preferences</span> <span class="o">=</span> <span class="n">memory</span><span class="o">.</span><span class="n">value</span><span class="p">[</span><span class="s2">&#34;preferences&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">preferences</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="execution-info-and-server-info-inside-tools">Execution info and server info inside tools</h2>
<p>Access execution identity (thread ID, run ID) via <code>runtime.execution_info</code>, and server-specific metadata (assistant ID, authenticated user) via <code>runtime.server_info</code> when running on LangGraph Server</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></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">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">context_aware_tool</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;A tool that uses execution and server info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Access thread and run IDs</span>
</span></span><span class="line"><span class="cl">    <span class="n">info</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">execution_info</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;Thread: {info.thread_id}, Run: {info.run_id}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Access server info (only available on LangGraph Server)</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">server_info</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">server</span> <span class="n">is</span> <span class="ow">not</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;Assistant: {server.assistant_id}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">server</span><span class="o">.</span><span class="n">user</span> <span class="n">is</span> <span class="ow">not</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;User: {server.user.identity}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;done&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="inside-middleware">Inside middleware</h2>
<p>通过middleware访问runtime来:创建动态prompt,修改messages,根据上下文控制agent的行为</p>
<p>Use the <code>Runtime</code> parameter to access the <a href="https://reference.langchain.com/python/langgraph/runtime/Runtime"><code>Runtime</code></a> object inside <a href="https://docs.langchain.com/oss/python/langchain/middleware/custom#node-style-hooks">node-style hooks</a>. For <a href="https://docs.langchain.com/oss/python/langchain/middleware/custom#wrap-style-hooks">wrap-style hooks</a>, the <code>Runtime</code> object is available inside the <a href="https://reference.langchain.com/python/langchain/agents/middleware/types/ModelRequest"><code>ModelRequest</code></a> parameter.</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><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></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">dataclasses</span> <span class="n">import</span> <span class="n">dataclass</span>
</span></span><span class="line"><span class="cl">
</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">messages</span> <span class="n">import</span> <span class="n">AnyMessage</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 class="p">,</span> <span class="n">AgentState</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">dynamic_prompt</span><span class="p">,</span> <span class="n">ModelRequest</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="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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">Context</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_name</span><span class="p">:</span> <span class="nb">str</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Dynamic prompts</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">dynamic_prompt</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">dynamic_system_prompt</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_name</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">user_name</span>  
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span> <span class="o">=</span> <span class="n">f</span><span class="s2">&#34;You are a helpful assistant. Address the user as {user_name}.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">system_prompt</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Before model hook</span>
</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="n">Context</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">dict</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;Processing request for user: {runtime.context.user_name}&#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="c1"># After model hook</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">after_model</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">log_after_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="n">Context</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">dict</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;Completed request for user: {runtime.context.user_name}&#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">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">dynamic_system_prompt</span><span class="p">,</span> <span class="n">log_before_model</span><span class="p">,</span> <span class="n">log_after_model</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">context_schema</span><span class="o">=</span><span class="n">Context</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="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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s my name?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">context</span><span class="o">=</span><span class="n">Context</span><span class="p">(</span><span class="n">user_name</span><span class="o">=</span><span class="s2">&#34;John Smith&#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><h2 id="execution-info-and-server-info-inside-middleware">Execution info and server info inside middleware</h2>
<p>Middleware hooks can also access <code>runtime.execution_info</code> and <code>runtime.server_info</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-fallback" data-lang="fallback"><span class="line"><span class="cl">from langchain.agents import AgentState
</span></span><span class="line"><span class="cl">from langchain.agents.middleware import before_model
</span></span><span class="line"><span class="cl">from langgraph.runtime import Runtime
</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
</span></span><span class="line"><span class="cl">def auth_gate(state: AgentState, runtime: Runtime) -&gt; dict | None:
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Block unauthenticated users when running on LangGraph Server.&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">    server = runtime.server_info
</span></span><span class="line"><span class="cl">    if server is not None and server.user is None:
</span></span><span class="line"><span class="cl">        raise ValueError(&#34;Authentication required&#34;)
</span></span><span class="line"><span class="cl">    print(f&#34;Thread: {runtime.execution_info.thread_id}&#34;)
</span></span><span class="line"><span class="cl">    return None
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Streaming</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/streaming/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/streaming/</guid>
      <description>Streaming相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="overview">Overview</h1>
<p>实现的功能:</p>
<ul>
<li>Stream agent progress</li>
<li>Stream LLM tokens</li>
<li>Streaming thinking/reasoning tokens</li>
<li>Stream custom updates</li>
<li>Stream multiple modes:从updates,messages,custom中进行选择</li>
</ul>
<h1 id="supported-stream-modes">Supported stream modes</h1>
<p>将下列的一个或者多个流式传输模式作为列表传输给stream或者astream方法</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Mode</th>
					<th style="text-align: left">Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>updates</code></td>
					<td style="text-align: left">Streams state updates after each agent step. If multiple updates are made in the same step (e.g., multiple nodes are run), those updates are streamed separately.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>messages</code></td>
					<td style="text-align: left">Streams tuples of <code>(token, metadata)</code> from any graph nodes where an LLM is invoked.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>custom</code></td>
					<td style="text-align: left">Streams custom data from inside your graph nodes using the stream writer.</td>
			</tr>
	</tbody>
</table>
<h1 id="agent-progress">Agent progress</h1>
<p>使用带有stream_mode =&ldquo;updates&quot;的stream或者astream方法
将在每个agent step之后发送一个事件</p>
<p>例子:
在一次tool call之后,你应该看到下列更新</p>
<ul>
<li><strong>LLM node</strong>: <a href="https://reference.langchain.com/python/langchain-core/messages/ai/AIMessage"><code>AIMessage</code></a> with tool call requests</li>
<li><strong>Tool node</strong>: <a href="https://reference.langchain.com/python/langchain-core/messages/tool/ToolMessage"><code>ToolMessage</code></a> with execution result</li>
<li><strong>LLM node</strong>: Final AI response</li>
</ul>
<p>通过<code>config</code>传递thread_id以便checkpointer进行保存和后续的resume
也可以传递context,后续可以使用runtime.context进行访问</p>
<h1 id="llm-tokens">LLM tokens</h1>
<p>使用<code>stream_mode = &quot;message&quot;</code>来访问token usage
在输出中可以看到agent的工具调用和最终的response</p>
<h1 id="custom-updates">Custom updates</h1>
<p>想要流式传输工具执行的过程(或结果),使用``get_stream_writer `inside the tool</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></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">langgraph</span><span class="o">.</span><span class="n">config</span> <span class="n">import</span> <span class="n">get_stream_writer</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">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a given city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span> <span class="o">=</span> <span class="n">get_stream_writer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># stream any arbitrary data</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Looking up data for city: {city}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Acquired data for city: {city}&#34;</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;It&#39;s always sunny in {city}!&#34;</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">tools</span><span class="o">=</span><span class="p">[</span><span class="n">get_weather</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="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What is the weather in SF?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">stream_mode</span><span class="o">=</span><span class="s2">&#34;custom&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v2&#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="k">if</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;custom&#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="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="stream-multiple-modes">Stream multiple modes</h1>
<p>通过将它们以mode列表的形式传输:stream_mode=[&ldquo;updates&rdquo;,&ldquo;custom&rdquo;]</p>
<p>每一段streamed chunk都是<code>StreamPart</code>的dict with type,ns and data 的keys
通过chunk[&ldquo;type&rdquo;]来决定流式模式,通过chunk[&ldquo;data&rdquo;]来访问负载</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"></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><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></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">langgraph</span><span class="o">.</span><span class="n">config</span> <span class="n">import</span> <span class="n">get_stream_writer</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">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a given city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span> <span class="o">=</span> <span class="n">get_stream_writer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Looking up data for city: {city}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Acquired data for city: {city}&#34;</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;It&#39;s always sunny in {city}!&#34;</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-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="n">get_weather</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="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What is the weather in SF?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">stream_mode</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;updates&#34;</span><span class="p">,</span> <span class="s2">&#34;custom&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v2&#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="nb">print</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;stream_mode: {chunk[&#39;type&#39;]}&#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="n">f</span><span class="s2">&#34;content: {chunk[&#39;data&#39;]}&#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="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="common-patterns">Common patterns</h1>
<p>常见的几种应用场景</p>
<h2 id="stream-thinkingreasoning-tokens">Stream thinking/reasoning tokens</h2>
<p>一些模型会在产生最终的answer之前在内部进行思考
可以通过过滤standard contnent blocks中的type = &ldquo;reasoning&quot;的部分来这些thinging/reasoning tokens</p>
<p>需要使用的mode为message即,stream_mode =&ldquo;message&rdquo;(与上文相符合)</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">stream = agent.stream_events(
</span></span><span class="line"><span class="cl">    {&#34;messages&#34;: [{&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: &#34;What is the weather in SF?&#34;}]},
</span></span><span class="line"><span class="cl">    version=&#34;v3&#34;,
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">for message in stream.messages:
</span></span><span class="line"><span class="cl">    for token in message.reasoning:
</span></span><span class="line"><span class="cl">        print(f&#34;[thinking] {token}&#34;, end=&#34;&#34;)
</span></span><span class="line"><span class="cl">    for token in message.text:
</span></span><span class="line"><span class="cl">        print(token, end=&#34;&#34;, flush=True)
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="streaming-tool-calls">Streaming tool calls</h2>
<p>if you want to stream both:</p>
<ol>
<li>Partial JSON as <a href="https://docs.langchain.com/oss/python/langchain/models#tool-calling">tool calls</a> are generated</li>
<li>The completed, parsed tool calls that are executed</li>
</ol>
<p>前者可以通过指定stream_mode= &ldquo;message&rdquo;
后者,根据是否在state中被tracked:
True: 指定stream_mode = [&ldquo;messages&rdquo;,&ldquo;updates&rdquo;],并通过state updates来访问(如下所示)
False: 使用custom updates 或者 aggregate the chunks during the streaming loop (<a href="https://docs.langchain.com/oss/python/langchain/streaming#accessing-completed-messages">next section</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></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="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">input_message</span><span class="p">]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">stream_mode</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="s2">&#34;updates&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v2&#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="k">if</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;messages&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span><span class="p">,</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</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">token</span><span class="p">,</span> <span class="n">AIMessageChunk</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">_render_message_chunk</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;updates&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">source</span><span class="p">,</span> <span class="n">update</span> <span class="ow">in</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">source</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">&#34;model&#34;</span><span class="p">,</span> <span class="s2">&#34;tools&#34;</span><span class="p">):</span>  <span class="c1"># `source` captures node name</span>
</span></span><span class="line"><span class="cl">                <span class="n">_render_completed_message</span><span class="p">(</span><span class="n">update</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="accessing-completed-messages">Accessing completed messages</h3>
<p>在message不通过state updates反映时采取的途径之一
通过aggrrgate message chunks in the streaming loop</p>
<p>1.使用stream_writer inside the tool
2.如果你不能添加自定义的时间到stream中时,你可以流式传输中聚合消息块</p>
<h2 id="streaming-with-human-in-the-loop">Streaming with human-in-the-loop</h2>
<p>解决human-in-the-loop interrupts:</p>
<ol>
<li>为agent配置hunman-in-the-loop middleare 和 checkpointer</li>
<li>搜集在&quot;update&quot;流式传输模式下的interrupts</li>
<li>通过command来对这些interrupts进行响应</li>
</ol>
<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><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</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">typing</span> <span class="n">import</span> <span class="n">Any</span>
</span></span><span class="line"><span class="cl">
</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">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">HumanInTheLoopMiddleware</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">messages</span> <span class="n">import</span> <span class="n">AIMessage</span><span class="p">,</span> <span class="n">AIMessageChunk</span><span class="p">,</span> <span class="n">AnyMessage</span><span class="p">,</span> <span class="n">ToolMessage</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">checkpoint</span><span class="o">.</span><span class="n">memory</span> <span class="n">import</span> <span class="n">InMemorySaver</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">types</span> <span class="n">import</span> <span class="n">Command</span><span class="p">,</span> <span class="n">Interrupt</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">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a given city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;It&#39;s always sunny in {city}!&#34;</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">checkpointer</span> <span class="o">=</span> <span class="n">InMemorySaver</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="s2">&#34;openai: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">get_weather</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">HumanInTheLoopMiddleware</span><span class="p">(</span><span class="n">interrupt_on</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;get_weather&#34;</span><span class="p">:</span> <span class="n">True</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">checkpointer</span><span class="o">=</span><span class="n">checkpointer</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></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_render_message_chunk</span><span class="p">(</span><span class="n">token</span><span class="p">:</span> <span class="n">AIMessageChunk</span><span class="p">)</span> <span class="o">-&gt;</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">token</span><span class="o">.</span><span class="n">text</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">token</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;|&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">tool_call_chunks</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">token</span><span class="o">.</span><span class="n">tool_call_chunks</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="k">def</span> <span class="nf">_render_completed_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="n">AnyMessage</span><span class="p">)</span> <span class="o">-&gt;</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">message</span><span class="p">,</span> <span class="n">AIMessage</span><span class="p">)</span> <span class="ow">and</span> <span class="n">message</span><span class="o">.</span><span class="n">tool_calls</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;Tool calls: {message.tool_calls}&#34;</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">message</span><span class="p">,</span> <span class="n">ToolMessage</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;Tool response: {message.content_blocks}&#34;</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="k">def</span> <span class="nf">_render_interrupt</span><span class="p">(</span><span class="n">interrupt</span><span class="p">:</span> <span class="n">Interrupt</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">interrupts</span> <span class="o">=</span> <span class="n">interrupt</span><span class="o">.</span><span class="n">value</span>  
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">request</span> <span class="ow">in</span> <span class="n">interrupts</span><span class="p">[</span><span class="s2">&#34;action_requests&#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="n">request</span><span class="p">[</span><span class="s2">&#34;description&#34;</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">input_message</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Can you look up the weather in Boston and San Francisco?&#34;</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="n">config</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="s2">&#34;some_id&#34;</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"><span class="n">interrupts</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">input_message</span><span class="p">]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">=</span><span class="n">config</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">stream_mode</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="s2">&#34;updates&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v2&#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="k">if</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;messages&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span><span class="p">,</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</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">token</span><span class="p">,</span> <span class="n">AIMessageChunk</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">_render_message_chunk</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;updates&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">source</span><span class="p">,</span> <span class="n">update</span> <span class="ow">in</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">source</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">&#34;model&#34;</span><span class="p">,</span> <span class="s2">&#34;tools&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="n">_render_completed_message</span><span class="p">(</span><span class="n">update</span><span class="p">[</span><span class="s2">&#34;messages&#34;</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 class="k">if</span> <span class="n">source</span> <span class="o">==</span> <span class="s2">&#34;__interrupt__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">interrupts</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">update</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">_render_interrupt</span><span class="p">(</span><span class="n">update</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>接下来,为每一个interrupt收集对应的decision,并且decision的顺序与我们收集的action对应</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-fallback" data-lang="fallback"><span class="line"><span class="cl">def _get_interrupt_decisions(interrupt: Interrupt) -&gt; list[dict]:
</span></span><span class="line"><span class="cl">    return [
</span></span><span class="line"><span class="cl">        {
</span></span><span class="line"><span class="cl">            &#34;type&#34;: &#34;edit&#34;,
</span></span><span class="line"><span class="cl">            &#34;edited_action&#34;: {
</span></span><span class="line"><span class="cl">                &#34;name&#34;: &#34;get_weather&#34;,
</span></span><span class="line"><span class="cl">                &#34;args&#34;: {&#34;city&#34;: &#34;Boston, U.K.&#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">        if &#34;boston&#34; in request[&#34;description&#34;].lower()
</span></span><span class="line"><span class="cl">        else {&#34;type&#34;: &#34;approve&#34;}
</span></span><span class="line"><span class="cl">        for request in interrupt.value[&#34;action_requests&#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">decisions = {}
</span></span><span class="line"><span class="cl">for interrupt in interrupts:
</span></span><span class="line"><span class="cl">    decisions[interrupt.id] = {
</span></span><span class="line"><span class="cl">        &#34;decisions&#34;: _get_interrupt_decisions(interrupt)
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">decisions
</span></span></code></pre></td></tr></table>
</div>
</div><p>我们可以通过向流式传输的循环传入一个command来resume</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></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">interrupts</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">Command</span><span class="p">(</span><span class="n">resume</span><span class="o">=</span><span class="n">decisions</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">=</span><span class="n">config</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">stream_mode</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="s2">&#34;updates&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v2&#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="c1"># Streaming loop is unchanged</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;messages&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span><span class="p">,</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</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">token</span><span class="p">,</span> <span class="n">AIMessageChunk</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">_render_message_chunk</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;updates&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">source</span><span class="p">,</span> <span class="n">update</span> <span class="ow">in</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">source</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">&#34;model&#34;</span><span class="p">,</span> <span class="s2">&#34;tools&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="n">_render_completed_message</span><span class="p">(</span><span class="n">update</span><span class="p">[</span><span class="s2">&#34;messages&#34;</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 class="k">if</span> <span class="n">source</span> <span class="o">==</span> <span class="s2">&#34;__interrupt__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">interrupts</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">update</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">_render_interrupt</span><span class="p">(</span><span class="n">update</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="streaming-from-sub-agents">Streaming from sub-agents</h2>
<p>当agent中有多个LLMs时,需要对其来源进行消歧
在创建agent时使用<code>name</code>参数,该参数在&quot;message&quot;的stream_mode中可以通过lc_agent_name键中的metadata访问
ps: 在创建stream时specify <code>subgraph是= True</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><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">typing</span> <span class="n">import</span> <span class="n">Any</span>
</span></span><span class="line"><span class="cl">
</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">langchain</span><span class="o">.</span><span class="n">chat_models</span> <span class="n">import</span> <span class="n">init_chat_model</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">messages</span> <span class="n">import</span> <span class="n">AIMessage</span><span class="p">,</span> <span class="n">AnyMessage</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">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a given city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;It&#39;s always sunny in {city}!&#34;</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">weather_model</span> <span class="o">=</span> <span class="n">init_chat_model</span><span class="p">(</span><span class="s2">&#34;openai:gpt-5.5&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">weather_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="n">weather_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="n">get_weather</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="o">=</span><span class="s2">&#34;weather_agent&#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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">call_weather_agent</span><span class="p">(</span><span class="n">query</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Query the weather agent.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">weather_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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="n">query</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="k">return</span> <span class="n">result</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">text</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">supervisor_model</span> <span class="o">=</span> <span class="n">init_chat_model</span><span class="p">(</span><span class="s2">&#34;openai:gpt-5.5&#34;</span><span class="p">)</span>
</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="n">supervisor_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="n">call_weather_agent</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="o">=</span><span class="s2">&#34;supervisor&#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><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></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="k">def</span> <span class="nf">_render_message_chunk</span><span class="p">(</span><span class="n">token</span><span class="p">:</span> <span class="n">AIMessageChunk</span><span class="p">)</span> <span class="o">-&gt;</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">token</span><span class="o">.</span><span class="n">text</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">token</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&#34;|&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">tool_call_chunks</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">token</span><span class="o">.</span><span class="n">tool_call_chunks</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="k">def</span> <span class="nf">_render_completed_message</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="n">AnyMessage</span><span class="p">)</span> <span class="o">-&gt;</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">message</span><span class="p">,</span> <span class="n">AIMessage</span><span class="p">)</span> <span class="ow">and</span> <span class="n">message</span><span class="o">.</span><span class="n">tool_calls</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;Tool calls: {message.tool_calls}&#34;</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">message</span><span class="p">,</span> <span class="n">ToolMessage</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;Tool response: {message.content_blocks}&#34;</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">input_message</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What is the weather in Boston?&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">current_agent</span> <span class="o">=</span> <span class="n">None</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">agent</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">input_message</span><span class="p">]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">stream_mode</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">,</span> <span class="s2">&#34;updates&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">subgraphs</span><span class="o">=</span><span class="n">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">version</span><span class="o">=</span><span class="s2">&#34;v2&#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="k">if</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;messages&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span><span class="p">,</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">agent_name</span> <span class="p">:</span><span class="o">=</span> <span class="n">metadata</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;lc_agent_name&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">agent_name</span> <span class="o">!=</span> <span class="n">current_agent</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;🤖 {agent_name}: &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">current_agent</span> <span class="o">=</span> <span class="n">agent_name</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">token</span><span class="p">,</span> <span class="n">AIMessageChunk</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">_render_message_chunk</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;type&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;updates&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">source</span><span class="p">,</span> <span class="n">update</span> <span class="ow">in</span> <span class="n">chunk</span><span class="p">[</span><span class="s2">&#34;data&#34;</span><span class="p">]</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">source</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">&#34;model&#34;</span><span class="p">,</span> <span class="s2">&#34;tools&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="n">_render_completed_message</span><span class="p">(</span><span class="n">update</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">][</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="disable-streaming">Disable streaming</h1>
<p>初始化时: set <code>streaming = False</code></p>
<h1 id="v2-streaming-format">v2 streaming format</h1>
<p>Pass <code>version=&quot;v2&quot;</code> to <code>stream()</code> or <code>astream()</code> to get a unified output format. Every chunk is a <code>StreamPart</code> dict with <code>type</code>, <code>ns</code>, and <code>data</code> keys — the same shape regardless of stream mode or number of modes:</p>
<p>The v2 format also improves <code>invoke()</code> — it returns a <code>GraphOutput</code> object with <code>.value</code> and <code>.interrupts</code> attributes, cleanly separating state from interrupt metadata:</p>
]]></content:encoded>
    </item>
    <item>
      <title>Structured Output</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/structured-output/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/structured-output/</guid>
      <description>Structured Output相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>返回的形式:
JSON objects, Pydantic models,or dataclasses that your application can use directly</p>
<p>LangChain&rsquo;s <code>create_agent</code> 会自动处理结构化输出
用户设置desired 结构化输出的schema
返回在 in the key <code>strucured_responese</code> key of the agent&rsquo;s state</p>
<p>Field函数的作用:
结构化输出时,是创建元数据的重要途径,且其内部的description可以作为Prompt的一部分指导LLM的工作</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">def create_agent(
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">    response_format: Union[
</span></span><span class="line"><span class="cl">        ToolStrategy[StructuredResponseT],
</span></span><span class="line"><span class="cl">        ProviderStrategy[StructuredResponseT],
</span></span><span class="line"><span class="cl">        type[StructuredResponseT],
</span></span><span class="line"><span class="cl">        None,
</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="response-format">Response format</h1>
<p>使用<code>response_format</code>来控制agent返回结构化数据的方式</p>
<ul>
<li><strong><code>ToolStrategy[StructuredResponseT]</code></strong>: Uses tool calling for structured output</li>
<li><strong><code>ProviderStrategy[StructuredResponseT]</code></strong>: Uses provider-native structured output</li>
<li><strong><code>type[StructuredResponseT]</code></strong>: Schema type - automatically selects best strategy based on model capabilities</li>
<li><strong><code>None</code></strong>: Structured output not explicitly requested</li>
</ul>
<p>When a schema type is provided directly, LangChain automatically chooses:</p>
<ul>
<li><code>ProviderStrategy</code> if the model and provider chosen supports native structured output (e.g. <a href="https://docs.langchain.com/oss/python/integrations/providers/openai">OpenAI</a>, <a href="https://docs.langchain.com/oss/python/integrations/providers/anthropic">Anthropic (Claude)</a>, or <a href="https://docs.langchain.com/oss/python/integrations/providers/xai">xAI (Grok)</a>).</li>
<li><code>ToolStrategy</code> for all other models.</li>
</ul>
<h1 id="provider-strategy">Provider strategy</h1>
<p>一些provider支持原生的结构化输出
这是最可靠的方式
要使用这种方式,请配置 <code>Providerstrategy</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">class ProviderStrategy(Generic[SchemaT]):
</span></span><span class="line"><span class="cl">    schema: type[SchemaT]
</span></span><span class="line"><span class="cl">    strict: bool | None = None
</span></span></code></pre></td></tr></table>
</div>
</div><p>参数
schema(required):
规定了结构化输出的format,取值有:</p>
<ul>
<li><strong>Pydantic models</strong>: <code>BaseModel</code> subclasses with field validation. Returns validated Pydantic instance.</li>
<li><strong>Dataclasses</strong>: Python dataclasses with type annotations. Returns dict.</li>
<li><strong>TypedDict</strong>: Typed dictionary classes. Returns dict.</li>
<li><strong>JSON Schema</strong>: Dictionary with JSON schema specification. Must include top-level <code>title</code> and <code>description</code> keys. Returns dict.</li>
</ul>
<p>strict:
可选;默认None;控制是否严格遵守schema</p>
<p>当你直接把schema type传给<a href="https://reference.langchain.com/python/langchain/agents/factory/create_agent"><code>create_agent.response_format</code></a> 时且模型支持原生的结构化输出时,LangChain会自动选择ProviderStrategy</p>
<h1 id="tool-calling-strategy">Tool calling strategy</h1>
<p>对于模型不支持原生的结构化输出,LangChain使用tool calling,这对于所有的支持tool calling的模型都适用
使用:配置 <code>ToolStrategy</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></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="k">class</span> <span class="nc">ToolStrategy</span><span class="p">(</span><span class="n">Generic</span><span class="p">[</span><span class="n">SchemaT</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">    <span class="n">schema</span><span class="p">:</span> <span class="nb">type</span><span class="p">[</span><span class="n">SchemaT</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">tool_message_content</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    <span class="n">handle_errors</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="nb">bool</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nb">type</span><span class="p">[</span><span class="ne">Exception</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nb">tuple</span><span class="p">[</span><span class="nb">type</span><span class="p">[</span><span class="ne">Exception</span><span class="p">],</span> <span class="o">...</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="n">Callable</span><span class="p">[[</span><span class="ne">Exception</span><span class="p">],</span> <span class="nb">str</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>schema: required
同上</p>
<p>tool_message_content:
生成结构化输出时的tool message的自定义内容
如果没有提供,为显示结构化响应的响应数据</p>
<p>handle_errors:
默认为True
当结构化输出的验证失败的时候的错误处理策略,参数如下</p>
<ul>
<li><strong><code>True</code></strong>: Catch all errors with default error template</li>
<li><strong><code>str</code></strong>: Catch all errors with this custom message</li>
<li><strong><code>type[Exception]</code></strong>: Only catch this exception type with default message</li>
<li><strong><code>tuple[type[Exception], ...]</code></strong>: Only catch these exception types with default message</li>
<li><strong><code>Callable[[Exception], str]</code></strong>: Custom function that returns error message</li>
<li><strong><code>False</code></strong>: No retry, let exceptions propagate</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></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">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</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">Literal</span><span class="p">,</span> <span class="n">Union</span>
</span></span><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.structured_output</span> <span class="kn">import</span> <span class="n">ToolStrategy</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">ProductReview</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Analysis of a product review.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">rating</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The rating of the product&#34;</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">le</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sentiment</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;positive&#34;</span><span class="p">,</span> <span class="s2">&#34;negative&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The sentiment of the review&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">key_points</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The key points of the review. Lowercase, 1-3 words each.&#34;</span><span class="p">)</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">CustomerComplaint</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;A customer complaint about a product or service.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">issue_type</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;product&#34;</span><span class="p">,</span> <span class="s2">&#34;service&#34;</span><span class="p">,</span> <span class="s2">&#34;shipping&#34;</span><span class="p">,</span> <span class="s2">&#34;billing&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The type of issue&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">severity</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;low&#34;</span><span class="p">,</span> <span class="s2">&#34;medium&#34;</span><span class="p">,</span> <span class="s2">&#34;high&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The severity of the complaint&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">description</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Brief description of the complaint&#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">tools</span><span class="o">=</span><span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="n">response_format</span><span class="o">=</span><span class="n">ToolStrategy</span><span class="p">(</span><span class="n">Union</span><span class="p">[</span><span class="n">ProductReview</span><span class="p">,</span> <span class="n">CustomerComplaint</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Analyze this review: &#39;Great product: 5 out of 5 stars. Fast shipping, but expensive&#39;&#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="n">result</span><span class="p">[</span><span class="s2">&#34;structured_response&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ProductReview(rating=5, sentiment=&#39;positive&#39;, key_points=[&#39;fast shipping&#39;, &#39;expensive&#39;])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="custom-tool-message-content">Custom tool message content</h2>
<p>the <code>tool_message_content</code> 参数允许你自定义结构化输出产生时呈现在会话历史中的message</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></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">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</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">Literal</span><span class="p">,</span> <span class="n">Union</span>
</span></span><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.structured_output</span> <span class="kn">import</span> <span class="n">ToolStrategy</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">ProductReview</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Analysis of a product review.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">rating</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The rating of the product&#34;</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">le</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sentiment</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;positive&#34;</span><span class="p">,</span> <span class="s2">&#34;negative&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The sentiment of the review&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">key_points</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The key points of the review. Lowercase, 1-3 words each.&#34;</span><span class="p">)</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">CustomerComplaint</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;A customer complaint about a product or service.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">issue_type</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;product&#34;</span><span class="p">,</span> <span class="s2">&#34;service&#34;</span><span class="p">,</span> <span class="s2">&#34;shipping&#34;</span><span class="p">,</span> <span class="s2">&#34;billing&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The type of issue&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">severity</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;low&#34;</span><span class="p">,</span> <span class="s2">&#34;medium&#34;</span><span class="p">,</span> <span class="s2">&#34;high&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;The severity of the complaint&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">description</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Brief description of the complaint&#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">tools</span><span class="o">=</span><span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="n">response_format</span><span class="o">=</span><span class="n">ToolStrategy</span><span class="p">(</span><span class="n">Union</span><span class="p">[</span><span class="n">ProductReview</span><span class="p">,</span> <span class="n">CustomerComplaint</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Analyze this review: &#39;Great product: 5 out of 5 stars. Fast shipping, but expensive&#39;&#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="n">result</span><span class="p">[</span><span class="s2">&#34;structured_response&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ProductReview(rating=5, sentiment=&#39;positive&#39;, key_points=[&#39;fast shipping&#39;, &#39;expensive&#39;])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="error-handing">Error handing</h2>
<p>LangChain提供了智能重试机制</p>
<h3 id="multiple-structured-outputs-error">Multiple structured outputs error</h3>
<p>当模型错误地调用了多个结构化工具输出,会在<code>ToolMessage</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">pydantic</span> <span class="n">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</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">Union</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">langchain</span><span class="o">.</span><span class="n">agents</span><span class="o">.</span><span class="n">structured_output</span> <span class="n">import</span> <span class="n">ToolStrategy</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">ContactInfo</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Person&#39;s name&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Email address&#34;</span><span class="p">)</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">EventDetails</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">event_name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Name of the event&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">date</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Event date&#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">tools</span><span class="o">=</span><span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="n">response_format</span><span class="o">=</span><span class="n">ToolStrategy</span><span class="p">(</span><span class="n">Union</span><span class="p">[</span><span class="n">ContactInfo</span><span class="p">,</span> <span class="n">EventDetails</span><span class="p">])</span>  <span class="c1"># Default: handle_errors=True</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th&#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="schema-validation-error">Schema validation error</h3>
<p>当结构化输出和schema不匹配时,agent会返回特定的错误反馈</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></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">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</span>
</span></span><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.structured_output</span> <span class="kn">import</span> <span class="n">ToolStrategy</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">ProductRating</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">rating</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Rating from 1-5&#34;</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">le</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">comment</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Review comment&#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">tools</span><span class="o">=</span><span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="n">response_format</span><span class="o">=</span><span class="n">ToolStrategy</span><span class="p">(</span><span class="n">ProductRating</span><span class="p">),</span>  <span class="c1"># Default: handle_errors=True</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span><span class="o">=</span><span class="s2">&#34;You are a helpful assistant that parses product reviews. Do not make any field or value up.&#34;</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Parse this: Amazing product, 10/10!&#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><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-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">================================</span> <span class="n">Human</span> <span class="n">Message</span> <span class="o">=================================</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Parse</span> <span class="n">this</span><span class="p">:</span> <span class="n">Amazing</span> <span class="n">product</span><span class="p">,</span> <span class="mi">10</span><span class="o">/</span><span class="mi">10</span><span class="o">!</span>
</span></span><span class="line"><span class="cl"><span class="o">==================================</span> <span class="n">Ai</span> <span class="n">Message</span> <span class="o">==================================</span>
</span></span><span class="line"><span class="cl"><span class="n">Tool</span> <span class="n">Calls</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">ProductRating</span> <span class="p">(</span><span class="n">call_1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">Call</span> <span class="n">ID</span><span class="p">:</span> <span class="n">call_1</span>
</span></span><span class="line"><span class="cl">  <span class="n">Args</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">rating</span><span class="p">:</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">    <span class="n">comment</span><span class="p">:</span> <span class="n">Amazing</span> <span class="n">product</span>
</span></span><span class="line"><span class="cl"><span class="o">=================================</span> <span class="n">Tool</span> <span class="n">Message</span> <span class="o">=================================</span>
</span></span><span class="line"><span class="cl"><span class="n">Name</span><span class="p">:</span> <span class="n">ProductRating</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Error</span><span class="p">:</span> <span class="n">Failed</span> <span class="n">to</span> <span class="n">parse</span> <span class="n">structured</span> <span class="n">output</span> <span class="k">for</span> <span class="k">tool</span> <span class="s1">&#39;ProductRating&#39;</span><span class="p">:</span> <span class="mi">1</span> <span class="n">validation</span> <span class="n">error</span> <span class="k">for</span> <span class="n">ProductRating</span><span class="o">.</span><span class="n">rating</span>
</span></span><span class="line"><span class="cl">  <span class="ne">Input</span> <span class="n">should</span> <span class="n">be</span> <span class="n">less</span> <span class="n">than</span> <span class="ow">or</span> <span class="n">equal</span> <span class="n">to</span> <span class="mi">5</span> <span class="p">[</span><span class="n">type</span><span class="o">=</span><span class="n">less_than_equal</span><span class="p">,</span> <span class="n">input_value</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">input_type</span><span class="o">=</span><span class="ne">int</span><span class="p">]</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="n">Please</span> <span class="n">fix</span> <span class="n">your</span> <span class="n">mistakes</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">==================================</span> <span class="n">Ai</span> <span class="n">Message</span> <span class="o">==================================</span>
</span></span><span class="line"><span class="cl"><span class="n">Tool</span> <span class="n">Calls</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">ProductRating</span> <span class="p">(</span><span class="n">call_2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">Call</span> <span class="n">ID</span><span class="p">:</span> <span class="n">call_2</span>
</span></span><span class="line"><span class="cl">  <span class="n">Args</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">rating</span><span class="p">:</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">    <span class="n">comment</span><span class="p">:</span> <span class="n">Amazing</span> <span class="n">product</span>
</span></span><span class="line"><span class="cl"><span class="o">=================================</span> <span class="n">Tool</span> <span class="n">Message</span> <span class="o">=================================</span>
</span></span><span class="line"><span class="cl"><span class="n">Name</span><span class="p">:</span> <span class="n">ProductRating</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Returning</span> <span class="n">structured</span> <span class="n">response</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;rating&#39;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;comment&#39;</span><span class="p">:</span> <span class="s1">&#39;Amazing product&#39;</span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="error-handling-strategies">Error handling strategies</h3>
<p>你可以通过handle_errors参数自定义错误处理的方式</p>
<p><strong>Custom error message:</strong></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></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="n">ToolStrategy</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">schema</span><span class="o">=</span><span class="n">ProductRating</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">handle_errors</span><span class="o">=</span><span class="s2">&#34;Please provide a valid rating between 1-5 and include a comment.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>If <code>handle_errors</code> is a string, the agent will <em>always</em> prompt the model to re-try with a fixed tool message:</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></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="o">=================================</span> <span class="n">Tool</span> <span class="n">Message</span> <span class="o">=================================</span>
</span></span><span class="line"><span class="cl"><span class="n">Name</span><span class="p">:</span> <span class="n">ProductRating</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Please</span> <span class="n">provide</span> <span class="n">a</span> <span class="n">valid</span> <span class="n">rating</span> <span class="n">between</span> <span class="mi">1</span><span class="o">-</span><span class="mi">5</span> <span class="ow">and</span> <span class="n">include</span> <span class="n">a</span> <span class="n">comment</span><span class="o">.</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>Handle specific exceptions only:</strong></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></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="n">ToolStrategy</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">schema</span><span class="o">=</span><span class="n">ProductRating</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">handle_errors</span><span class="o">=</span><span class="ne">ValueError</span>  <span class="c1"># Only retry on ValueError, raise others</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>If <code>handle_errors</code> is an exception type, the agent will only retry (using the default error message) if the exception raised is the specified type. In all other cases, the exception will be raised.<strong>Handle multiple exception types:</strong></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></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="n">ToolStrategy</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">schema</span><span class="o">=</span><span class="n">ProductRating</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">handle_errors</span><span class="o">=</span><span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">TypeError</span><span class="p">)</span>  <span class="c1"># Retry on ValueError and TypeError</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>If <code>handle_errors</code> is a tuple of exceptions, the agent will only retry (using the default error message) if the exception raised is one of the specified types. In all other cases, the exception will be raised.<strong>Custom error handler function:</strong></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></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.structured_output</span> <span class="kn">import</span> <span class="n">StructuredOutputValidationError</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain.agents.structured_output</span> <span class="kn">import</span> <span class="n">MultipleStructuredOutputsError</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">custom_error_handler</span><span class="p">(</span><span class="n">error</span><span class="p">:</span> <span class="ne">Exception</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">error</span><span class="p">,</span> <span class="n">StructuredOutputValidationError</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;There was an issue with the format. Try again.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">error</span><span class="p">,</span> <span class="n">MultipleStructuredOutputsError</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;Multiple structured outputs were returned. Pick the most relevant one.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">error</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</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></span><span class="line"><span class="cl">    <span class="n">response_format</span><span class="o">=</span><span class="n">ToolStrategy</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                        <span class="n">schema</span><span class="o">=</span><span class="n">Union</span><span class="p">[</span><span class="n">ContactInfo</span><span class="p">,</span> <span class="n">EventDetails</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                        <span class="n">handle_errors</span><span class="o">=</span><span class="n">custom_error_handler</span>
</span></span><span class="line"><span class="cl">                    <span class="p">)</span>  <span class="c1"># Default: handle_errors=True</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th&#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></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">result</span><span class="p">[</span><span class="s1">&#39;messages&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># If message is actually a ToolMessage object (not a dict), check its class name</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;ToolMessage&#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="n">msg</span><span class="o">.</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># If message is a dictionary or you want a fallback</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="nb">dict</span><span class="p">)</span> <span class="ow">and</span> <span class="n">msg</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;tool_call_id&#39;</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">msg</span><span class="p">[</span><span class="s1">&#39;content&#39;</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>On <code>StructuredOutputValidationError</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">================================= Tool Message =================================
</span></span><span class="line"><span class="cl">Name: ToolStrategy
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">There was an issue with the format. Try again.
</span></span></code></pre></td></tr></table>
</div>
</div><p>On <code>MultipleStructuredOutputsError</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">================================= Tool Message =================================
</span></span><span class="line"><span class="cl">Name: ToolStrategy
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Multiple structured outputs were returned. Pick the most relevant one.
</span></span></code></pre></td></tr></table>
</div>
</div><p>On other errors:</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">================================= Tool Message =================================
</span></span><span class="line"><span class="cl">Name: ToolStrategy
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Error: &lt;error message&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>No error handling:</strong></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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">response_format = ToolStrategy(
</span></span><span class="line"><span class="cl">    schema=ProductRating,
</span></span><span class="line"><span class="cl">    handle_errors=False  # All errors raised
</span></span><span class="line"><span class="cl">)
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Tools</title>
      <link>https://tuanzi-wow.cn/frameworks/langchain/core_components/tools/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langchain/core_components/tools/</guid>
      <description>Tools相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="tools">Tools</h1>
<p>Extend what agents can do!</p>
<h1 id="create-tools">Create tools</h1>
<h2 id="basic-tool-definition">Basic tool definition</h2>
<p>The easiest way to create a tool is to use the <strong><code>@tool</code> decorator</strong>
In this way the <strong>docstring</strong> will become the description of the tool</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></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">tools</span> <span class="n">import</span> <span class="k">tool</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">search_database</span><span class="p">(</span><span class="n">query</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="ne">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Search the customer database for records matching the query.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">        query: Search terms to look for
</span></span></span><span class="line"><span class="cl"><span class="s2">        limit: Maximum number of results to return
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;Found {limit} results for &#39;{query}&#39;&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>docstring:&rsquo;&rsquo;'
ada
&rsquo;&rsquo;'
的部分
docstring应该内容丰富且简明扼要
参数的type hints是必须的,因为他们定义了工具的输入参数</p>
<h2 id="customize-tool-properties">Customize tool properties</h2>
<h3 id="custom-tool-name">Custom tool name</h3>
<p>by default,the tool name is the function name. But you can override it.</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></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="err">@</span><span class="k">tool</span><span class="p">(</span><span class="s2">&#34;web_search&#34;</span><span class="p">)</span>  <span class="c1"># Custom name</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="n">query</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Search the web for information.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;Results for: {query}&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">search</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>  <span class="c1"># web_search</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="custom-tool-description">Custom tool description</h3>
<p>Override the auto-generated tool description(refer to the docstring) for clearer model guidance</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></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="err">@</span><span class="k">tool</span><span class="p">(</span><span class="s2">&#34;calculator&#34;</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">&#34;Performs arithmetic calculations. Use this for any math problems.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">calc</span><span class="p">(</span><span class="n">expression</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Evaluate mathematical expressions.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">eval</span><span class="p">(</span><span class="n">expression</span><span class="p">))</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="advanced-schema-definition">Advanced schema definition</h2>
<p>Define complex inputs with Pydantic models or JSON schemas</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">pydantic</span> <span class="n">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</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">Literal</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">WeatherInput</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Input for weather queries.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">location</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;City name or coordinates&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">units</span><span class="p">:</span> <span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;celsius&#34;</span><span class="p">,</span> <span class="s2">&#34;fahrenheit&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">default</span><span class="o">=</span><span class="s2">&#34;celsius&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">description</span><span class="o">=</span><span class="s2">&#34;Temperature unit preference&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">include_forecast</span><span class="p">:</span> <span class="ne">bool</span> <span class="o">=</span> <span class="n">Field</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">False</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">description</span><span class="o">=</span><span class="s2">&#34;Include 5-day forecast&#34;</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="err">@</span><span class="k">tool</span><span class="p">(</span><span class="n">args_schema</span><span class="o">=</span><span class="n">WeatherInput</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather</span><span class="p">(</span><span class="n">location</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">units</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&#34;celsius&#34;</span><span class="p">,</span> <span class="n">include_forecast</span><span class="p">:</span> <span class="ne">bool</span> <span class="o">=</span> <span class="n">False</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get current weather and optional forecast.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">temp</span> <span class="o">=</span> <span class="mi">22</span> <span class="k">if</span> <span class="n">units</span> <span class="o">==</span> <span class="s2">&#34;celsius&#34;</span> <span class="k">else</span> <span class="mi">72</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">f</span><span class="s2">&#34;Current weather in {location}: {temp} degrees {units[0].upper()}&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">include_forecast</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="o">+=</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">Next 5 days: Sunny&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="reserved-argument-names">Reserved argument names</h2>
<p>Those can not be used as parameters&rsquo; name which will cause runtime errors
config: reserved for  RunnableConfig
runtime: reserved for ToolRuntime</p>
<h1 id="access-context">Access context</h1>
<p>How to access and update this information(conversation history,user data,persistent memory)</p>
<p>How!
By the <code>ToolRuntime</code> parameter</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Component</th>
					<th style="text-align: left">Description</th>
					<th style="text-align: left">Use case</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>State</strong></td>
					<td style="text-align: left">Short-term memory - mutable data that exists for the current conversation (messages, counters, custom fields)</td>
					<td style="text-align: left">Access conversation history, track tool call counts</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Context</strong></td>
					<td style="text-align: left">Immutable configuration passed at invocation time (user IDs, session info)</td>
					<td style="text-align: left">Personalize responses based on user identity</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Store</strong></td>
					<td style="text-align: left">Long-term memory - persistent data that survives across conversations</td>
					<td style="text-align: left">Save user preferences, maintain knowledge base</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Stream Writer</strong></td>
					<td style="text-align: left">Emit real-time updates during tool execution</td>
					<td style="text-align: left">Show progress for long-running operations</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Execution Info</strong></td>
					<td style="text-align: left">Identity and retry information for the current execution (thread ID, run ID, attempt number)</td>
					<td style="text-align: left">Access thread/run IDs, adjust behavior based on retry state</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Server Info</strong></td>
					<td style="text-align: left">Server-specific metadata when running on LangGraph Server (assistant ID, graph ID, authenticated user)</td>
					<td style="text-align: left">Access assistant ID, graph ID, or authenticated user info</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Config</strong></td>
					<td style="text-align: left"><a href="https://reference.langchain.com/python/langchain-core/runnables/config/RunnableConfig"><code>RunnableConfig</code></a> for the execution</td>
					<td style="text-align: left">Access callbacks, tags, and metadata</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Tool Call ID</strong></td>
					<td style="text-align: left">Unique identifier for the current tool invocation</td>
					<td style="text-align: left">Correlate tool calls for logs and model invocations</td>
			</tr>
	</tbody>
</table>
<p><img alt="image-20260919214843362" loading="lazy" src="c:\\Users\\14152\\AppData\\Roaming\\Typora\\typora-user-images\\image-20260919214843362.png"></p>
<h2 id="short-term-memorystate">Short-term memory(State)</h2>
<p>Include the message history and any custom field you define in your graph state.</p>
<h3 id="access-state">Access state</h3>
<p>Use <code>runtime: ToolRuntime</code> to your signature to access state
在调用的时候,<code>ToolNode</code>会自动地注入value,但是这个参数不会出现在发送给model地schema上
你需要通过<code>runtime.state</code>才能访问到当前对话的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><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></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">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</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">messages</span> <span class="n">import</span> <span class="n">HumanMessage</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_last_user_message</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get the most recent message from the user.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">messages</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">state</span><span class="p">[</span><span class="s2">&#34;messages&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Find the last human message</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">message</span> <span class="ow">in</span> <span class="n">reversed</span><span class="p">(</span><span class="n">messages</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">message</span><span class="p">,</span> <span class="n">HumanMessage</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">message</span><span class="o">.</span><span class="n">content</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;No user messages found&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Access custom state fields</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_preference</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">pref_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get a user preference value.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">preferences</span> <span class="o">=</span> <span class="n">runtime</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;user_preferences&#34;</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">preferences</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pref_name</span><span class="p">,</span> <span class="s2">&#34;Not set&#34;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>runtime</code> parameter is hidden from the model. For the example above, the model only sees <code>pref_name</code> in the tool schema.(上面说过了,结合例子更好理解)</p>
<h3 id="update-state">Update state</h3>
<p>Use <code>Command</code> to update the agent&rsquo;s state.
Useful to update custom state field.
Include a <code>ToolMessage</code> in the update so the model can see the result of this tool call</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></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">AgentState</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">messages</span> <span class="n">import</span> <span class="n">ToolMessage</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">tools</span> <span class="n">import</span> <span class="n">ToolRuntime</span><span class="p">,</span> <span class="k">tool</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">types</span> <span class="n">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="n">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">user_name</span><span class="p">:</span> <span class="nb">str</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="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">set_user_name</span><span class="p">(</span><span class="n">new_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">[</span><span class="n">None</span><span class="p">,</span> <span class="n">CustomState</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Command</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Set the user&#39;s name in the conversation state.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">Command</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">update</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;user_name&#34;</span><span class="p">:</span> <span class="n">new_name</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></span><span class="line"><span class="cl">                <span class="n">ToolMessage</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">content</span><span class="o">=</span><span class="n">f</span><span class="s2">&#34;User name set to {new_name}.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">tool_call_id</span><span class="o">=</span><span class="n">runtime</span><span class="o">.</span><span class="n">tool_call_id</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 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>When tools update state variables, consider defining a <a href="https://docs.langchain.com/oss/python/langgraph/graph-api#reducers">reducer</a> for those fields. Since LLMs can call multiple tools in parallel, a reducer determines how to resolve conflicts when the same state field is updated by concurrent tool calls.
(创建reducer来应对并发修改同一个state值)</p>
<h2 id="context">Context</h2>
<p>Provides immutable configuration at invocation time
Including user IDs,session details,application-specific settings</p>
<p>Access context through <code>runtime.context</code>.
Pass it alongside a <code>thread_id</code> so the conversation is persisted across turns.</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><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></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">dataclasses</span> <span class="n">import</span> <span class="n">dataclass</span>
</span></span><span class="line"><span class="cl">
</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">langchain</span><span class="o">.</span><span class="n">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain_core</span><span class="o">.</span><span class="n">utils</span><span class="o">.</span><span class="n">uuid</span> <span class="n">import</span> <span class="n">uuid7</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain_openai</span> <span class="n">import</span> <span class="n">ChatOpenAI</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">USER_DATABASE</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;user123&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Alice Johnson&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;account_type&#34;</span><span class="p">:</span> <span class="s2">&#34;Premium&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;balance&#34;</span><span class="p">:</span> <span class="mi">5000</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;alice@example.com&#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="s2">&#34;user456&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Bob Smith&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;account_type&#34;</span><span class="p">:</span> <span class="s2">&#34;Standard&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;balance&#34;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;bob@example.com&#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></span><span class="line"><span class="cl"><span class="err">@</span><span class="n">dataclass</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">UserContext</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="nb">str</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="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_account_info</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">[</span><span class="n">UserContext</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get the current user&#39;s account information.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_id</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">user_id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user_id</span> <span class="ow">in</span> <span class="n">USER_DATABASE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">user</span> <span class="o">=</span> <span class="n">USER_DATABASE</span><span class="p">[</span><span class="n">user_id</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">f</span><span class="s2">&#34;Account holder: {user[&#39;name&#39;]}</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="n">f</span><span class="s2">&#34;Type: {user[&#39;account_type&#39;]}</span><span class="se">\n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="n">f</span><span class="s2">&#34;Balance: ${user[&#39;balance&#39;]}&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;User not found&#34;</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">model</span> <span class="o">=</span> <span class="n">ChatOpenAI</span><span class="p">(</span><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">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="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">get_account_info</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="n">context_schema</span><span class="o">=</span><span class="n">UserContext</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span><span class="o">=</span><span class="s2">&#34;You are a financial assistant.&#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></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="p">{</span><span class="s2">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s my current balance?&#34;</span><span class="p">}]},</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">=</span><span class="p">{</span><span class="s2">&#34;configurable&#34;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&#34;thread_id&#34;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="n">uuid7</span><span class="p">())}},</span>
</span></span><span class="line"><span class="cl">    <span class="n">context</span><span class="o">=</span><span class="n">UserContext</span><span class="p">(</span><span class="n">user_id</span><span class="o">=</span><span class="s2">&#34;user123&#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><h2 id="long-term-memorystore">Long-term memory(Store)</h2>
<p>The <a href="https://reference.langchain.com/python/langchain-core/stores/BaseStore"><code>BaseStore</code></a> provides persistent storage that survives across conversations.
Access store through <code>runtime.store</code>. The store uses a namespace/key pattern to organize data</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><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></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">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 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">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain_openai</span> <span class="n">import</span> <span class="n">ChatOpenAI</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Access memory</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_user_info</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Look up user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">store</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_info</span> <span class="o">=</span> <span class="n">store</span><span class="o">.</span><span class="n">get</span><span class="p">((</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">user_info</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="k">if</span> <span class="n">user_info</span> <span class="k">else</span> <span class="s2">&#34;Unknown user&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Update memory</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">save_user_info</span><span class="p">(</span><span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="ne">int</span><span class="p">,</span> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Save user info.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">store</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">store</span>
</span></span><span class="line"><span class="cl">    <span class="n">store</span><span class="o">.</span><span class="n">put</span><span class="p">((</span><span class="s2">&#34;users&#34;</span><span class="p">,),</span> <span class="n">user_id</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span> <span class="s2">&#34;age&#34;</span><span class="p">:</span> <span class="n">age</span><span class="p">,</span> <span class="s2">&#34;email&#34;</span><span class="p">:</span> <span class="n">email</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;Successfully saved user info.&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">model</span> <span class="o">=</span> <span class="n">ChatOpenAI</span><span class="p">(</span><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></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 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="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">get_user_info</span><span class="p">,</span> <span class="n">save_user_info</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></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"># First session: save user info</span>
</span></span><span class="line"><span class="cl"><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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Save the following user: userid: abc123, name: Foo, age: 25, email: foo@langchain.dev&#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></span><span class="line"><span class="cl"><span class="c1"># Second session: get user info</span>
</span></span><span class="line"><span class="cl"><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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Get user info for user with id &#39;abc123&#39;&#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="c1"># Here is the user info for user with ID &#34;abc123&#34;:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Name: Foo</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Age: 25</span>
</span></span><span class="line"><span class="cl"><span class="c1"># - Email: foo@langchain.dev</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="streaming-writer">Streaming writer</h2>
<p>Stream real-time updates from tools during execution.
Useful for providing progress feedback
Use <code>runtime.stream_Writer</code> to emit custom 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></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">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a given city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">stream_writer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Stream custom updates as the tool executes</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Looking up data for city: {city}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="s2">&#34;Acquired data for city: {city}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;It&#39;s always sunny in {city}!&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="execution-info">Execution info</h2>
<p>Access thread ID,run ID,and retry state from within a tool via <code>runtime.execution_info</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></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">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">log_execution_context</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Log execution identity information.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">info</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">execution_info</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;Thread: {info.thread_id}, Run: {info.run_id}&#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="n">f</span><span class="s2">&#34;Attempt: {info.node_attempt}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;done&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="server-info">Server info</h2>
<p>When your tool runs on LangGraph Server , access assistant ID,graph ID,and authenticated user via <code>runtime.server_info</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></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">tools</span> <span class="n">import</span> <span class="k">tool</span><span class="p">,</span> <span class="n">ToolRuntime</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_assistant_scoped_data</span><span class="p">(</span><span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch data scoped to the current assistant.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">=</span> <span class="n">runtime</span><span class="o">.</span><span class="n">server_info</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">server</span> <span class="n">is</span> <span class="ow">not</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;Assistant: {server.assistant_id}, Graph: {server.graph_id}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">server</span><span class="o">.</span><span class="n">user</span> <span class="n">is</span> <span class="ow">not</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;User: {server.user.identity}&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;done&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>server_info</code> is <code>None</code> when the tool is not running on LangGraph Server</p>
<h1 id="tool-execution">Tool execution</h1>
<p>Tool: used by agent
Tool error handing is configured by middleware</p>
<h2 id="tool-return-values">Tool return values</h2>
<p>a <code>string</code> for human-readable results;an <code>object</code> for structured results;a <code>Command</code> with optional message when you need to update the state</p>
<h3 id="return-a-string">Return a string</h3>
<p>when tool should provide plain text for the model to read and use in its next response</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></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">tools</span> <span class="n">import</span> <span class="k">tool</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="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get weather for a city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;It is currently sunny in {city}.&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The return value is converted to a <code>ToolMessage</code></p>
<h3 id="return-an-object">Return an object</h3>
<p>when your tool produces structured data that the model should inspect</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></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">tools</span> <span class="n">import</span> <span class="k">tool</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="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_weather_data</span><span class="p">(</span><span class="n">city</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">dict</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Get structured weather data for a city.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;city&#34;</span><span class="p">:</span> <span class="n">city</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;temperature_c&#34;</span><span class="p">:</span> <span class="mi">22</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;conditions&#34;</span><span class="p">:</span> <span class="s2">&#34;sunny&#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><ul>
<li>The object is serialized and sent back as tool output.</li>
<li>The model can read specific fields and reason over them.</li>
<li>Like string returns, this does not directly update graph state.</li>
</ul>
<h3 id="return-multimodal-content">Return multimodal content</h3>
<p>For multimodal content ,use <strong>standard content blocks</strong> so the model receives multimodal resources</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></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">tools</span> <span class="n">import</span> <span class="k">tool</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="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">capture_screenshot</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">list</span><span class="p">[</span><span class="n">dict</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Capture a screenshot of the current page.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;text&#34;</span><span class="p">,</span> <span class="s2">&#34;text&#34;</span><span class="p">:</span> <span class="s2">&#34;Screenshot of the current page:&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="s2">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;image&#34;</span><span class="p">,</span> <span class="s2">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://example.com/page.png&#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><ul>
<li>The return value is converted to a <code>ToolMessage</code> with multimodal <code>content</code>.</li>
<li>Use <code>message.content_blocks</code> to read the normalized block list after the tool runs.</li>
<li>The model must support the modalities you return. Check your <a href="https://docs.langchain.com/oss/python/integrations/chat">model’s capabilities</a> before returning images, audio, or video.</li>
</ul>
<h3 id="return-a-command">Return a Command</h3>
<p>when the tool need to update graph state(or example, setting user preferences or app state)</p>
<p>When the <code>Command</code> targets the current graph, include a <code>ToolMessage</code> in the update whose tool call ID matches the current tool call. Every tool call in the message history must have a corresponding <code>ToolMessage</code>.
返回的一个Command的Command.update中必须包含一个对应的ToolMessage.对应是指tool_call_id对应</p>
<p>Use <code>runtime.tool_call_id</code> for the <code>tool_call_id</code> parameter. <code>ToolNode</code> enforces this requirement: if the update has no <code>ToolMessage</code> matching the tool call, it raises a <code>ValueError</code>.
否则会返回一个ValueError</p>
<h3 id="return-directly-from-a-tool">Return directly from a tool</h3>
<p>设置return_direct来使得agent-loop短路,这样工具输出不会传回给模型而是直接传给caller</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></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">tools</span> <span class="n">import</span> <span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="n">langchain_openai</span> <span class="n">import</span> <span class="n">ChatOpenAI</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="k">tool</span><span class="p">(</span><span class="n">return_direct</span><span class="o">=</span><span class="n">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_order_status</span><span class="p">(</span><span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch the current status of a customer order.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># In production, query your order management system here</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">f</span><span class="s2">&#34;Order {order_id} is shipped and will arrive in 2 days.&#34;</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">ChatOpenAI</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-4o-mini&#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">fetch_order_status</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="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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What is the status of order #12345?&#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="c1"># The agent returns the tool output directly without another LLM call:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;Order 12345 is shipped and will arrive in 2 days.&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Behavior:</p>
<ul>
<li>The tool executes normally and its output is wrapped in a <code>ToolMessage</code>.</li>
<li>The agent stops looping and returns the tool’s output as the final response, bypassing any additional model call.</li>
<li><strong>Multiple parallel tool calls:</strong> When the model calls several tools in one step, all of them execute first. After all tools finish, the agent routes to <code>END</code> only if <strong>every</strong> tool in that batch has <code>return_direct=True</code>. The final response includes the <code>ToolMessage</code> outputs of every tool that was called in that step.</li>
</ul>
<p>Use this when:</p>
<ul>
<li>The tool’s output is the complete, user-ready answer (for example, a lookup that returns a ready-to-display result).</li>
<li>You want to avoid an extra model call when no additional reasoning is needed.</li>
<li>You need deterministic, unmodified output: the model cannot rephrase, summarize, or act on the tool result.</li>
</ul>
<h3 id="return-a-command-with-return_direct">Return a Command with return_direct</h3>
<p>A tool with <code>return_direct=True</code> can also return a <a href="https://reference.langchain.com/python/langgraph/types/Command"><code>Command</code></a> to update graph state before the agent exits. Unlike a plain return value, a <code>Command</code> is not automatically converted into a <code>ToolMessage</code>. When the <code>Command</code> targets the current graph (<code>graph</code> is not set or is <code>None</code>), include a <code>ToolMessage</code> in <code>Command.update</code> matching the tool call’s <code>tool_call_id</code>. Omitting it causes <code>ToolNode</code> to raise a <code>ValueError</code>, because every <code>AIMessage</code> tool call must have a corresponding <code>ToolMessage</code> in the message history.</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">messages</span> <span class="n">import</span> <span class="n">ToolMessage</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">tools</span> <span class="n">import</span> <span class="n">ToolRuntime</span><span class="p">,</span> <span class="k">tool</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">types</span> <span class="n">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="err">@</span><span class="k">tool</span><span class="p">(</span><span class="n">return_direct</span><span class="o">=</span><span class="n">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">fetch_and_store_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">runtime</span><span class="p">:</span> <span class="n">ToolRuntime</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Command</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Fetch order status and store it in state.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">status</span> <span class="o">=</span> <span class="n">f</span><span class="s2">&#34;Order {order_id} is shipped and will arrive in 2 days.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">Command</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">update</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;last_order_status&#34;</span><span class="p">:</span> <span class="n">status</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Must include a ToolMessage so the message history stays valid</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></span><span class="line"><span class="cl">                <span class="n">ToolMessage</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">content</span><span class="o">=</span><span class="n">status</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">tool_call_id</span><span class="o">=</span><span class="n">runtime</span><span class="o">.</span><span class="n">tool_call_id</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 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="error-handing">Error handing</h2>
<p>Using LangChain middleware to retry failed tool calls or return custom error message</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></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">collections</span><span class="o">.</span><span class="n">abc</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 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">wrap_tool_call</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">messages</span> <span class="n">import</span> <span class="n">ToolMessage</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">tools</span><span class="o">.</span><span class="n">tool_node</span> <span class="n">import</span> <span class="n">ToolCallRequest</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">wrap_tool_call</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_tool_errors</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="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="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Convert tool exceptions into ToolMessages the model can handle.&#34;&#34;&#34;</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">return</span> <span class="n">ToolMessage</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">content</span><span class="o">=</span><span class="n">f</span><span class="s2">&#34;Tool error: Please check your input and try again. ({e})&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">tool_call_id</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">tool_call</span><span class="p">[</span><span class="s2">&#34;id&#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></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;openai: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></span><span class="line"><span class="cl">    <span class="n">middleware</span><span class="o">=</span><span class="p">[</span><span class="n">handle_tool_errors</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="state-injection">State injection</h2>
<p>Tool access graph state through <code>ToolRUntime</code></p>
<h1 id="dynamic-tool-section">Dynamic tool section</h1>
<p>太多tool会占用过多的context,太少则会限制能力
动态工具允许在runtime时修改
主要有两种方式实现,取决于工具是否事先已知</p>
<h3 id="filtering-pre-registered-tools">Filtering pre-registered tools</h3>
<p>When all tools are known at agent creation time,you can pre-register them and dynamically filter which ones are exposed to the model based on state,permissions,or context</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">  代码有三个,自己去官网看
</span></span></code></pre></td></tr></table>
</div>
</div><ul>
<li>All possible tools are known at compile/startup time</li>
<li>You want to filter based on permissions, feature flags, or conversation state</li>
<li>Tools are static but their availability is dynamic</li>
</ul>
<h3 id="runtime-tool-register">Runtime tool register</h3>
<p>when tools are not discovered or created at runtime</p>
<ol>
<li><code>wrap_model_call</code> - Add the dynamic tools to the request</li>
<li><code>wrap_tool_call</code> - Handle execution of the dynamically added tools</li>
</ol>
<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></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">tools</span> <span class="n">import</span> <span class="k">tool</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">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">ToolCallRequest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># A tool that will be added dynamically at runtime</span>
</span></span><span class="line"><span class="cl"><span class="err">@</span><span class="k">tool</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">calculate_tip</span><span class="p">(</span><span class="n">bill_amount</span><span class="p">:</span> <span class="ne">float</span><span class="p">,</span> <span class="n">tip_percentage</span><span class="p">:</span> <span class="ne">float</span> <span class="o">=</span> <span class="mf">20.0</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Calculate the tip amount for a bill.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">tip</span> <span class="o">=</span> <span class="n">bill_amount</span> <span class="o">*</span> <span class="p">(</span><span class="n">tip_percentage</span> <span class="o">/</span> <span class="mi">100</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;Tip: ${tip:.2f}, Total: ${bill_amount + tip:.2f}&#34;</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">DynamicToolMiddleware</span><span class="p">(</span><span class="n">AgentMiddleware</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Middleware that registers and handles dynamic tools.&#34;&#34;&#34;</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">wrap_model_call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">ModelRequest</span><span class="p">,</span> <span class="n">handler</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Add dynamic tool to the request</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># This could be loaded from an MCP server, database, etc.</span>
</span></span><span class="line"><span class="cl">        <span class="n">updated</span> <span class="o">=</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="p">[</span><span class="o">*</span><span class="n">request</span><span class="o">.</span><span class="n">tools</span><span class="p">,</span> <span class="n">calculate_tip</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">updated</span><span class="p">)</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">wrap_tool_call</span><span class="p">(</span><span class="bp">self</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="n">handler</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Handle execution of the dynamic tool</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">tool_call</span><span class="p">[</span><span class="s2">&#34;name&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;calculate_tip&#34;</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="k">tool</span><span class="o">=</span><span class="n">calculate_tip</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></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">get_weather</span><span class="p">],</span>  <span class="c1"># Only static tools registered here</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">DynamicToolMiddleware</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"># The agent can now use both get_weather AND calculate_tip</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="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Calculate a 20% tip on $85&#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><p>This approach is best when:</p>
<ul>
<li>Tools are discovered at runtime (e.g., from an MCP server)</li>
<li>Tools are generated dynamically based on user data or configuration</li>
<li>You’re integrating with external tool registries</li>
</ul>
<h1 id="headless-tools">Headless tools</h1>
<p>某些工具应该运行在<strong>用户应用运行的地方</strong>（通常是浏览器），而不是在进程内部。<strong>无头工具 (Headless tools)<strong>是工具定义（包括名称、描述和参数 schema），您在</strong>服务端</strong>向智能体进行注册。而<strong>实现</strong>仅在<strong>客户端</strong>注册，并在简短的中断/恢复（interrupt/resume）握手后执行</p>
<h2 id="when-to-use">when to use</h2>
<p>when the work depends on the <strong>environment, device, or UI</strong> that only exists on the client. For example:</p>
<ul>
<li>Browser APIs</li>
<li>Privacy and locality</li>
<li>Latency(延迟)</li>
<li>Structured,safe effects</li>
</ul>
<h2 id="how-it-work">How it work</h2>
<p>model can see it and call it but it actually happens outside the process</p>
<ol>
<li>Define with (name, description,parameter) without in-process implementation</li>
<li>Register it with create agent so the model can call it normally</li>
<li>Handle the interrupt payload when the tool is invoked. Instead of running locally, the graph pauses with a payload shaped like <code>{&quot;type&quot;: &quot;tool&quot;, &quot;tool_call&quot;: {&quot;id&quot;, &quot;name&quot;, &quot;args&quot;}}</code>.</li>
<li>Resume the graph</li>
</ol>
<p>Use the optional <strong><code>onTool</code></strong> callback to observe lifecycle events (<code>start</code>, <code>success</code>, <code>error</code>) for UI feedback such as spinners or toasts.</p>
<h1 id="prebuilt-tools">Prebuilt tools</h1>
<p>LangChain provides a large collection of prebuilt tools and toolkits for common tasks like web search, code interpretation, database access, and more. These ready-to-use tools can be directly integrated into your agents without writing custom code.See the <a href="https://docs.langchain.com/oss/python/integrations/tools">tools and toolkits</a> integration page for a complete list of available tools organized by category.</p>
<h1 id="tools-from-mcp">Tools from MCP</h1>
<p><a href="https://modelcontextprotocol.io/">Model Context Protocol (MCP)</a> is an open protocol that standardizes how applications expose tools to language models. Rather than hand-authoring a tool, you connect to an MCP server and adapt the tools it advertises into LangChain tools, ready to pass to an agent like any other tool.<a href="https://reference.langchain.com/python/langchain/mcp/adapter/MCPAdapter"><code>MCPAdapter</code></a> discovers a server’s tools and converts them into LangChain tools. Open the adapter, call <code>list_tools()</code>, and hand the result to <a href="https://reference.langchain.com/python/langchain/agents/factory/create_agent"><code>create_agent</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></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">mcp</span> <span class="n">import</span> <span class="n">MCPAdapter</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">async</span> <span class="n">with</span> <span class="n">MCPAdapter</span><span class="p">(</span><span class="s2">&#34;https://example.com/mcp&#34;</span><span class="p">)</span> <span class="n">as</span> <span class="n">adapter</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">await</span> <span class="n">adapter</span><span class="o">.</span><span class="n">list_tools</span><span class="p">()</span>
</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 class="s2">&#34;claude-sonnet-4-6&#34;</span><span class="p">,</span> <span class="n">tools</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="server-side-tool-use">Server-side tool use</h1>
<p>Some chat models feature built-in tools that are executed server-side by the model provider. These include capabilities like web search and code interpreters that don’t require you to define or host the tool logic.Refer to the individual <a href="https://docs.langchain.com/oss/python/integrations/providers">chat model integration pages</a> and the <a href="https://docs.langchain.com/oss/python/langchain/models#server-side-tool-use">tool calling documentation</a> for details on enabling and using these built-in tools.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
