<?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>Frameworks on On My Way</title>
    <link>https://tuanzi-wow.cn/categories/frameworks/</link>
    <description>Recent content in Frameworks on On My Way</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <atom:link href="https://tuanzi-wow.cn/categories/frameworks/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>Agent</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/agent/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/agent/</guid>
      <description>Agent相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="agent">Agent</h1>
<h2 id="概述">概述:</h2>
<p>带有特定技能,专业知识和职责的专业团队成员</p>
<h2 id="智能体的属性">智能体的属性:</h2>
<p>最基本的三个
Role,Goal,Backstory
后续全为可选,请自行查阅官网</p>
<h2 id="创建智能体">创建智能体</h2>
<p>两种方法:
YAML配置(推荐)和直接在代码中定义</p>
<h3 id="yaml配置推荐">YAML配置(推荐)</h3>
<p>在代码中使用YAML配置,需要创建一个继承自CrewBase的工作组类</p>
<h3 id="直接代码定义">直接代码定义</h3>
<p>实例化Agent类直接在代码中创建智能体</p>
<h2 id="参数详情">参数详情:</h2>
<h4 id="关键参数">关键参数</h4>
<ul>
<li><code>role</code>, <code>goal</code> 和 <code>backstory</code> 是必填项，决定了智能体的行为。</li>
<li><code>llm</code> 确定使用的语言模型（默认：OpenAI 的 GPT-4）。</li>
</ul>
<h4 id="记忆与上下文">记忆与上下文</h4>
<ul>
<li><code>memory</code>: 启用以保持对话历史。</li>
<li><code>respect_context_window</code>: 防止 Token 限制问题。</li>
<li><code>knowledge_sources</code>: 添加特定领域的知识库。</li>
</ul>
<h4 id="执行控制">执行控制</h4>
<ul>
<li><code>max_iter</code>: 给定最佳答案前的最大尝试次数。</li>
<li><code>max_execution_time</code>: 超时时间（秒）。</li>
<li><code>max_rpm</code>: API 调用速率限制。</li>
<li><code>max_retry_limit</code>: 错误重试次数。</li>
</ul>
<h4 id="代码执行">代码执行</h4>
<p><code>allow_code_execution</code> 和 <code>code_execution_mode</code> 已弃用。<code>CodeInterpreterTool</code> 已从 <code>crewai-tools</code> 中移除。请使用如 <a href="https://e2b.org.cn/">E2B</a> 或 <a href="https://modal.com/">Modal</a> 等专用沙箱服务进行安全的代码执行。</p>
<ul>
<li><code>allow_code_execution</code> <em>(已弃用)</em>: 此前通过 <code>CodeInterpreterTool</code> 启用内置代码执行。</li>
<li><code>code_execution_mode</code> <em>(已弃用)</em>: 此前控制执行模式（<code>&quot;safe&quot;</code> 用于 Docker，<code>&quot;unsafe&quot;</code> 用于直接执行）。</li>
</ul>
<h4 id="高级功能">高级功能</h4>
<ul>
<li><code>multimodal</code>: 启用多模态能力以处理文本和视觉内容。</li>
<li><code>reasoning</code>: 启用智能体在执行任务前反思并制定计划。</li>
<li><code>inject_date</code>: 自动将当前日期注入任务描述中。</li>
</ul>
<h4 id="模板">模板</h4>
<ul>
<li><code>system_template</code>: 定义智能体的核心行为。</li>
<li><code>prompt_template</code>: 构建输入格式。</li>
<li><code>response_template</code>: 格式化智能体响应。</li>
</ul>
<h2 id="智能体工具">智能体工具</h2>
<p>可以使用CrewAI和LangChain工具包</p>
<h2 id="智能体记忆和上下文">智能体记忆和上下文</h2>
<h2 id="上下文窗口管理">上下文窗口管理</h2>
<h3 id="上下文窗口管理的工作原理">上下文窗口管理的工作原理</h3>
<p>由respect_context_window控制</p>
<p>when context window is too large,
1.自动摘要内容(参数为true)
2.停止执行并且报错(参数为false)</p>
<h3 id="大数据量的替代方案">大数据量的替代方案</h3>
<h4 id="1rag工具">1.RAG工具</h4>
<h4 id="2使用知识源">2.使用知识源</h4>
<h3 id="上下文窗口最佳实践">上下文窗口最佳实践</h3>
<ol>
<li><strong>监控上下文使用</strong>: 启用 <code>verbose=True</code> 以查看正在运行的上下文管理。</li>
<li><strong>高效设计</strong>: 构建任务以最小化上下文积累。</li>
<li><strong>使用合适的模型</strong>: 选择上下文窗口适合任务的 LLM。</li>
<li><strong>测试两种设置</strong>: 尝试 <code>True</code> 和 <code>False</code>，看看哪种更适合你的用例。</li>
<li><strong>结合 RAG 使用</strong>: 对于超大数据集，使用 RAG 工具而不是完全依赖上下文窗口。</li>
</ol>
<h3 id="使用kickoff进行直接智能体交互">使用Kickoff()进行直接智能体交互</h3>
<h4 id="工作原理">工作原理:</h4>
<p>kickoff()允许你直接向智能体发送消息获取回复</p>
<h4 id="参数和返回值">参数和返回值</h4>
<p>message;response_format</p>
]]></content:encoded>
    </item>
    <item>
      <title>AutoGen学习文档</title>
      <link>https://tuanzi-wow.cn/frameworks/autogen/autogen-learning-notes/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/autogen/autogen-learning-notes/</guid>
      <description>AutoGen学习文档相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="autogen-框架深度解析与全景知识总结报告">AutoGen 框架深度解析与全景知识总结报告</h1>
<blockquote>
<p><strong>编制日期</strong>：2026年11月<br>
<strong>报告归档目录</strong>：<code>D:\那就，战个未来吧\content\Frameworks\AutoGen</code><br>
<strong>适用版本</strong>：Microsoft AutoGen v0.4+（现代 Actor 架构）、AG2 / pyautogen（经典兼顾版）及 AutoGen Studio</p>
</blockquote>
<hr>
<h2 id="目录">目录</h2>
<ol>
<li><a href="/frameworks/autogen/autogen-learning-notes/#1-%e6%a1%86%e6%9e%b6%e8%83%8c%e6%99%af%e4%b8%8e%e5%8f%91%e5%b1%95%e6%bc%94%e5%8f%98%e8%b5%b7%e6%ba%90%e5%88%86%e6%ad%a7%e4%b8%8e%e7%8e%b0%e7%8a%b6">框架背景与发展演变：起源、分歧与现状</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#2-%e6%a0%b8%e5%bf%83%e8%ae%be%e8%ae%a1%e8%8c%83%e5%bc%8f%e5%af%b9%e8%af%9d%e5%8d%b3%e8%ae%a1%e7%ae%97-vs-actor-%e6%a8%a1%e5%9e%8b">核心设计范式：对话即计算 vs Actor 模型</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#3-autogen-v04-%e7%8e%b0%e4%bb%a3%e5%8c%96%e6%9e%b6%e6%9e%84%e5%88%86%e5%b1%82%e5%89%96%e6%9e%90">AutoGen v0.4 现代化架构分层剖析</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#4-%e6%a0%b8%e5%bf%83%e6%9e%84%e4%bb%b6%e4%b8%8e%e5%85%b3%e9%94%ae%e6%8a%bd%e8%b1%a1">核心构件与关键抽象</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#5-%e5%a4%9a%e6%99%ba%e8%83%bd%e4%bd%93%e7%bc%96%e6%8e%92%e4%b8%8e%e5%8d%8f%e4%bd%9c%e6%8b%93%e6%89%91%e6%a8%a1%e5%bc%8f">多智能体编排与协作拓扑模式</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#6-%e6%97%97%e8%88%b0%e7%ba%a7%e9%80%9a%e7%94%a8%e5%a4%9a%e6%99%ba%e8%83%bd%e4%bd%93%e7%b3%bb%e7%bb%9fmagnetic-one-%e6%9e%b6%e6%9e%84">旗舰级通用多智能体系统：Magentic-One 架构</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#7-%e5%85%b3%e9%94%ae%e6%9c%ba%e5%88%b6%e8%af%a6%e8%a7%a3%e4%bb%a3%e7%a0%81%e6%b2%99%e7%ae%b1%e6%89%a7%e8%a1%8c%e7%bb%88%e6%ad%a2%e6%9d%a1%e4%bb%b6%e4%b8%8e%e4%ba%ba%e6%9c%ba%e5%8d%8f%e5%90%8c">关键机制详解：代码沙箱执行、终止条件与人机协同</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#8-%e5%8f%af%e8%a7%86%e5%8c%96%e4%b8%8e%e4%bd%8e%e4%bb%a3%e7%a0%81%e4%b8%ad%e6%9e%a2autogen-studio">可视化与低代码中枢：AutoGen Studio</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#9-%e4%b8%bb%e6%b5%81%e5%a4%9a%e6%99%ba%e8%83%bd%e4%bd%93%e6%a1%86%e6%9e%b6%e6%a8%aa%e5%90%91%e5%af%b9%e6%af%94autogen-vs-langgraph-vs-crewai">主流多智能体框架横向对比：AutoGen vs LangGraph vs CrewAI</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#10-%e5%b7%a5%e4%b8%9a%e7%ba%a7%e5%ae%9e%e6%88%98%e4%bb%a3%e7%a0%81%e6%a0%b7%e4%be%8b%e5%a4%a7%e5%85%a8">工业级实战代码样例大全</a></li>
<li><a href="/frameworks/autogen/autogen-learning-notes/#11-%e7%94%9f%e4%ba%a7%e8%90%bd%e5%9c%b0%e6%8c%91%e6%88%98%e9%98%b2%e5%9d%91%e6%8c%87%e5%8d%97%e4%b8%8e%e6%9c%80%e4%bd%b3%e5%ae%9e%e8%b7%b5">生产落地挑战、防坑指南与最佳实践</a></li>
</ol>
<hr>
<h2 id="1-框架背景与发展演变起源分歧与现状">1. 框架背景与发展演变：起源、分歧与现状</h2>
<h3 id="11-起源与核心愿景">1.1 起源与核心愿景</h3>
<p>AutoGen 最早由 <strong>微软研究院（Microsoft Research）</strong> 联合宾夕法尼亚州立大学与华盛顿大学团队于 2023 年秋季开源推出（主创团队包括 Chi Wang、Qingyun Wu 等人）。其诞生的初衷是为了打破单一 LLM 提示工程（Prompt Engineering）的能力瓶颈，通过模拟人类团队在面对复杂多步骤问题时的<strong>多角色分工协作、批判审查、动态反馈与工具调用</strong>，来完成复杂的自动化软件工程、数据科学研究及多步骤推理任务。</p>
<p>AutoGen 首创了以**“对话（Conversation）”作为多智能体交互的统一通信范式**，让 Agent 之间的协作就像工程师团队在 Slack / 钉钉群聊中交流一样自然，迅速成为开源界最受欢迎的多智能体框架之一。</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">timeline
</span></span><span class="line"><span class="cl">    title AutoGen 演进与分化历史脉络
</span></span><span class="line"><span class="cl">    2023年下半年 : 微软开源 AutoGen (v0.1 / v0.2) : &#34;ConversableAgent&#34; 奠定对话协作范式 : PyPI 注册 pyautogen
</span></span><span class="line"><span class="cl">    2024年中期 : AutoGen Studio 诞生 : 引入低代码 Web 界面原型调试 : 社区生态爆炸式增长
</span></span><span class="line"><span class="cl">    2024年底 : 治理与技术路线大分流 : 创始人团队推出 AG2 保持兼容 : 微软启动 v0.4 底层异步 Actor 重构
</span></span><span class="line"><span class="cl">    2025年 - 2026年 : 微软统一生态 : AutoGen v0.4 融入 Microsoft Agent Framework : Magentic-One 确立复杂任务工业标杆
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h3 id="12-历史分歧ag2-与-microsoft-autogen-v04-的双轨现状">1.2 历史分歧：AG2 与 Microsoft AutoGen (v0.4+) 的双轨现状</h3>
<p>在 2024 年末，AutoGen 社区经历了一次重大的技术与治理分水岭，了解这一分歧对于当下开发者选型至关重要：</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">维度</th>
					<th style="text-align: left">AG2 (原团队 / 社区分支)</th>
					<th style="text-align: left">Microsoft AutoGen (微软官方分支 v0.4+)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>所属组织 / 仓库</strong></td>
					<td style="text-align: left"><code>ag2ai/ag2</code>（独立社区基金会）</td>
					<td style="text-align: left"><code>microsoft/autogen</code>（微软企业级主导）</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>PyPI 包名</strong></td>
					<td style="text-align: left"><code>autogen</code>, <code>pyautogen</code>, <code>ag2</code></td>
					<td style="text-align: left"><code>autogen-agentchat</code>, <code>autogen-core</code>, <code>autogen-ext</code></td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>设计哲学</strong></td>
					<td style="text-align: left"><strong>向后兼容优先</strong>，延续经典对话 API，演进为全功能 AgentOS</td>
					<td style="text-align: left"><strong>彻底重写重构</strong>，引入底层 Actor 模型与异步事件总线，面向高并发与分布式</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>核心 API 风格</strong></td>
					<td style="text-align: left"><code>ConversableAgent</code>, <code>GroupChat</code>, <code>GroupChatManager</code>（同步阻塞或原生循环）</td>
					<td style="text-align: left">异步/事件驱动（<code>asyncio</code>）、<code>AssistantAgent</code>、<code>RoundRobinGroupChat</code>、<code>SelectorGroupChat</code></td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>典型生态配合</strong></td>
					<td style="text-align: left">适合习惯 v0.2/v0.3 教程、快速脚本、丰富社区插件的开发者</td>
					<td style="text-align: left">适合需要企业级分布式部署、高可观测性、对接微软 Agent Framework / Semantic Kernel 的团队</td>
			</tr>
	</tbody>
</table>
<blockquote>
<p>[!IMPORTANT]
<strong>安装与导入防坑提示</strong>：</p>
<ul>
<li>如果执行 <code>pip install autogen</code> 或 <code>pip install pyautogen</code>，安装的是 <strong>AG2（经典兼容分支）</strong>。</li>
<li>如果要使用微软最新的现代化架构，应安装：<code>pip install -U autogen-agentchat autogen-ext[openai]</code>。</li>
<li>本报告将重点以<strong>现代 v0.4+ 架构</strong>为主轴进行深度解构，同时兼顾经典的 v0.2 / AG2 核心理念，提供全景认知。</li>
</ul>
</blockquote>
<hr>
<h2 id="2-核心设计范式对话即计算-vs-actor-模型">2. 核心设计范式：对话即计算 vs Actor 模型</h2>
<h3 id="21-传统理念对话即计算conversation-as-computation">2.1 传统理念：“对话即计算”（Conversation as Computation）</h3>
<p>在经典的 AutoGen 设计中，核心哲学是：</p>
<ol>
<li><strong>统一接口</strong>：任何能够发送和接收消息的实体都可以抽象为一个 Agent（无论是大模型、Python 代码执行器、数据库代理、还是坐在电脑前的人类）。</li>
<li><strong>状态共享与推进</strong>：计算过程不是由硬编码的控制流（如 if-else）决定的，而是由 Agent 发送的消息流驱动。一条消息既是<strong>交流媒介</strong>，也是<strong>计算输入</strong>。</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">flowchart LR
</span></span><span class="line"><span class="cl">    User([人类用户 / 触发器]) --&gt;|发包任务| Proxy[UserProxyAgent]
</span></span><span class="line"><span class="cl">    Proxy --&gt;|请求解决方案| Coder[Coder Agent]
</span></span><span class="line"><span class="cl">    Coder --&gt;|返回代码块| Proxy
</span></span><span class="line"><span class="cl">    Proxy --&gt;|沙箱执行代码| Runner[(Code Runner / Docker)]
</span></span><span class="line"><span class="cl">    Runner --&gt;|输出运行日志/报错| Proxy
</span></span><span class="line"><span class="cl">    Proxy --&gt;|反馈报错| Coder
</span></span><span class="line"><span class="cl">    Coder --&gt;|修复代码| Proxy
</span></span><span class="line"><span class="cl">    Proxy --&gt;|运行成功，产出成果| User
</span></span></code></pre></td></tr></table>
</div>
</div><p>这种机制极大降低了构建多角色协同的门槛，但也暴露出了局限：高度依赖 Chat Loop 循环，长流程下容易死循环、缺乏强类型事件约束、分布式横向扩展困难。</p>
<h3 id="22-现代演进基于-actor-模型的事件驱动架构">2.2 现代演进：基于 Actor 模型的事件驱动架构</h3>
<p>在 AutoGen v0.4+ 中，系统引入了计算机科学经典的 <strong>Actor 模型（Actor Model）</strong>：</p>
<ul>
<li><strong>完全解耦与隔离</strong>：每一个 Agent 都是一个完全独立的 Actor，拥有自己私有的内部状态，彼此之间绝不共享内存或直接调用对方的内部方法。</li>
<li><strong>消息总线传递（Message Passing）</strong>：Agent 之间唯一的交互方式是向**代理运行时（Agent Runtime）**的消息总线投递强类型消息包。</li>
<li><strong>异步响应驱动</strong>：Agent 监听特定类型的事件/消息，非阻塞地进行推理、工具调用或状态变更，随后向外广播事件或定向回复。</li>
</ul>
<p>这一转变使 AutoGen 从“实验性脚本库”蜕变为具备<strong>高并发处理能力、分布式跨机通信潜力与工业级 OpenTelemetry 追踪能力</strong>的企业级基础平台。</p>
<hr>
<h2 id="3-autogen-v04-现代化架构分层剖析">3. AutoGen v0.4 现代化架构分层剖析</h2>
<p>现代 AutoGen 采用了优雅的三层分层架构，从底层运行时到顶层高阶协作进行彻底解耦：</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-text" data-lang="text"><span class="line"><span class="cl">+---------------------------------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                                 Extensions Layer                                |
</span></span><span class="line"><span class="cl">|                                  (autogen-ext)                                  |
</span></span><span class="line"><span class="cl">|   - 模型客户端: OpenAI, Azure OpenAI, Anthropic, Gemini, Ollama, LiteLLM        |
</span></span><span class="line"><span class="cl">|   - 代码沙箱: DockerCommandLineCodeExecutor, LocalCommandLineCodeExecutor        |
</span></span><span class="line"><span class="cl">|   - 向量检索与缓存: Qdrant, Chroma, Redis                                        |
</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">|                                 AgentChat Layer                                 |
</span></span><span class="line"><span class="cl">|                               (autogen-agentchat)                               |
</span></span><span class="line"><span class="cl">|   - 智能体定义: AssistantAgent, CodeExecutorAgent, UserProxyAgent               |
</span></span><span class="line"><span class="cl">|   - 协作拓扑: RoundRobinGroupChat, SelectorGroupChat, Swarm                     |
</span></span><span class="line"><span class="cl">|   - 控制原语: TerminationConditions (AND / OR / Regex), Console UI 实时流        |
</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">|                                   Core Layer                                    |
</span></span><span class="line"><span class="cl">|                                 (autogen-core)                                  |
</span></span><span class="line"><span class="cl">|   - Actor Runtime: AgentId, SingleThreadedAgentRuntime, 消息总线路由           |
</span></span><span class="line"><span class="cl">|   - 事件驱动基石: 强类型消息（Typed Message Handler）、订阅与广播机制            |
</span></span><span class="line"><span class="cl">|   - 状态追踪与可观测性: OpenTelemetry 分布式跟踪, 上下文生命周期管理                |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------------------------------+
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="31-autogen-core底层基础设施与-actor-运行时">3.1 <code>autogen-core</code>：底层基础设施与 Actor 运行时</h3>
<ul>
<li><strong><code>AgentRuntime</code></strong>：Actor 的托管容器与路由中心，负责管理 Agent 实例的注册、生命周期、跨 Actor 投递消息。</li>
<li><strong><code>AgentId</code></strong>：全局唯一的身份标识（由 <code>type</code> 和 <code>key</code> 组成），支持多租户和多会话隔离。</li>
<li><strong>消息协议与类型派发</strong>：通过 Python 的类型注解和处理装饰器（如 <code>@message_handler</code>），让 Agent 只响应其关心的事件消息，极大增强了鲁棒性。</li>
</ul>
<h3 id="32-autogen-agentchat高阶协作抽象层">3.2 <code>autogen-agentchat</code>：高阶协作抽象层</h3>
<p>大部分应用开发者直接接触该层：</p>
<ul>
<li>提供面向自然语言对话的标准 Agent 封装。</li>
<li>提供了高度成熟的多智能体团队（Teams）模式，无需开发者从底层编写消息广播逻辑。</li>
<li>统一的异步流式接口（<code>run()</code> 与 <code>run_stream()</code>），无缝支撑 Web 前端实时打字机效果。</li>
</ul>
<h3 id="33-autogen-ext连接外部生态的扩展桥梁">3.3 <code>autogen-ext</code>：连接外部生态的扩展桥梁</h3>
<p>保持核心最小化的关键。无论是不同的模型厂商驱动、代码安全沙箱、还是各种内存与向量数据库，均作为可选插件在此模块发布，避免依赖膨胀。</p>
<hr>
<h2 id="4-核心构件与关键抽象">4. 核心构件与关键抽象</h2>
<h3 id="41-智能体角色agents">4.1 智能体角色（Agents）</h3>
<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">classDiagram</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="n">Agent</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="ne">String</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="ne">String</span> <span class="n">description</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">on_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="k">class</span> <span class="n">AssistantAgent</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">ChatCompletionClient</span> <span class="n">model_client</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="ne">String</span> <span class="n">system_message</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">List</span><span class="o">~</span><span class="n">Tool</span><span class="o">~</span> <span class="n">tools</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">generate_reply</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">class</span> <span class="n">CodeExecutorAgent</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">CodeExecutor</span> <span class="n">code_executor</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">execute_code_blocks</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">class</span> <span class="n">UserProxyAgent</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">HumanInputMode</span> <span class="n">input_mode</span>
</span></span><span class="line"><span class="cl">        <span class="o">+</span><span class="n">get_human_input</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">&lt;|--</span> <span class="n">AssistantAgent</span>
</span></span><span class="line"><span class="cl">    <span class="n">Agent</span> <span class="o">&lt;|--</span> <span class="n">CodeExecutorAgent</span>
</span></span><span class="line"><span class="cl">    <span class="n">Agent</span> <span class="o">&lt;|--</span> <span class="n">UserProxyAgent</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol>
<li><strong><code>AssistantAgent</code>（LLM 助理）</strong>：
<ul>
<li>依赖底层的 <code>ChatCompletionClient</code> 调用模型。</li>
<li>具备系统提示词（<code>system_message</code>）与工具（<code>tools</code>）注册能力。</li>
<li>必须配置清晰的 <code>description</code>。在多智能体动态路由中，路由选择器根据 <code>description</code> 来评估由谁接管任务。</li>
</ul>
</li>
<li><strong><code>CodeExecutorAgent</code>（代码执行专员）</strong>：
<ul>
<li>不依赖大语言模型进行文本生成，其职责是专门扫描上一轮对话中的 Markdown 代码块（如 <code>```python ... ```</code>），将其送入沙箱环境运行，并将执行输出（STDOUT / STDERR）作为新消息返回。</li>
</ul>
</li>
<li><strong><code>UserProxyAgent</code>（人类代理）</strong>：
<ul>
<li>作为现实世界用户与数字智能体群之间的桥梁，支持配置人工介入模式（如每轮必确认、仅在报错时确认、完全自动）。</li>
</ul>
</li>
</ol>
<hr>
<h3 id="42-团队与编排容器teams">4.2 团队与编排容器（Teams）</h3>
<p>在 v0.4 中，多个 Agent 协同的容器统称为 <strong>Team</strong>。Team 统一实现了任务派发、对话历史维持、流式状态广播以及终止条件裁决。</p>
<ul>
<li><code>team.run(task=&quot;...&quot;)</code>：返回最终的执行汇总（TaskResult）。</li>
<li><code>team.run_stream(task=&quot;...&quot;)</code>：返回异步生成器，实时捕获每一个内部事件（发言、思考、工具调用、工具返回）。</li>
</ul>
<hr>
<h3 id="43-强类型终止条件termination-conditions">4.3 强类型终止条件（Termination Conditions）</h3>
<p>避免无限递归或无休止的“客套对话”（如 A说“谢谢”，B说“不客气”，A又说“应该的”）是多智能体系统的核心难题。现代 AutoGen 提供了强大的组合式终止条件系统：</p>
<ul>
<li><strong><code>TextMentionTermination(text=&quot;TERMINATE&quot;)</code></strong>：当任意消息中出现指定终止关键词时触发。</li>
<li><strong><code>MaxMessageTermination(max_messages=10)</code></strong>：对话总消息数达到上限时强制刹车，作为防止 Token 爆炸的安全底线。</li>
<li><strong><code>StopMessageTermination()</code></strong>：当智能体发出特定的显式 Stop 信号时触发。</li>
<li><strong>逻辑运算符重载</strong>：
<ul>
<li><code>condition_a | condition_b</code>：<strong>或（OR）逻辑</strong>，只要满足其一即终止。</li>
<li><code>condition_a &amp; condition_b</code>：<strong>与（AND）逻辑</strong>，必须全部满足才终止。</li>
</ul>
</li>
</ul>
<hr>
<h2 id="5-多智能体编排与协作拓扑模式">5. 多智能体编排与协作拓扑模式</h2>
<p>AutoGen 提供了从简单到高度复杂的全谱系协作拓扑结构：</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-fallback" data-lang="fallback"><span class="line"><span class="cl">flowchart TD
</span></span><span class="line"><span class="cl">    subgraph 模式A: 轮询协作 (RoundRobin)
</span></span><span class="line"><span class="cl">        A1[Agent 1] --&gt; A2[Agent 2] --&gt; A3[Agent 3] --&gt; A1
</span></span><span class="line"><span class="cl">    end
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    subgraph 模式B: 动态选择器 (SelectorGroupChat)
</span></span><span class="line"><span class="cl">        M[Selector / Router]
</span></span><span class="line"><span class="cl">        M --&gt;|1. 分析上下文| B1[Planner]
</span></span><span class="line"><span class="cl">        M --&gt;|2. 调度执行| B2[Coder]
</span></span><span class="line"><span class="cl">        M --&gt;|3. 调度校验| B3[Reviewer]
</span></span><span class="line"><span class="cl">        B1 -.-&gt;|返回结果| M
</span></span><span class="line"><span class="cl">        B2 -.-&gt;|返回结果| M
</span></span><span class="line"><span class="cl">        B3 -.-&gt;|返回结果| M
</span></span><span class="line"><span class="cl">    end
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    subgraph 模式C: 蜂群交接 (Swarm / Handoffs)
</span></span><span class="line"><span class="cl">        S1[FrontDesk] --&gt;|Handoff: 账务问题| S2[BillingAgent]
</span></span><span class="line"><span class="cl">        S1 --&gt;|Handoff: 技术故障| S3[TechSupport]
</span></span><span class="line"><span class="cl">        S3 --&gt;|Handoff: 需要退款| S2
</span></span><span class="line"><span class="cl">    end
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="51-轮询协作roundrobingroupchat">5.1 轮询协作（RoundRobinGroupChat）</h3>
<ul>
<li><strong>机制</strong>：智能体按照预先定义的数组顺序依次轮流发言。</li>
<li><strong>适用场景</strong>：线性工作流，如【需求分析师 $\rightarrow$ 架构设计师 $\rightarrow$ 编码工程师 $\rightarrow$ 测试人员】。</li>
<li><strong>优缺点</strong>：逻辑清晰、执行顺序 100% 确定；但缺乏根据运行时突发情况动态调整人员的能力。</li>
</ul>
<h3 id="52-动态选择器群聊selectorgroupchat">5.2 动态选择器群聊（SelectorGroupChat）</h3>
<ul>
<li><strong>机制</strong>：内部配备一个独立的“路由大模型”，在每一轮对话结束时，该模型阅读当前全量对话记录以及每个 Agent 的 <code>description</code>，动态判断下一轮最适合发言的成员。</li>
<li><strong>关键参数</strong>：
<ul>
<li><code>allow_repeated_speaker=False</code>：防止同一个 Agent 连续自我对话陷入死循环。</li>
<li><code>selector_prompt</code>：可定制选择器的决策依据和规则。</li>
</ul>
</li>
<li><strong>适用场景</strong>：开放式探索、故障排查、复杂调试闭环（例如：Coder 编写 $\rightarrow$ Executor 运行 $\rightarrow$ 报错 $\rightarrow$ Coder 修复；Reviewer 只有在运行成功后才切入验证）。</li>
</ul>
<h3 id="53-蜂群交接模式swarm--handoffs">5.3 蜂群交接模式（Swarm &amp; Handoffs）</h3>
<ul>
<li><strong>机制</strong>：受到 OpenAI Swarm 的启发，由当前的执行 Agent 自主决定何时何地将当前对话上下文与控制权“交接（Handoff）”给指定名字的目标 Agent。</li>
<li><strong>适用场景</strong>：多分支客服系统、分诊引导、多级审批系统。</li>
</ul>
<h3 id="54-层次化与社会心智模式society-of-mind--hierarchical">5.4 层次化与社会心智模式（Society of Mind / Hierarchical）</h3>
<ul>
<li><strong>机制</strong>：将一个内部由多个 Agent 组成的复杂 Team 封装为一个对外的单一“虚拟 Agent”。对于外部观察者而言，它表现为一个输入输出接口；在内部，它启动子会话完成深度分工。</li>
<li><strong>适用场景</strong>：超大规模项目管理、跨领域知识集成、隔离子任务上下文（防止子任务的繁琐交互污染主上下文）。</li>
</ul>
<hr>
<h2 id="6-旗舰级通用多智能体系统magentic-one-架构">6. 旗舰级通用多智能体系统：Magentic-One 架构</h2>
<p>在 AutoGen v0.4 中，微软推出了其集大成的通用多智能体参考架构 —— <strong>Magentic-One</strong>（发表于顶级学术评测，专为解决跨 Web 与复杂本地文件系统的长时间开放性任务设计）。</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-fallback" data-lang="fallback"><span class="line"><span class="cl">graph TD
</span></span><span class="line"><span class="cl">    User([用户高维任务目标]) --&gt; Orchestrator
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    subgraph Magentic-One 编排中枢
</span></span><span class="line"><span class="cl">        Orchestrator[Orchestrator 核心调度器]
</span></span><span class="line"><span class="cl">        TaskLedger[(Task Ledger 任务总账本\n事实 / 猜测 / 宏观计划)]
</span></span><span class="line"><span class="cl">        ProgressLedger[(Progress Ledger 进度账本\n当前步骤 / 评估 / 下一步决策)]
</span></span><span class="line"><span class="cl">        Orchestrator &lt;--&gt; TaskLedger
</span></span><span class="line"><span class="cl">        Orchestrator &lt;--&gt; ProgressLedger
</span></span><span class="line"><span class="cl">    end
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    subgraph 领域专家 Worker 矩阵
</span></span><span class="line"><span class="cl">        WebSurfer[MultimodalWebSurfer\n基于 Chromium 视觉驱动网页浏览]
</span></span><span class="line"><span class="cl">        FileSurfer[FileSurfer\n深度探索与解析本地文件系统]
</span></span><span class="line"><span class="cl">        Coder[Coder\n编写并审查高质量 Python 脚本]
</span></span><span class="line"><span class="cl">        Terminal[ComputerTerminal\n安全沙箱命令与脚本执行环境]
</span></span><span class="line"><span class="cl">    end
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Orchestrator --&gt;|调度与分发| WebSurfer
</span></span><span class="line"><span class="cl">    Orchestrator --&gt;|调度与分发| FileSurfer
</span></span><span class="line"><span class="cl">    Orchestrator --&gt;|调度与分发| Coder
</span></span><span class="line"><span class="cl">    Orchestrator --&gt;|调度与分发| Terminal
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="61-双层推理循环dual-loop-mechanism">6.1 双层推理循环（Dual-Loop Mechanism）</h3>
<p>Magentic-One 克服复杂长程任务失控的关键，在于其著名的“双账本双循环”设计：</p>
<ol>
<li><strong>外层循环（Task Ledger &amp; 宏观规划）</strong>：
<ul>
<li>维护结构化的<strong>任务总账本</strong>（Task Ledger），详细记录当前已确认的客观事实（Facts）、尚待求证的推论（Educated Guesses）以及分步实施计划。</li>
<li>当任务遭遇重大挫折、路线走不通或内层循环多次重试失败时，触发外层规划重置（Replanning），推翻旧计划并重构路线。</li>
</ul>
</li>
<li><strong>内层循环（Progress Ledger &amp; 微观执行）</strong>：
<ul>
<li>维护<strong>执行进度账本</strong>（Progress Ledger），跟踪当前这一微观步骤是否完成、下一步应当委派哪个专属 Agent 进行操作。</li>
</ul>
</li>
</ol>
<h3 id="62-专属专家矩阵">6.2 专属专家矩阵</h3>
<ul>
<li><strong>MultimodalWebSurfer</strong>：结合视觉与 DOM 树的网页冲浪专员，能自主点击、滚动、填写表单、阅读验证码与抓取图文信息。</li>
<li><strong>FileSurfer</strong>：擅长在复杂的本地文件目录中检索、分析结构化与非结构化文件（PDF、Excel、CSV 等）。</li>
<li><strong>Coder &amp; ComputerTerminal</strong>：代码构思与命令行执行的黄金搭档，负责数值计算、数据整理与环境交互。</li>
</ul>
<hr>
<h2 id="7-关键机制详解代码沙箱执行终止条件与人机协同">7. 关键机制详解：代码沙箱执行、终止条件与人机协同</h2>
<h3 id="71-安全隔离的代码执行机制">7.1 安全隔离的代码执行机制</h3>
<p>AutoGen 与其他框架最显著的区别之一，是其自诞生起就将“代码生成与就地运行反馈”作为一等公民（First-class Citizen）。</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-text" data-lang="text"><span class="line"><span class="cl">[智能体产出 Markdown 文本]
</span></span><span class="line"><span class="cl">           │
</span></span><span class="line"><span class="cl">           ▼
</span></span><span class="line"><span class="cl">[代码提取器识别 ```python ... ``` 块]
</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">│                    执行器选择 (Code Executor)                │
</span></span><span class="line"><span class="cl">├──────────────────────────────┬──────────────────────────────┤
</span></span><span class="line"><span class="cl">│  LocalCommandLineCodeExecutor│ DockerCommandLineCodeExecutor│
</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">           │
</span></span><span class="line"><span class="cl">           ▼
</span></span><span class="line"><span class="cl">[捕获返回 Exit Code, STDOUT, STDERR]
</span></span><span class="line"><span class="cl">           │
</span></span><span class="line"><span class="cl">           ▼
</span></span><span class="line"><span class="cl">[作为消息重注入会话，由 Agent 评估或自我修复]
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>[!CAUTION]
<strong>生产安全警示</strong>：永远不要在没有隔离措施的生产服务器上，使用 <code>LocalCommandLineCodeExecutor</code> 运行未经审核的 Agent 生成代码！必须使用 Docker 沙箱或云端隔离容器（如 Azure Container Apps, E2B 沙箱）。</p>
</blockquote>
<hr>
<h3 id="72-动态人机协同human-in-the-loop-hitl">7.2 动态人机协同（Human-in-the-Loop, HITL）</h3>
<p>AutoGen 允许人类随时作为最高裁决者介入流程。在 v0.4 中，主要通过以下方式实现：</p>
<ol>
<li><strong>拦截批准机制（Approval Handlers）</strong>：在工具调用或代码执行前，挂载审批钩子函数。如果操作涉及危险命令（如删除数据库、发送外部邮件），挂起等待用户终端或 Web 端确认。</li>
<li><strong><code>UserProxyAgent</code> 交互注入</strong>：配置输入模式：
<ul>
<li><code>ALWAYS</code>：每一步执行完毕均暂停并要求人类输入下一步指令或评价。</li>
<li><code>NEVER</code>：完全自动化运行，直至触发终止条件。</li>
<li><code>TERMINATE</code>：仅在 Agent 准备结束会话时，询问人类是否认可结果；若不认可，可输入反馈让 Agent 继续修改。</li>
</ul>
</li>
</ol>
<hr>
<h2 id="8-可视化与低代码中枢autogen-studio">8. 可视化与低代码中枢：AutoGen Studio</h2>
<p>为了降低多智能体系统的配置门槛，微软推出了配套的可视化工作台 —— <strong>AutoGen Studio</strong>（在 v0.4 伴随底层进行了全面重构）。</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">graph LR
</span></span><span class="line"><span class="cl">    subgraph AutoGen Studio Web 架构
</span></span><span class="line"><span class="cl">        UI[现代 Web 前端\nReact / Next.js] &lt;---&gt; API[FastAPI 后端服务]
</span></span><span class="line"><span class="cl">        API &lt;---&gt; ORM[SQLModel / SQLAlchemy\nSQLite, PostgreSQL]
</span></span><span class="line"><span class="cl">        API &lt;---&gt; Engine[AutoGen v0.4 AgentChat Engine]
</span></span><span class="line"><span class="cl">    end
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="81-核心功能板块">8.1 核心功能板块</h3>
<ol>
<li><strong>Team Builder（团队构建画布）</strong>：
<ul>
<li>可视化配置 Agent 的 System Message、模型参数（Temperature、Top_p）、绑定工具（Skills）与终止规则。</li>
<li>以声明式 JSON / YAML Schema 导出配置，方便 Git 版本控制与 CI/CD 交付。</li>
</ul>
</li>
<li><strong>Playground（交互式试验场）</strong>：
<ul>
<li>提供类似聊天界面的对话控制台，支持<strong>实时打字机流式展示</strong>思考轨迹（Thoughts）与工具输出。</li>
<li><strong>交互拓扑图（Transition Graph）</strong>：可视化显示当前轮次信息在哪个 Agent 之间流转。</li>
<li><strong>中途打断与状态回滚（Mid-Execution Control）</strong>：可在 Agent 偏离预期时暂停任务，修正上下文后继续。</li>
</ul>
</li>
<li><strong>Gallery（模板画廊）</strong>：
<ul>
<li>沉淀官方与社区的最佳实践模板（如数据分析套件、学术论文精读团队、代码审查流水线），一键拉起复用。</li>
</ul>
</li>
</ol>
<h3 id="82-快速启动指南">8.2 快速启动指南</h3>
<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-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. 安装 AutoGen Studio</span>
</span></span><span class="line"><span class="cl">pip install autogenstudio
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 设置大模型 API 密钥 (Windows PowerShell)</span>
</span></span><span class="line"><span class="cl"><span class="nv">$env</span>:OPENAI_API_KEY<span class="o">=</span><span class="s2">&#34;sk-...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. 启动本地 Web 服务</span>
</span></span><span class="line"><span class="cl">autogenstudio ui --port <span class="m">8081</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><em>浏览器访问 <code>http://localhost:8081</code> 即可进入可视化操作界面。</em></p>
<hr>
<h2 id="9-主流多智能体框架横向对比autogen-vs-langgraph-vs-crewai">9. 主流多智能体框架横向对比：AutoGen vs LangGraph vs CrewAI</h2>
<p>在当前的技术生态中，<strong>AutoGen</strong>、<strong>LangGraph</strong> 与 <strong>CrewAI</strong> 形成了三足鼎立的局面。下表从底层原理到适用场景进行了全维度剖析：</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left">评估维度</th>
					<th style="text-align: left">AutoGen (v0.4+ / AG2)</th>
					<th style="text-align: left">LangGraph</th>
					<th style="text-align: left">CrewAI</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>底层核心范式</strong></td>
					<td style="text-align: left"><strong>对话协商与 Actor 消息传递</strong><br>（Conversational / Actor-based）</td>
					<td style="text-align: left"><strong>状态图与有限状态机</strong><br>（StateGraph / Pregel DAGs）</td>
					<td style="text-align: left"><strong>拟人化角色与任务链</strong><br>（Role-Playing Task Teams）</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>控制流确定性</strong></td>
					<td style="text-align: left"><strong>中 $\rightarrow$ 高</strong><br>通过 Team 类型与终止条件约束，自组织性强</td>
					<td style="text-align: left"><strong>极高</strong><br>通过显式边（Edges）和条件分支精确编码</td>
					<td style="text-align: left"><strong>中等</strong><br>通过 Crew 序列或层级委派执行，强调角色扮演</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>状态持久化与回滚</strong></td>
					<td style="text-align: left"><strong>成熟中</strong><br>v0.4 依靠 Actor 状态与 Studio 数据库存储</td>
					<td style="text-align: left"><strong>原生顶级</strong><br>内置 Checkpointer，完美支持 Time-Travel 回滚与分支</td>
					<td style="text-align: left"><strong>良好</strong><br>提供短期、长期与实体记忆库，易于开箱即用</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>代码执行与纠错能力</strong></td>
					<td style="text-align: left"><strong>天花板级别</strong><br>原生将代码沙箱与自我调试作为核心交互机制</td>
					<td style="text-align: left"><strong>需自行组装</strong><br>需编写对应节点并挂载沙箱工具</td>
					<td style="text-align: left"><strong>良好</strong><br>内置基础代码执行工具，但交互循环略重</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>上手门槛与研发周期</strong></td>
					<td style="text-align: left"><strong>快（2~6小时）</strong><br>AgentChat 高层封装优雅，开箱即用</td>
					<td style="text-align: left"><strong>陡峭（2~5天）</strong><br>需要深入理解图论、Reducer 及状态模式</td>
					<td style="text-align: left"><strong>极快（1~3小时）</strong><br>直观的“角色、目标、背景故事、任务”抽象</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>企业生产就绪度</strong></td>
					<td style="text-align: left"><strong>高</strong><br>微软背书，集成分布式运行时与 OTEL 遥测</td>
					<td style="text-align: left"><strong>极高</strong><br>LangSmith 生态协同，强合规金融/法律场景首选</td>
					<td style="text-align: left"><strong>高</strong><br>CrewAI Enterprise 企业平台支持</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>最佳匹配业务场景</strong></td>
					<td style="text-align: left"><strong>研发协同、代码调试、复杂探索性分析、Magentic 级多工具联动作业</strong></td>
					<td style="text-align: left"><strong>确定性极强的工作流、高合规审批流、强依赖分支回溯的业务</strong></td>
					<td style="text-align: left"><strong>内容生产矩阵、营销策划、固定流程的信息收集与汇总</strong></td>
			</tr>
	</tbody>
</table>
<hr>
<h2 id="10-工业级实战代码样例大全">10. 工业级实战代码样例大全</h2>
<p>以下代码均基于现代 <strong>AutoGen v0.4+ (<code>autogen-agentchat</code>, <code>autogen-ext</code>)</strong> 规范编写，采用原生 <code>asyncio</code> 与强类型架构。</p>
<h3 id="101-场景一双智能体协同思考与流式输出规划者--创作者">10.1 场景一：双智能体协同思考与流式输出（规划者 + 创作者）</h3>
<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-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 class="kn">from</span> <span class="nn">autogen_agentchat.agents</span> <span class="kn">import</span> <span class="n">AssistantAgent</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_agentchat.conditions</span> <span class="kn">import</span> <span class="n">MaxMessageTermination</span><span class="p">,</span> <span class="n">TextMentionTermination</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_agentchat.teams</span> <span class="kn">import</span> <span class="n">RoundRobinGroupChat</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_agentchat.ui</span> <span class="kn">import</span> <span class="n">Console</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_ext.models.openai</span> <span class="kn">import</span> <span class="n">OpenAIChatCompletionClient</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">async</span> <span class="k">def</span> <span class="nf">run_round_robin_demo</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 1. 实例化底层大模型客户端</span>
</span></span><span class="line"><span class="cl">  <span class="n">model_client</span> <span class="o">=</span> <span class="n">OpenAIChatCompletionClient</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-4o&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># api_key=&#34;sk-...&#34; 可从环境变量 OPENAI_API_KEY 自动读取</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"># 2. 定义角色清晰的专家 Agent</span>
</span></span><span class="line"><span class="cl">  <span class="n">planner</span> <span class="o">=</span> <span class="n">AssistantAgent</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;Architect&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">model_client</span><span class="o">=</span><span class="n">model_client</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">system_message</span><span class="o">=</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;你是一位资深系统架构师。面对用户的需求，请给出精炼的 3 步高可用设计框架。&#34;</span>
</span></span><span class="line"><span class="cl">          <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></span><span class="line"><span class="cl">  <span class="n">engineer</span> <span class="o">=</span> <span class="n">AssistantAgent</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;Engineer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">model_client</span><span class="o">=</span><span class="n">model_client</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">system_message</span><span class="o">=</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;你是一位资深全栈研发工程师。根据架构师给出的方案，给出具体的实现要点或伪代码。&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;当确认设计与实现完整闭环后，请在回复末尾附带严格的单词：TERMINATE。&#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></span><span class="line"><span class="cl">  <span class="c1"># 3. 设置鲁棒的组合终止条件（满足其一即停）</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># - 文本中包含 TERMINATE</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># - 或对话达到 6 轮防死锁上限</span>
</span></span><span class="line"><span class="cl">  <span class="n">termination</span> <span class="o">=</span> <span class="n">TextMentionTermination</span><span class="p">(</span><span class="s2">&#34;TERMINATE&#34;</span><span class="p">)</span> <span class="o">|</span> <span class="n">MaxMessageTermination</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">max_messages</span><span class="o">=</span><span class="mi">6</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"># 4. 构建轮询编排团队</span>
</span></span><span class="line"><span class="cl">  <span class="n">team</span> <span class="o">=</span> <span class="n">RoundRobinGroupChat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">participants</span><span class="o">=</span><span class="p">[</span><span class="n">planner</span><span class="p">,</span> <span class="n">engineer</span><span class="p">],</span> <span class="n">termination_condition</span><span class="o">=</span><span class="n">termination</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"># 5. 启动任务并以控制台流式渲染</span>
</span></span><span class="line"><span class="cl">  <span class="n">task_prompt</span> <span class="o">=</span> <span class="s2">&#34;请为日均千万级请求的短链接系统设计一套后端架构。&#34;</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">task_prompt</span><span class="si">}</span><span class="s2"> ===</span><span class="se">\n</span><span class="s2">&#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"># Console 自动流式打印每个 Agent 的回复与状态</span>
</span></span><span class="line"><span class="cl">  <span class="k">await</span> <span class="n">Console</span><span class="p">(</span><span class="n">team</span><span class="o">.</span><span class="n">run_stream</span><span class="p">(</span><span class="n">task</span><span class="o">=</span><span class="n">task_prompt</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><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">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">run_round_robin_demo</span><span class="p">())</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h3 id="102-场景二动态路由selector-本地安全代码执行闭环">10.2 场景二：动态路由（Selector）+ 本地安全代码执行闭环</h3>
<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><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></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 class="kn">from</span> <span class="nn">autogen_agentchat.agents</span> <span class="kn">import</span> <span class="n">AssistantAgent</span><span class="p">,</span> <span class="n">CodeExecutorAgent</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_agentchat.conditions</span> <span class="kn">import</span> <span class="n">TextMentionTermination</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_agentchat.teams</span> <span class="kn">import</span> <span class="n">SelectorGroupChat</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_agentchat.ui</span> <span class="kn">import</span> <span class="n">Console</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_ext.code_executors.local</span> <span class="kn">import</span> <span class="n">LocalCommandLineCodeExecutor</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">autogen_ext.models.openai</span> <span class="kn">import</span> <span class="n">OpenAIChatCompletionClient</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">async</span> <span class="k">def</span> <span class="nf">run_code_execution_selector_demo</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">  <span class="n">model_client</span> <span class="o">=</span> <span class="n">OpenAIChatCompletionClient</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-4o&#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"># 1. 编程专家：输出规范的 markdown 代码块</span>
</span></span><span class="line"><span class="cl">  <span class="n">coder</span> <span class="o">=</span> <span class="n">AssistantAgent</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;PythonCoder&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">model_client</span><span class="o">=</span><span class="n">model_client</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;精通 Python 的算法工程师。负责编写完整的、可直接运行的代码块来解决问题。&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">system_message</span><span class="o">=</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;你是一名资深 Python 开发者。请针对任务编写无外部危险依赖的高效代码。&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;务必将代码包裹在 ```python ... ``` 格式的 markdown 代码块中。&#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></span><span class="line"><span class="cl">  <span class="c1"># 2. 代码执行专员：只负责运行代码并捕获控制台输出</span>
</span></span><span class="line"><span class="cl">  <span class="n">workspace_dir</span> <span class="o">=</span> <span class="s2">&#34;./coding_workspace&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">local_executor</span> <span class="o">=</span> <span class="n">LocalCommandLineCodeExecutor</span><span class="p">(</span><span class="n">work_dir</span><span class="o">=</span><span class="n">workspace_dir</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">executor</span> <span class="o">=</span> <span class="n">CodeExecutorAgent</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;CodeRunner&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">code_executor</span><span class="o">=</span><span class="n">local_executor</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;代码执行专员。负责运行上文对话中的 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></span><span class="line"><span class="cl">  <span class="c1"># 3. 质量审查与验收员</span>
</span></span><span class="line"><span class="cl">  <span class="n">reviewer</span> <span class="o">=</span> <span class="n">AssistantAgent</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;QualityReviewer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">model_client</span><span class="o">=</span><span class="n">model_client</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;质量评估专家。审查运行结果是否正确，决定是否验收通过。&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">system_message</span><span class="o">=</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;你负责审查代码执行返回的结果是否完全符合用户需求。&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;如果运行报错，请指出问题并要求 PythonCoder 修复；&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;如果计算结果准确无误，请在发言末尾输出 TERMINATE。&#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></span><span class="line"><span class="cl">  <span class="c1"># 4. 终止条件</span>
</span></span><span class="line"><span class="cl">  <span class="n">termination</span> <span class="o">=</span> <span class="n">TextMentionTermination</span><span class="p">(</span><span class="s2">&#34;TERMINATE&#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"># 5. 构建动态选择器团队（SelectorGroupChat）</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Selector 会利用 model_client 来分析各 Agent 的 description 并决定下一位发言人</span>
</span></span><span class="line"><span class="cl">  <span class="n">team</span> <span class="o">=</span> <span class="n">SelectorGroupChat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="n">participants</span><span class="o">=</span><span class="p">[</span><span class="n">coder</span><span class="p">,</span> <span class="n">executor</span><span class="p">,</span> <span class="n">reviewer</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="n">model_client</span><span class="o">=</span><span class="n">model_client</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">termination_condition</span><span class="o">=</span><span class="n">termination</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="n">allow_repeated_speaker</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>  <span class="c1"># 强制禁止同一个智能体连续发言两次</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">task</span> <span class="o">=</span> <span class="s2">&#34;请计算第 30 个斐波那契数列的值，并计算前 30 个数的总和，打印输出结果。&#34;</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">task</span><span class="si">}</span><span class="s2"> ===</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">await</span> <span class="n">Console</span><span class="p">(</span><span class="n">team</span><span class="o">.</span><span class="n">run_stream</span><span class="p">(</span><span class="n">task</span><span class="o">=</span><span class="n">task</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">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">run_code_execution_selector_demo</span><span class="p">())</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h3 id="103-场景三经典版ag2--autogen-v02常用代码存照">10.3 场景三：经典版（AG2 / AutoGen v0.2）常用代码存照</h3>
<p>为了方便开发者维护存量系统或阅读历史开源代码，下文列出经典的 <code>ConversableAgent</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><span class="lnt">36
</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="c1"># 适用环境：pip install pyautogen 或 pip install ag2</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">autogen</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config_list</span> <span class="o">=</span> <span class="p">[{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;model&#34;</span><span class="p">:</span> <span class="s2">&#34;gpt-4o&#34;</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 class="s2">&#34;sk-...&#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">llm_config</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;config_list&#34;</span><span class="p">:</span> <span class="n">config_list</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;temperature&#34;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;timeout&#34;</span><span class="p">:</span> <span class="mi">120</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"># 1. 具备代码执行能力的 UserProxyAgent</span>
</span></span><span class="line"><span class="cl"><span class="n">user_proxy</span> <span class="o">=</span> <span class="n">autogen</span><span class="o">.</span><span class="n">UserProxyAgent</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;Admin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_message</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="n">code_execution_config</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;work_dir&#34;</span><span class="p">:</span> <span class="s2">&#34;classic_workspace&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;use_docker&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>  <span class="c1"># 仅测试，生产务必设为 True</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="n">human_input_mode</span><span class="o">=</span><span class="s2">&#34;NEVER&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">is_termination_msg</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="s2">&#34;TERMINATE&#34;</span> <span class="ow">in</span> <span class="n">x</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;content&#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="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. 专家助理</span>
</span></span><span class="line"><span class="cl"><span class="n">engineer</span> <span class="o">=</span> <span class="n">autogen</span><span class="o">.</span><span class="n">AssistantAgent</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;Engineer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">llm_config</span><span class="o">=</span><span class="n">llm_config</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_message</span><span class="o">=</span><span class="s2">&#34;资深数据分析师。通过编写 Python 代码处理数据，当结论得出后输出 TERMINATE。&#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"># 3. 经典双 Agent 发起对话</span>
</span></span><span class="line"><span class="cl"><span class="n">user_proxy</span><span class="o">.</span><span class="n">initiate_chat</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">engineer</span><span class="p">,</span> <span class="n">message</span><span class="o">=</span><span class="s2">&#34;请帮我抓取近 7 天某加密货币的价格趋势并绘制折线图保存在本地。&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="11-生产落地挑战防坑指南与最佳实践">11. 生产落地挑战、防坑指南与最佳实践</h2>
<h3 id="111-常见问题与避坑策略">11.1 常见问题与避坑策略</h3>
<h4 id="坑点-1agent-间陷入客套与循环死锁">坑点 1：Agent 间陷入“客套与循环死锁”</h4>
<ul>
<li><strong>现象</strong>：Agent A 与 Agent B 反复互相道谢，或者在某个微小逻辑上反复横跳，Token 飞速消耗。</li>
<li><strong>解决策略</strong>：
<ol>
<li>必须始终配置 <code>MaxMessageTermination</code> 作为双保险底线。</li>
<li>在 <code>system_message</code> 中明确规范退出准则（例如：“当目标达成时，严禁输出任何多余寒暄，只输出结论与 TERMINATE”）。</li>
<li>在 <code>SelectorGroupChat</code> 中开启 <code>allow_repeated_speaker=False</code>。</li>
</ol>
</li>
</ul>
<h4 id="坑点-2动态选择器selector频繁选错人">坑点 2：动态选择器（Selector）频繁选错人</h4>
<ul>
<li><strong>根因</strong>：Agent 的 <code>description</code> 写得过于简略或互相重叠。</li>
<li><strong>最佳实践</strong>：<strong>“Agent 的 description 是写给 Router 模型的提示词”</strong>。必须明确写出：该 Agent 能做什么、不能做什么、在什么输入出现时应当触发它（如上文样例中，明确指出 CodeRunner 专门“执行代码”）。</li>
</ul>
<h4 id="坑点-3上下文窗口溢出与成本剧增">坑点 3：上下文窗口溢出与成本剧增</h4>
<ul>
<li><strong>解决策略</strong>：
<ol>
<li><strong>层级折叠</strong>：采用 Society of Mind 模式，将低级别的调试和长日志封印在子 Team 中，只向主 Team 汇报最终结论。</li>
<li><strong>上下文修剪与总结</strong>：在长程对话中挂载消息过滤器（Message Truncation），定期对历史记录进行摘要压缩。</li>
</ol>
</li>
</ul>
<h4 id="坑点-4生产环境代码执行安全风险">坑点 4：生产环境代码执行安全风险</h4>
<ul>
<li><strong>铁律</strong>：严禁在生产宿主机直接运行 <code>LocalCommandLineCodeExecutor</code>。必须配置 Docker 沙箱，限制容器的最大内存、CPU 配额、磁盘写权限与对外网络访问（阻止反弹 Shell 或泄露环境变量）。</li>
</ul>
<hr>
<h3 id="112-生产化架构落地推荐范式">11.2 生产化架构落地推荐范式</h3>
<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-text" data-lang="text"><span class="line"><span class="cl">                                [客户端 / 前端应用]
</span></span><span class="line"><span class="cl">                                         │
</span></span><span class="line"><span class="cl">                                         ▼ (WebSocket / SSE 流)
</span></span><span class="line"><span class="cl">┌─────────────────────────────────────────────────────────────────────────────┐
</span></span><span class="line"><span class="cl">│                       应用服务网关 (FastAPI / gRPC)                         │
</span></span><span class="line"><span class="cl">│  - 鉴权认证、速率限制 (Rate Limit)、输入安全审查 (Guardrails)                │
</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">│                 AutoGen v0.4 运行时容器 (Agent Runtime Pool)                │
</span></span><span class="line"><span class="cl">│  - 基于 Team 组织架构 (Selector / Magentic-One 模式)                         │
</span></span><span class="line"><span class="cl">│  - OpenTelemetry 链路跟踪: 监控 LLM 延迟、Token 消耗、工具调用耗时          │
</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">│         模型服务接入层              │ │            隔离沙箱执行层           │
</span></span><span class="line"><span class="cl">│  - 统一调用网关 (Azure / LiteLLM)   │ │  - 弹性容器池 (Docker / E2B)        │
</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><hr>
<h2 id="12-总结与未来展望">12. 总结与未来展望</h2>
<p>AutoGen 以其独特的<strong>对话协同设计</strong>与<strong>强大的代码自我闭环</strong>，极大地推动了多智能体范式在学术界与工业界的繁荣。</p>
<ul>
<li>从 <strong>AutoGen 0.2</strong> 的轻量级对话探索，到 <strong>AutoGen 0.4</strong> 的分布式异步 <strong>Actor 模型</strong> 重塑；</li>
<li>从单体实验脚本，到以 <strong>Magentic-One</strong> 为代表的工业级通用任务解决架构；</li>
<li>从单一命令行，到 <strong>AutoGen Studio</strong> 的可视化低代码协同。</li>
</ul>
<p>无论你选择拥抱原班作者团队维护的 <strong>AG2</strong> 开源生态，还是选择深耕微软官方的 <strong>AutoGen v0.4 与 Microsoft Agent Framework</strong>，深入理解其<strong>消息传递机制、角色职责解耦、安全执行沙箱与严格终止控制</strong>，都将是构建下一代生产级企业 AI Agent 系统的核心竞争力。</p>
]]></content:encoded>
    </item>
    <item>
      <title>Checkpointers</title>
      <link>https://tuanzi-wow.cn/frameworks/langgraph/capabilities/checkpointers/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langgraph/capabilities/checkpointers/</guid>
      <description>Checkpointers相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>Checkpointer在每个super-step之后保存图状态的 snapshot(快照),并且按照thread进行组织</p>
<h1 id="why-use-checkpointers">Why use checkpointers</h1>
<p>适用于以下的场景:</p>
<ul>
<li>Human-in-the-loop:从interrupt中恢复运行</li>
<li>Memory:在交互之间保存memory,说直白点就是conversation history</li>
<li>Time travel:重放之前的图执行来调试或者审批;从外,允许fork(分叉)图状态</li>
<li>Fault-tolerance:从错误中恢复</li>
<li>Pending writes:在super-step中途失败时,可以恢复到之前成功的checkpoints</li>
</ul>
<h1 id="core-concepts">Core concepts</h1>
<h2 id="thread">Thread</h2>
<p>是分配给每个检查点的唯一ID或者标识符
包含一次运行的累计状态;当执行运行时,graph state会被持久化到这个线程中</p>
<p>当适用checkpointer调用graph 时,必须要在configurable中指明thread_id</p>
<p>线程的现状和历史状态都可以被检索.为了持久化,在执行运行之前必须创建一个线程</p>
<p>thread_id是存储和查询 checkpoints的主键</p>
<h1 id="checkpoints">Checkpoints</h1>
<p>线程中特定时间的state叫做checkpoint
是对每个super-step保存的graph state snapshot,由StateSnapshot对象表示</p>
<h2 id="super-steps">Super-steps</h2>
<p>LangGraph会在每个super-step边界常见checkpoint
resume只能从checkpoint执行</p>
<p>除了超级步骤检查点外，LangGraph 还会持久化<strong>节点（任务）级别</strong>的写入。随着超级步骤中的每个节点完成，其输出将作为链接到进行中检查点的任务条目写入检查点保存器的 <code>checkpoint_writes</code> 表中。这些按任务进行的写入支持了<a href="https://docs.langchain.org.cn/oss/python/langgraph/checkpointers#pending-writes">待处理写入</a>恢复：如果同一超级步骤中的另一个节点失败，已成功节点的写入已经是持久化的，在恢复时无需重新运行。超级步骤完成后，将提交完整的状态快照。</p>
<p>LangGraph 还会持久化超级步骤内单个节点执行的写入。这些写入以任务形式存储并用于容错：如果同一超级步骤中的另一个节点失败，恢复时无需重新计算成功的节点写入。这些任务写入不是完整的 <code>StateSnapshot</code> 检查点，因此时间旅行会从超级步骤边界的完整检查点恢复。</p>
<h2 id="checkpoints-namespace">checkpoints namespace</h2>
<p>每个检查点都有一个namespace_ns字段,来标识属于哪个图或者子图</p>
<ul>
<li>&ldquo;&quot;(空字符串):属于父(根)图</li>
<li>&ldquo;node_name: uuid&rdquo;: 属于给定节点调用的子图;对于嵌套子图，命名空间使用 <code>|</code> 分隔符连接（例如 <code>&quot;outer_node:uuid|inner_node:uuid&quot;</code>）</li>
</ul>
<p>你可以在node内部通过config访问checkpoint namespace</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-py" data-lang="py"><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">RunnableConfig</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">my_node</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">State</span><span class="p">,</span> <span class="n">config</span><span class="p">:</span> <span class="n">RunnableConfig</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">checkpoint_ns</span> <span class="o">=</span> <span class="n">config</span><span class="p">[</span><span class="s2">&#34;configurable&#34;</span><span class="p">][</span><span class="s2">&#34;checkpoint_ns&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># &#34;&#34; for the parent graph, &#34;node_name:uuid&#34; for a subgraph</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="get-and-update-state">Get and update state</h1>
<h2 id="get-state">Get state</h2>
<p>调用graph.get_state(config)查看图的最新状态,会返回一个<code>StateSnapshot</code>对象,对应提供的线程ID所关联的最新checkpoint,或者提供特定的checkpoint_id访问特定的checkpoint</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-fallback" data-lang="fallback"><span class="line"><span class="cl"># get the latest state snapshot
</span></span><span class="line"><span class="cl">config = {&#34;configurable&#34;: {&#34;thread_id&#34;: &#34;1&#34;}}
</span></span><span class="line"><span class="cl">graph.get_state(config)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># get a state snapshot for a specific checkpoint_id
</span></span><span class="line"><span class="cl">config = {&#34;configurable&#34;: {&#34;thread_id&#34;: &#34;1&#34;, &#34;checkpoint_id&#34;: &#34;1ef663ba-28fe-6528-8002-5a559208592c&#34;}}
</span></span><span class="line"><span class="cl">graph.get_state(config)
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="statesnapshot-fields">StateSnapshot fields</h3>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Field</th>
					<th style="text-align: left">Type</th>
					<th style="text-align: left">Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>values</code></td>
					<td style="text-align: left"><code>dict</code></td>
					<td style="text-align: left">State channel values at this checkpoint.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>next</code></td>
					<td style="text-align: left"><code>tuple[str, ...]</code></td>
					<td style="text-align: left">Node names to execute next. Empty <code>()</code> means the graph is complete.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>config</code></td>
					<td style="text-align: left"><code>dict</code></td>
					<td style="text-align: left">Contains <code>thread_id</code>, <code>checkpoint_ns</code>, and <code>checkpoint_id</code>.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>metadata</code></td>
					<td style="text-align: left"><code>dict</code></td>
					<td style="text-align: left">Execution metadata. Contains <code>source</code> (<code>&quot;input&quot;</code>, <code>&quot;loop&quot;</code>, or <code>&quot;update&quot;</code>), <code>writes</code> (node outputs), and <code>step</code> (super-step counter).</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>created_at</code></td>
					<td style="text-align: left"><code>str</code></td>
					<td style="text-align: left">ISO 8601 timestamp of when this checkpoint was created.</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>parent_config</code></td>
					<td style="text-align: left">`dict</td>
					<td style="text-align: left">None`</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>tasks</code></td>
					<td style="text-align: left"><code>tuple[PregelTask, ...]</code></td>
					<td style="text-align: left">Tasks to execute at this step. Each task has <code>id</code>, <code>name</code>, <code>error</code>, <code>interrupts</code>, and optionally <code>state</code> (subgraph snapshot, when using <code>subgraphs=True</code>).</td>
			</tr>
	</tbody>
</table>
<h2 id="get-state-history">Get state history</h2>
<p>通过调用 <a href="https://reference.langchain.org.cn/python/langgraph/graphs/#langgraph.graph.state.CompiledStateGraph.get_state_history"><code>graph.get_state_history(config)</code></a>获得对应thread_id的完整图执行历史记录,返回<code>StateSnapshot</code>对象列表,按照时间和顺序排序,最新的检查点为第一个元素</p>
<h3 id="find-a-specific-checkpoint">Find a specific checkpoint</h3>
<p>通过筛选过滤满足特定条件的checkpoints</p>
<h2 id="replay">Replay</h2>
<p>适用chexkpoint_id可以重新运行该检查点之后的节点,跳过之前的节点</p>
<h2 id="update-state">Update state</h2>
<p>适用<code>update_state</code>编辑graph state,这会创建一个包含更新值新的checkpoint:如果规定了reducer函数,值会通过reducer函数传递
可以指定as_node来控制更新被视为来自哪个node,这会影响接下来执行哪个node</p>
<h1 id="durabilty-modes持久化模式">Durabilty modes(持久化模式)</h1>
<p>提供了三种模式来在性能和数据一致性之间取得平衡</p>
<ul>
<li>&ldquo;exit&rdquo;:只在退出时持久化变更</li>
<li>&ldquo;async&rdquo;:在执行下一步的同时异步持久化变更</li>
<li>&ldquo;sync&rdquo;:在下一步开始之前同步持久化变更</li>
</ul>
<h1 id="optimize-checkpoint-storage">Optimize checkpoint storage</h1>
<p><a href="https://reference.langchain.org.cn/python/langgraph/channels/delta/DeltaChannel"><code>DeltaChannel</code></a> 仅存储增量变更（deltas）而非完整的累积值，大幅降低了对于追加频繁的通道的检查点大小</p>
<h1 id="checkpointer-libraries">Checkpointer libraries</h1>
<ul>
<li><code>langgraph-checkpoint</code>：检查点保存器基础接口（<a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.base.BaseCheckpointSaver"><code>BaseCheckpointSaver</code></a>）和序列化/反序列化接口（<a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.serde.base.SerializerProtocol"><code>SerializerProtocol</code></a>）。包含用于实验的内存中检查点保存器实现（<a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.memory.InMemorySaver"><code>InMemorySaver</code></a>）。LangGraph 自带包含 <code>langgraph-checkpoint</code>。</li>
<li><code>langgraph-checkpoint-sqlite</code>：使用 SQLite 数据库的 LangGraph 检查点保存器实现（<a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.sqlite.SqliteSaver"><code>SqliteSaver</code></a> / <a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.sqlite.aio.AsyncSqliteSaver"><code>AsyncSqliteSaver</code></a>）。非常适合实验和本地工作流。需要单独安装。</li>
<li><code>langgraph-checkpoint-postgres</code>：使用 Postgres 数据库的高级检查点保存器（<a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.postgres.PostgresSaver"><code>PostgresSaver</code></a> / <a href="https://reference.langchain.org.cn/python/langgraph/checkpoints/#langgraph.checkpoint.postgres.aio.AsyncPostgresSaver"><code>AsyncPostgresSaver</code></a>），在 LangSmith 中使用。非常适合在生产环境中使用。需要单独安装。</li>
<li><code>langchain-azure-cosmosdb</code>：使用 Azure Cosmos DB for NoSQL 的 LangGraph 检查点保存器实现（<a href="https://reference.langchain.org.cn/python/langchain-azure-cosmosdb/"><code>CosmosDBSaverSync</code></a> / <a href="https://reference.langchain.org.cn/python/langchain-azure-cosmosdb/"><code>CosmosDBSaver</code></a>）。非常适合与 Azure 一起在生产环境中使用。支持同步和异步操作，并支持 Microsoft Entra ID 身份验证。需要单独安装。</li>
</ul>
<h2 id="checkpointer-interface">Checkpointer interface</h2>
<ul>
<li><code>.put</code> - 存储包含配置和元数据的检查点。</li>
<li><code>.put_writes</code> - 存储链接到检查点的中间写入（即<a href="https://docs.langchain.org.cn/oss/python/langgraph/checkpointers#pending-writes">待处理写入</a>）。</li>
<li><code>.get_tuple</code> - 获取用于给定配置（<code>thread_id</code> 和 <code>checkpoint_id</code>）的检查点元组。这用于填充 <code>graph.get_state()</code> 中的 <code>StateSnapshot</code>。</li>
<li><code>.list</code> - 列出匹配给定配置和筛选条件的检查点。这用于填充 <code>graph.get_state_history()</code> 中的状态历史。</li>
</ul>
<p>如果检查点保存器与异步图执行一起使用（即通过 <code>.ainvoke</code>、<code>.astream</code>、<code>.abatch</code> 执行图），将使用上述方法的异步版本（<code>.aput</code>、<code>.aput_writes</code>、<code>.aget_tuple</code>、<code>.alist</code>）。</p>
]]></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>Crew</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/crew/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/crew/</guid>
      <description>Crew相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="crew">Crew</h1>
<h2 id="概念">概念:</h2>
<p>代表一组协作工作的agent,共同完成一系列的任务</p>
<h2 id="属性">属性</h2>
<p>必须:tasks,agents
剩下的均为可选,请自行前往官网查阅</p>
<h2 id="创建crew">创建crew</h2>
<p>两种方法:YAML配置(推荐)和直接再代码中定义</p>
<h3 id="yaml中配置">YAML中配置</h3>
<p>继承自CrewBase并使用装饰器定义agent,task和本身的类</p>
<p>@agent,@taks,@crew,@before_kickoff,@after_kickoff</p>
<h4 id="几种装饰器的概述">几种装饰器的概述</h4>
<ul>
<li><code>@CrewBase</code>: 将类标记为船员基类。</li>
<li><code>@agent</code>: 表示返回 <code>Agent</code> 对象的方法。</li>
<li><code>@task</code>: 表示返回 <code>Task</code> 对象的方法。</li>
<li><code>@crew</code>: 表示返回 <code>Crew</code> 对象的方法。</li>
<li><code>@before_kickoff</code>: (可选) 标记在船员启动前执行的方法。</li>
<li><code>@after_kickoff</code>: (可选) 标记在船员完成执行后执行的方法。</li>
</ul>
<h3 id="直接代码定义">直接代码定义</h3>
<h2 id="crewoutput">CrewOutput</h2>
<p>CrewAI框架中crew的输出封装再CrewOutput类中,提供了一种结构化的方法来访问crew的执行结果,包括原始字符串,JSON和Pydantic模型等各种格式
包含的内容:最终任务输出的结果,token使用情况以及单个任务的输出</p>
<h3 id="属性-1">属性</h3>
<table>
	<thead>
			<tr>
					<th style="text-align: left">属性</th>
					<th style="text-align: left">参数</th>
					<th style="text-align: left">类型</th>
					<th style="text-align: left">描述</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>原始</strong></td>
					<td style="text-align: left"><code>raw</code></td>
					<td style="text-align: left"><code>str</code></td>
					<td style="text-align: left">船员的原始输出。这是输出的默认格式。</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Pydantic</strong></td>
					<td style="text-align: left"><code>pydantic</code></td>
					<td style="text-align: left"><code>Optional[BaseModel]</code></td>
					<td style="text-align: left">表示船员结构化输出的 Pydantic 模型对象。</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>JSON 字典</strong></td>
					<td style="text-align: left"><code>json_dict</code></td>
					<td style="text-align: left"><code>Optional[Dict[str, Any]]</code></td>
					<td style="text-align: left">表示船员 JSON 输出的字典。</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>任务输出</strong></td>
					<td style="text-align: left"><code>tasks_output</code></td>
					<td style="text-align: left"><code>List[TaskOutput]</code></td>
					<td style="text-align: left"><code>TaskOutput</code> 对象的列表，每个对象代表船员中一个任务的输出。</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>Token 使用情况</strong></td>
					<td style="text-align: left"><code>token_usage</code></td>
					<td style="text-align: left"><code>Dict[str, Any]</code></td>
					<td style="text-align: left">token 使用情况摘要，提供关于语言模型在执行期间性能的洞察。</td>
			</tr>
	</tbody>
</table>
<h3 id="方法或者属性">方法或者属性</h3>
<table>
	<thead>
			<tr>
					<th style="text-align: left">方法/属性</th>
					<th style="text-align: left">描述</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>json</strong></td>
					<td style="text-align: left">如果输出格式为 JSON，则返回船员输出的 JSON 字符串表示。</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>to_dict 转换为字典格式</strong></td>
					<td style="text-align: left">将 JSON 和 Pydantic 输出转换为字典。</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>str 字符串</strong></td>
					<td style="text-align: left">返回船员输出的字符串表示，优先级为 Pydantic，然后是 JSON，最后是原始。</td>
			</tr>
	</tbody>
</table>
<h3 id="访问输出">访问输出</h3>
<p>通过crew对象的output属性访问输出</p>
<h2 id="访问crew日志">访问Crew日志</h2>
<p>通过将 <code>output_log_file</code> 设置为 <code>True(布尔值)</code> 或 <code>file_name(字符串)</code>，您可以实时查看船员执行日志。支持将事件记录为 <code>file_name.txt</code> 和 <code>file_name.json</code> 两种格式。如果设置为 <code>True(布尔值)</code>，将保存为 <code>logs.txt</code>。</p>
<h2 id="记忆利用">记忆利用</h2>
<p>通过存储和回忆执行记忆,来强化执行能力</p>
<h2 id="缓存利用">缓存利用</h2>
<p>利用缓存存储执行结果,减少重复任务的执行来提供流程效率</p>
<h2 id="crew使用指标">Crew使用指标</h2>
<p>通过usage_metrics属性来查看执行所有任务的LLM使用指标</p>
<h2 id="执行流程">执行流程</h2>
<p>顺序流程
层级流程:一个管理器进行协调</p>
<h3 id="启动crew">启动crew</h3>
<p>使用kickoff()方法启动process</p>
<h3 id="几种不同的启动方式">几种不同的启动方式</h3>
<p>kickoff(),kickoff_for_each(),kickoff_async(),kickoff_for_each_asych()</p>
<ul>
<li><code>kickoff()</code>: 根据定义的流程启动执行过程。</li>
<li><code>kickoff_for_each()</code>: 对集合中提供的每个输入事件或项目顺序执行任务。</li>
<li><code>kickoff_async()</code>: 异步启动工作流程。</li>
<li><code>kickoff_for_each_async()</code>: 对提供的每个输入事件或项目并发执行任务，利用异步处理。</li>
</ul>
<h3 id="从特定的任务重放">从特定的任务重放</h3>
<p>CLI命令replay重放</p>
<p>CrewAI 中的重放功能允许您使用命令行界面 (CLI) 从特定任务重放。通过运行命令 <code>crewai replay -t &lt;task_id&gt;</code>，您可以指定重放过程的 <code>task_id</code>。</p>
]]></content:encoded>
    </item>
    <item>
      <title>发布自定义工具</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/publish-custom-tools/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/publish-custom-tools/</guid>
      <description>发布自定义工具相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="发布自定义工具">发布自定义工具</h1>
<p>必须满足下面两种接口之一:
1.继承BaseTool
2.使用@tool装饰器</p>
<p>参数要求:</p>
<ul>
<li>拥有一个 <strong><code>name</code></strong> —— 一个简短的描述性标识符。</li>
<li>拥有一个 <strong><code>description</code></strong> —— 告诉代理何时以及如何使用该工具。这直接影响代理使用工具的效果，因此请务必清晰具体。</li>
<li>实现 <strong><code>_run</code></strong> (BaseTool) 或提供 <strong>函数体</strong> (@tool) —— 这是同步执行逻辑。</li>
<li>对所有参数和返回值使用 <strong>类型注解</strong>。</li>
<li>返回一个 <strong>字符串</strong> 结果（或可以有意义地转换为字符串的内容）。</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Thinking In LangGraph</title>
      <link>https://tuanzi-wow.cn/frameworks/langgraph/thinking-in-langgraph/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langgraph/thinking-in-langgraph/</guid>
      <description>Thinking In LangGraph相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="thinking-in-langgraph">Thinking in LangGraph</h1>
<p>使用LangGraph创建agent时
首先将他拆分为称为<code>nodes</code>的离散步骤
然后你要描述每个节点的decisions和transitions
最后你将<code>nodes</code>连接起来,通过一个每个节点都可以读写的共享<code>state</code></p>
<h1 id="example">Example</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></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">The</span> <span class="n">agent</span> <span class="n">should</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">-</span> <span class="n">Read</span> <span class="n">incoming</span> <span class="n">customer</span> <span class="n">emails</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span> <span class="n">Classify</span> <span class="n">them</span> <span class="n">by</span> <span class="n">urgency</span> <span class="ow">and</span> <span class="n">topic</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span> <span class="n">Search</span> <span class="n">relevant</span> <span class="n">documentation</span> <span class="n">to</span> <span class="n">answer</span> <span class="n">questions</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span> <span class="n">Draft</span> <span class="n">appropriate</span> <span class="n">responses</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span> <span class="n">Escalate</span> <span class="n">complex</span> <span class="n">issues</span> <span class="n">to</span> <span class="n">human</span> <span class="n">agents</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span> <span class="n">Schedule</span> <span class="n">follow</span><span class="o">-</span><span class="n">ups</span> <span class="n">when</span> <span class="n">needed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Example</span> <span class="n">scenarios</span> <span class="n">to</span> <span class="n">handle</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="mf">1.</span> <span class="n">Simple</span> <span class="n">product</span> <span class="n">question</span><span class="p">:</span> <span class="s2">&#34;How do I reset my password?&#34;</span>
</span></span><span class="line"><span class="cl"><span class="mf">2.</span> <span class="n">Bug</span> <span class="n">report</span><span class="p">:</span> <span class="s2">&#34;The export feature crashes when I select PDF format&#34;</span>
</span></span><span class="line"><span class="cl"><span class="mf">3.</span> <span class="n">Urgent</span> <span class="n">billing</span> <span class="n">issue</span><span class="p">:</span> <span class="s2">&#34;I was charged twice for my subscription!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="mf">4.</span> <span class="n">Feature</span> <span class="n">request</span><span class="p">:</span> <span class="s2">&#34;Can you add dark mode to the mobile app?&#34;</span>
</span></span><span class="line"><span class="cl"><span class="mf">5.</span> <span class="n">Complex</span> <span class="n">technical</span> <span class="n">issue</span><span class="p">:</span> <span class="s2">&#34;Our API integration fails intermittently with 504 errors&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="stpe1-map-out-your-workflow-as-discrete-steps">Stpe1: Map out your workflow as discrete steps</h2>
<p>Identifying the distinct steps and build a node(a function that does one specific thing)
Then, sketch how these steps connnect to each other.|</p>
<p><img alt="image-20260923104130267" loading="lazy" src="c:\\Users\\14152\\AppData\\Roaming\\Typora\\typora-user-images\\image-20260923104130267.png"></p>
<ul>
<li><code>Read Email</code>: Extract and parse the email content</li>
<li><code>Classify Intent</code>: Use an LLM to categorize urgency and topic, then route to appropriate action</li>
<li><code>Doc Search</code>: Query your knowledge base for relevant information</li>
<li><code>Bug Track</code>: Create or update issue in tracking system</li>
<li><code>Draft Reply</code>: Generate an appropriate response</li>
<li><code>Human Review</code>: Escalate to human agent for approval or handling</li>
<li><code>Send Reply</code>: Dispatch the email response</li>
</ul>
<h2 id="step2-identify-what-each-step-needs-to-do">Step2: Identify what each step needs to do</h2>
<p>决定每个<code>node</code>需要执行什么操作和它的正常运行需要哪些context</p>
<p>LLM steps: 当需要理解,分析,生成文本或者推理时
Data steps:当你需要从外部数据源检索数据时
Action steps: 当你需要外部操作时
User input steps:需要人工干预时</p>
<p>按照上面三种对于<code>nodes</code>进行分类</p>
<h2 id="step3-design-your-state">Step3: Design your state</h2>
<p><code>State</code>是agent中所有节点共享的memory</p>
<h3 id="what-belongs-to-state">what belongs to state:</h3>
<p>Include in state:
需要跨步骤持久化吗?需要则存入<code>state</code>
Don&rsquo;s  store:
能否从其他数据推导出来,如果可以,没必要存储</p>
<h3 id="keep-state-raw-format-prompts-on-demand">Keep state raw, format prompts on demand</h3>
<p>your state raw data, not formatted text. Format prompts inside nodes when you need them</p>
<p>优点:</p>
<ul>
<li>不同的节点可以按需结构化同样的数据</li>
<li>不需要更改你的state schema就可以更改prompt的模板</li>
<li>调试更清晰:你可以看到每个节点接受了什么样的数据</li>
<li>你的agent可以在不改变现有state的情况下不断演进</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></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 TypedDict, Literal
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Define the structure for email classification
</span></span><span class="line"><span class="cl">class EmailClassification(TypedDict):
</span></span><span class="line"><span class="cl">    intent: Literal[&#34;question&#34;, &#34;bug&#34;, &#34;billing&#34;, &#34;feature&#34;, &#34;complex&#34;]
</span></span><span class="line"><span class="cl">    urgency: Literal[&#34;low&#34;, &#34;medium&#34;, &#34;high&#34;, &#34;critical&#34;]
</span></span><span class="line"><span class="cl">    topic: str
</span></span><span class="line"><span class="cl">    summary: str
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class EmailAgentState(TypedDict):
</span></span><span class="line"><span class="cl">    # Raw email data
</span></span><span class="line"><span class="cl">    email_content: str
</span></span><span class="line"><span class="cl">    sender_email: str
</span></span><span class="line"><span class="cl">    email_id: str
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Classification result
</span></span><span class="line"><span class="cl">    classification: EmailClassification | None
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Raw search/API results
</span></span><span class="line"><span class="cl">    search_results: list[str] | None  # List of raw document chunks
</span></span><span class="line"><span class="cl">    customer_history: dict | None  # Raw customer data from CRM
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Generated content
</span></span><span class="line"><span class="cl">    draft_response: str | None
</span></span><span class="line"><span class="cl">    messages: list[str] | None
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="steps-4-build-your-nodes">Steps 4: Build your nodes</h2>
<p>A node in LangGraph is just a Python function that takes the current state and returns updates to it.</p>
<h3 id="handle-error-appropriately">Handle error appropriately</h3>
<table>
	<thead>
			<tr>
					<th style="text-align: left">Error Type</th>
					<th style="text-align: left">Who Fixes It</th>
					<th style="text-align: left">Strategy</th>
					<th style="text-align: left">When to Use</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left">Transient errors (network issues, rate limits)</td>
					<td style="text-align: left">System (automatic)</td>
					<td style="text-align: left">Retry policy</td>
					<td style="text-align: left">Temporary failures that usually resolve on retry</td>
			</tr>
			<tr>
					<td style="text-align: left">LLM-recoverable errors (tool failures, parsing issues)</td>
					<td style="text-align: left">LLM</td>
					<td style="text-align: left">Store error in state and loop back</td>
					<td style="text-align: left">LLM can see the error and adjust its approach</td>
			</tr>
			<tr>
					<td style="text-align: left">User-fixable errors (missing information, unclear instructions)</td>
					<td style="text-align: left">Human</td>
					<td style="text-align: left">Pause with <code>interrupt()</code></td>
					<td style="text-align: left">Need user input to proceed</td>
			</tr>
			<tr>
					<td style="text-align: left">Recoverable failure after retries</td>
					<td style="text-align: left">Developer (declarative)</td>
					<td style="text-align: left"><code>error_handler</code></td>
					<td style="text-align: left">Run a compensation/recovery branch after retry exhaustion</td>
			</tr>
			<tr>
					<td style="text-align: left">Unexpected errors</td>
					<td style="text-align: left">Developer</td>
					<td style="text-align: left">Let them bubble up</td>
					<td style="text-align: left">Unknown issues that need debugging</td>
			</tr>
	</tbody>
</table>
<h3 id="implementing-our-email-agent-nodes">Implementing our email agent nodes:</h3>
<p>其中一个部分的源码
[from typing import Literal
from langgraph.graph import StateGraph, START, END
from langgraph.types import interrupt, Command, RetryPolicy
from langchain_openai import ChatOpenAI
from langchain.messages import HumanMessage</p>
<p>llm = ChatOpenAI(model=&ldquo;gpt-5-nano&rdquo;)</p>
<p>def read_email(state: EmailAgentState) -&gt; dict:
&ldquo;&ldquo;&ldquo;Extract and parse email content&rdquo;&rdquo;&rdquo;
# In production, this would connect to your email service
return {
&ldquo;messages&rdquo;: [HumanMessage(content=f&quot;Processing email: {state[&rsquo;email_content&rsquo;]}&quot;)]
}</p>
<p>def classify_intent(state: EmailAgentState) -&gt; Command[Literal[&ldquo;search_documentation&rdquo;, &ldquo;human_review&rdquo;, &ldquo;draft_response&rdquo;, &ldquo;bug_tracking&rdquo;]]:
&ldquo;&ldquo;&ldquo;Use LLM to classify email intent and urgency, then route accordingly&rdquo;&rdquo;&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><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></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"># Create structured LLM that returns EmailClassification dict</span>
</span></span><span class="line"><span class="cl"><span class="n">structured_llm</span> <span class="o">=</span> <span class="n">llm</span><span class="o">.</span><span class="n">with_structured_output</span><span class="p">(</span><span class="n">EmailClassification</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Format the prompt on-demand, not stored in state</span>
</span></span><span class="line"><span class="cl"><span class="n">classification_prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">Analyze this customer email and classify it:
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Email: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;email_content&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">From: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;sender_email&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Provide classification including intent, urgency, topic, and summary.
</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="c1"># Get structured response directly as dict</span>
</span></span><span class="line"><span class="cl"><span class="n">classification</span> <span class="o">=</span> <span class="n">structured_llm</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">classification_prompt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Determine next node based on classification</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;intent&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;billing&#39;</span> <span class="ow">or</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;urgency&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;critical&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">goto</span> <span class="o">=</span> <span class="s2">&#34;human_review&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;intent&#39;</span><span class="p">]</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">&#39;question&#39;</span><span class="p">,</span> <span class="s1">&#39;feature&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">goto</span> <span class="o">=</span> <span class="s2">&#34;search_documentation&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;intent&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;bug&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">goto</span> <span class="o">=</span> <span class="s2">&#34;bug_tracking&#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="n">goto</span> <span class="o">=</span> <span class="s2">&#34;draft_response&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Store classification as a single dict in state</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 class="s2">&#34;classification&#34;</span><span class="p">:</span> <span class="n">classification</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="n">goto</span><span class="o">=</span><span class="n">goto</span>
</span></span><span class="line"><span class="cl"><span class="p">)]()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="step-5-wire-it-together">Step 5: Wire it together</h2>
<p>将<code>nodes</code>连接成一个可运行的图,由于<code>nodes</code>会自行处理决策路由,因此我们只需要少数几个必要的边(edge)</p>
<p>通过interrupt()启用Human-in-the-loop,我们需要checkpointer进行编译,以便在不同运行之间保存<code>state</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><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></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">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">langgraph.graph</span> <span class="kn">import</span> <span class="n">StateGraph</span><span class="p">,</span> <span class="n">START</span><span class="p">,</span> <span class="n">END</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">interrupt</span><span class="p">,</span> <span class="n">Command</span><span class="p">,</span> <span class="n">RetryPolicy</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain_openai</span> <span class="kn">import</span> <span class="n">ChatOpenAI</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></span><span class="line"><span class="cl"><span class="n">llm</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-nano&#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">def</span> <span class="nf">read_email</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">EmailAgentState</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Extract and parse email content&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># In production, this would connect to your email service</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">HumanMessage</span><span class="p">(</span><span class="n">content</span><span class="o">=</span><span class="sa">f</span><span class="s2">&#34;Processing email: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;email_content&#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="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">classify_intent</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">EmailAgentState</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Command</span><span class="p">[</span><span class="n">Literal</span><span class="p">[</span><span class="s2">&#34;search_documentation&#34;</span><span class="p">,</span> <span class="s2">&#34;human_review&#34;</span><span class="p">,</span> <span class="s2">&#34;draft_response&#34;</span><span class="p">,</span> <span class="s2">&#34;bug_tracking&#34;</span><span class="p">]]:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Use LLM to classify email intent and urgency, then route accordingly&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Create structured LLM that returns EmailClassification dict</span>
</span></span><span class="line"><span class="cl">    <span class="n">structured_llm</span> <span class="o">=</span> <span class="n">llm</span><span class="o">.</span><span class="n">with_structured_output</span><span class="p">(</span><span class="n">EmailClassification</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Format the prompt on-demand, not stored in state</span>
</span></span><span class="line"><span class="cl">    <span class="n">classification_prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Analyze this customer email and classify it:
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Email: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;email_content&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    From: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;sender_email&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Provide classification including intent, urgency, topic, and summary.
</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="c1"># Get structured response directly as dict</span>
</span></span><span class="line"><span class="cl">    <span class="n">classification</span> <span class="o">=</span> <span class="n">structured_llm</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">classification_prompt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Determine next node based on classification</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;intent&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;billing&#39;</span> <span class="ow">or</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;urgency&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;critical&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">goto</span> <span class="o">=</span> <span class="s2">&#34;human_review&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;intent&#39;</span><span class="p">]</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">&#39;question&#39;</span><span class="p">,</span> <span class="s1">&#39;feature&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="n">goto</span> <span class="o">=</span> <span class="s2">&#34;search_documentation&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">classification</span><span class="p">[</span><span class="s1">&#39;intent&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;bug&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">goto</span> <span class="o">=</span> <span class="s2">&#34;bug_tracking&#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="n">goto</span> <span class="o">=</span> <span class="s2">&#34;draft_response&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Store classification as a single dict in state</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 class="s2">&#34;classification&#34;</span><span class="p">:</span> <span class="n">classification</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="n">goto</span><span class="o">=</span><span class="n">goto</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>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>Persistence</title>
      <link>https://tuanzi-wow.cn/frameworks/langgraph/capabilities/persistence/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langgraph/capabilities/persistence/</guid>
      <description>Persistence相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<p>需求:continue a conversation, resume from a interrupt, recover from a failure, remember information across conversations
LangGraph提供了两个互补的持久化系统:</p>
<p><code>Checkpointer</code>将线程的graph state持久化为checkpoints
适用场景:short-term memory,thread -scoped memory</p>
<p><code>Stores:</code>将程序规定的数据持久化到graph state之外
适用场景:long-term memory,across-thread memory</p>
<h1 id="quickstart">Quickstart</h1>
<p>编译你的 graph with a checkpointer ,a store ,or both</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">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.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="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="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">graph</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="n">checkpointer</span><span class="o">=</span><span class="n">checkpointer</span><span class="p">,</span> <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></span><span class="line"><span class="cl"><span class="n">result</span> <span class="o">=</span> <span class="n">graph</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="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="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="checkpointer-vs-store">Checkpointer vs store</h1>
<table>
	<thead>
			<tr>
					<th style="text-align: left"></th>
					<th style="text-align: left">Checkpointer</th>
					<th style="text-align: left">Store</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left">Persists</td>
					<td style="text-align: left">Graph state snapshots</td>
					<td style="text-align: left">Application-defined key-value data</td>
			</tr>
			<tr>
					<td style="text-align: left">Scope</td>
					<td style="text-align: left">A single thread</td>
					<td style="text-align: left">Across threads</td>
			</tr>
			<tr>
					<td style="text-align: left">Memory type</td>
					<td style="text-align: left">Short-term, thread-scoped memory</td>
					<td style="text-align: left">Long-term, cross-thread memory</td>
			</tr>
			<tr>
					<td style="text-align: left">Use for</td>
					<td style="text-align: left">Conversation continuity, human-in-the-loop, time travel, and fault tolerance</td>
					<td style="text-align: left">User preferences, facts, and shared knowledge</td>
			</tr>
			<tr>
					<td style="text-align: left">Access pattern</td>
					<td style="text-align: left">Pass a <code>thread_id</code> in graph config</td>
					<td style="text-align: left">Read and write items from nodes or application code</td>
			</tr>
			<tr>
					<td style="text-align: left">Full guide</td>
					<td style="text-align: left"><a href="https://docs.langchain.com/oss/python/langgraph/checkpointers">Checkpointers</a></td>
					<td style="text-align: left"><a href="https://docs.langchain.com/oss/python/langgraph/stores">Stores</a></td>
			</tr>
	</tbody>
</table>
<h1 id="troubleshooting-common-issues">Troubleshooting common issues</h1>
<h2 id="postgressaverthread_idtoo-long">PostgresSaver:<code>thread_id</code>too long</h2>
<p>使用(Async)PostgresSaver,<code>thread_id</code>存储在一个长度受限的列中,如果长度超出限制,则会报错</p>
<p>解决办法:将thread_id保持在255字符之内</p>
<h2 id="memorysaver-does-not-persist-between-restarts">MemorySaver does not persist between restarts</h2>
<p>MemorySaver和InMemorySaver 把checkpoints保存在RAM中,重启时,所有的checkpoints会丢失</p>
<p>解决方法:生产环境中使用可持久化的checkpointer</p>
<h2 id="checkpoints-growing-unboundedly">Checkpoints growing unboundedly</h2>
<p>checkpoints无休止增长可能导致延迟和存储的开销</p>
<p>解决方法:定期清理旧的checkpoints或者设置保留策略</p>
<h2 id="state-access-from-parent-graph-to-subgraph">State access from parent graph to subgraph</h2>
<p>子图的state更新时,父图不会立马看到这些更新:因为子图管理自己的checkpoint namespace</p>
<p>解决方法:需要跨图边界的数据,使用Store共享状态,或者配置你的子图谢图父checkpoints</p>
]]></content:encoded>
    </item>
    <item>
      <title>智能体能力</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/agent-capabilities/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/agent-capabilities/</guid>
      <description>智能体能力相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="智能体能力">智能体能力</h1>
<p>工具:可调用函数
MCP服务器
应用:平台集合
技能:skills
知识:检索事实</p>
<h2 id="分类">分类</h2>
<h3 id="行动能力toolsmcpsapps">行动能力(Tools,MCPs,Apps)</h3>
<p>赋予了代理做事的能力</p>
<h3 id="背景能力skillsknowledge">背景能力(Skills,Knowledge)</h3>
<p>会修改代理的提示词</p>
<h2 id="对比表">对比表</h2>
<table>
	<thead>
			<tr>
					<th style="text-align: left">特性</th>
					<th style="text-align: center">工具</th>
					<th style="text-align: center">MCPs</th>
					<th style="text-align: center">应用</th>
					<th style="text-align: center">技能 (Skills)</th>
					<th style="text-align: center">知识</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><strong>赋予代理行动能力</strong></td>
					<td style="text-align: center">✅</td>
					<td style="text-align: center">✅</td>
					<td style="text-align: center">✅</td>
					<td style="text-align: center">❌</td>
					<td style="text-align: center">❌</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>修改提示词</strong></td>
					<td style="text-align: center">❌</td>
					<td style="text-align: center">❌</td>
					<td style="text-align: center">❌</td>
					<td style="text-align: center">✅</td>
					<td style="text-align: center">✅</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>需要代码</strong></td>
					<td style="text-align: center">是</td>
					<td style="text-align: center">仅配置</td>
					<td style="text-align: center">仅配置</td>
					<td style="text-align: center">仅 Markdown</td>
					<td style="text-align: center">仅配置</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>本地运行</strong></td>
					<td style="text-align: center">是</td>
					<td style="text-align: center">视情况而定</td>
					<td style="text-align: center">是（带环境变量）</td>
					<td style="text-align: center">不适用</td>
					<td style="text-align: center">是</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>需要 API 密钥</strong></td>
					<td style="text-align: center">按工具</td>
					<td style="text-align: center">按服务器</td>
					<td style="text-align: center">集成令牌</td>
					<td style="text-align: center">否</td>
					<td style="text-align: center">仅嵌入器 (Embedder)</td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>在 Agent 上设置</strong></td>
					<td style="text-align: center"><code>tools=[]</code></td>
					<td style="text-align: center"><code>mcps=[]</code></td>
					<td style="text-align: center"><code>apps=[]</code></td>
					<td style="text-align: center"><code>skills=[]</code></td>
					<td style="text-align: center"><code>knowledge_sources=[]</code></td>
			</tr>
			<tr>
					<td style="text-align: left"><strong>在 Crew 上设置</strong></td>
					<td style="text-align: center">❌</td>
					<td style="text-align: center">❌</td>
					<td style="text-align: center">❌</td>
					<td style="text-align: center"><code>skills=[]</code></td>
					<td style="text-align: center"><code>knowledge_sources=[]</code></td>
			</tr>
	</tbody>
</table>
]]></content:encoded>
    </item>
    <item>
      <title>基础概念</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/crewai-fundamentals/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/crewai-fundamentals/</guid>
      <description>基础概念相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="1crew和flow的定义以及选择">1.crew和flow的定义以及选择</h1>
<p>crew:团队,更适合复杂和创造性更强的用例,强调自主性和协作智能优化
flow:流程,通过事件进行触发,有严格的执行决策路径,适合路径确定,有更高的安全需求的用例,强调细粒度,事件驱动的控制</p>
<h3 id="一些核心概念">一些核心概念</h3>
<p>对于团队
crew,agent,process,task</p>
<p>对于流程</p>
<p>flow,事件,state(context),crew support</p>
<h3 id="如何决策框架crew-or-flow-or-both">如何决策框架(Crew or Flow or Both)</h3>
<p>在于理解应用的精确性和复杂性</p>
<h3 id="复杂性-精确度矩阵详解">复杂性-精确度矩阵详解</h3>
<h3 id="什么是复杂性">什么是复杂性？</h3>
<p>在CrewAI应用的上下文中，<strong>复杂性</strong>指的是：</p>
<ul>
<li>所需的不同步骤或操作数量</li>
<li>需要执行任务的多样性</li>
<li>不同组件之间的相互依赖关系</li>
<li>对条件逻辑和分支的需求</li>
<li>整体工作流程的复杂程度</li>
</ul>
<h3 id="什么是精确率"><a href="https://www.aidoczh.com/crewai/en/guides/concepts/evaluating-use-cases.html#what-is-precision"></a>什么是精确率？</h3>
<p><strong>精确度</strong>在此上下文中指的是：</p>
<ul>
<li>最终输出所需的准确度</li>
<li>对结构化、可预测结果的需求</li>
<li>可重现性的重要性</li>
<li>每个步骤所需的控制级别</li>
<li>输出结果的容差范围</li>
</ul>
<h3 id="四个象限"><a href="https://www.aidoczh.com/crewai/en/guides/concepts/evaluating-use-cases.html#the-four-quadrants"></a>四个象限</h3>
<h4 id="1-低复杂度低精度"><a href="https://www.aidoczh.com/crewai/en/guides/concepts/evaluating-use-cases.html#1-low-complexity,-low-precision"></a>1. 低复杂度，低精度</h4>
<p><strong>特性：</strong></p>
<ul>
<li>简单、直接的任务</li>
<li>对输出结果存在一定变化的容忍度</li>
<li>步骤数量有限</li>
<li>创意或探索性应用</li>
</ul>
<p><strong>推荐方法：<strong>使用最少智能体的简单Crews</strong>示例用例：</strong></p>
<ul>
<li>基础内容生成</li>
<li>创意头脑风暴</li>
<li>简单的摘要任务</li>
<li>创意写作辅助</li>
</ul>
<h4 id="2-低复杂度高精度"><a href="https://www.aidoczh.com/crewai/en/guides/concepts/evaluating-use-cases.html#2-low-complexity,-high-precision"></a>2. 低复杂度，高精度</h4>
<p><strong>特性：</strong></p>
<ul>
<li>需要精确、结构化输出的简单工作流</li>
<li>需要可重现的结果</li>
<li>步骤有限但精度要求高</li>
<li>通常涉及数据处理或转换</li>
</ul>
<p><strong>推荐方法：</strong> 使用直接LLM调用的流程或具有结构化输出的简单Crews<strong>示例用例：</strong></p>
<ul>
<li>数据提取与转换</li>
<li>表单填写与验证</li>
<li>结构化内容生成（JSON、XML）</li>
<li>简单的分类任务</li>
</ul>
<h4 id="3-高复杂度低精度"><a href="https://www.aidoczh.com/crewai/en/guides/concepts/evaluating-use-cases.html#3-high-complexity,-low-precision"></a>3. 高复杂度，低精度</h4>
<p><strong>特性：</strong></p>
<ul>
<li>包含多个步骤的多阶段流程</li>
<li>创意或探索性输出</li>
<li>组件间的复杂交互</li>
<li>对最终结果的差异容忍度</li>
</ul>
<p><strong>推荐方法：<strong>使用包含多个专业智能体的复杂Crew</strong>示例用例：</strong></p>
<ul>
<li>研究与分析</li>
<li>内容创作流水线</li>
<li>探索性数据分析</li>
<li>创造性问题解决</li>
</ul>
<h4 id="4-高复杂度高精度"><a href="https://www.aidoczh.com/crewai/en/guides/concepts/evaluating-use-cases.html#4-high-complexity,-high-precision"></a>4. 高复杂度，高精度</h4>
<p><strong>特性：</strong></p>
<ul>
<li>需要结构化输出的复杂工作流</li>
<li>具有严格准确性要求的多重相互依赖步骤</li>
<li>需要复杂的处理和精确的结果</li>
<li>通常是关键任务应用</li>
</ul>
<p><strong>推荐方法：</strong> 通过验证步骤编排多个Crew的流程<strong>示例用例：</strong></p>
<ul>
<li>企业决策支持系统</li>
<li>复杂数据处理流水线</li>
<li>多阶段文档处理</li>
<li>受监管行业应用</li>
</ul>
<h3 id="决策">决策</h3>
<p>when crew:
协作智能;问题需要思考;任务为创意性或者分析性;重视适应性而非严格结构;输出格式具有灵活性</p>
<p>when flow:
执行过程需要精确控制;应用具有复杂的状态需求;需要结构化可预测的输出;工作流程设计逻辑条件;需要将人工智能和过程化代码相结合</p>
<p>when both:
复杂地多阶段流程;需要兼顾创意和结构的应用场景;企业级人工智能应用</p>
<h1 id="2打造高效的智能体">2.打造高效的智能体</h1>
<h2 id="8020法则专注于任务而非智能体">80/20法则:专注于任务而非智能体</h2>
<p>80%的精力放在设计任务上,20%放在定义智能体上</p>
<h2 id="设计的核心原则">设计的核心原则</h2>
<h3 id="1角色-目标-背景故事框架">1.角色-目标-背景故事框架</h3>
<p>角色:智能体的专业职能:
具体专业;符合现实;包含领域专长</p>
<p>目标:智能体的目标和动机:
明确且以结果为导向;强调质量标准;纳入成功标准</p>
<p>背景故事:智能体的经验和视角
尽力专长和经验;定义工作风格和价值观;创造连贯的人格</p>
<h3 id="2专家优于通才">2.专家优于通才</h3>
<p>高度专注的智能体提供更加精确和更强相关性的输出</p>
<h3 id="3平衡专业化与多功能性">3.平衡专业化与多功能性</h3>
<p>角色专业化,应用多功能化;避免过于狭隘的定义;考虑协作环境</p>
<h3 id="4设置适当的专家水平">4.设置适当的专家水平:</h3>
<p>新手;中级;专家;世界级;</p>
<h2 id="为你的智能体打造高效任务">为你的智能体打造高效任务</h2>
<h3 id="高效任务的解剖结构">高效任务的解剖结构</h3>
<h4 id="任务描述过程">任务描述:过程</h4>
<p>详细的执行说明;上下文和背景信息;范围和限制;需要遵循的流程步骤</p>
<h4 id="预期输出交付物">预期输出:交付物</h4>
<p>格式规范(js,md);结构要求;质量标准;优质输出的示例</p>
<p>1.单一目的,单一输出 2.明确输入和输出 3.包含目的和上下文 4.使用结构化输出工具</p>
<h3 id="应该避免的常见错误">应该避免的常见错误:</h3>
<p>1.任务说明不明确 2.试图做太多全能任务 3.描述与预期输出不匹配 4.你自己都不了解流程 5.过早使用层级结构 6.模糊胡总和通用的智能体定义</p>
<h2 id="设计策略">设计策略:</h2>
<h3 id="协作设计">协作设计:</h3>
<p>在一个crew中的agent:
互补技能;交接点;建设性的张力</p>
<h3 id="创建专业的工具使用者">创建专业的工具使用者</h3>
<h3 id="使agent适应llm能力">使agent适应LLM能力</h3>
<h3 id="测试和迭代智能体设计">测试和迭代智能体设计</h3>
<p>1.从原型开始 2.使用样本任务测试 3.分析输出 4.优化定义 5.在协作中测试</p>
]]></content:encoded>
    </item>
    <item>
      <title>Workflow And Agent</title>
      <link>https://tuanzi-wow.cn/frameworks/langgraph/workflow-agent/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/langgraph/workflow-agent/</guid>
      <description>Workflow And Agent相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<ul>
<li>workflow拥有预先设计好的代码路径,按设定的顺序执行</li>
<li>Agent是动态的,定义自己的流程和工具使用</li>
</ul>
<p>LangGraph提供了persistent,streaming,和对debugging,deployment的支持</p>
<h1 id="set-up">Set up</h1>
<p>1.install dependency</p>
<p>2.initialize the LLM</p>
<h1 id="llms-and-augmentations">LLMs and augmentations</h1>
<p>Workflows and agentic systems are based on LLMs and the various augmentations you add to them. <a href="https://docs.langchain.com/oss/python/langchain/tools">Tool calling</a>, <a href="https://docs.langchain.com/oss/python/langchain/structured-output">structured outputs</a>, and <a href="https://docs.langchain.com/oss/python/langchain/short-term-memory">short term memory</a> are a few options for tailoring LLMs to your needs.</p>
<h1 id="prompt-chaining">Prompt chaining</h1>
<p>提示词链化是指每一轮提示词都处理上一轮的输出
常用于可以将分解为更小的,可验证的步骤的任务</p>
<h1 id="parallelization">Parallelization</h1>
<p>并行化:可以通过运行多个独立的子任务;或者多次运行同一任务以检查不同的输出,常用于:</p>
<ul>
<li>拆分子任务并且并行运行来提高速度</li>
<li>多次运行任务以检查不同的输出来提高置信度</li>
</ul>
<h1 id="routing">Routing</h1>
<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><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><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</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_extensions import Literal
</span></span><span class="line"><span class="cl">from langchain.messages import HumanMessage, SystemMessage
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Schema for structured output to use as routing logic
</span></span><span class="line"><span class="cl">class Route(BaseModel):
</span></span><span class="line"><span class="cl">    step: Literal[&#34;poem&#34;, &#34;story&#34;, &#34;joke&#34;] = Field(
</span></span><span class="line"><span class="cl">        None, description=&#34;The next step in the routing process&#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"># Augment the LLM with schema for structured output
</span></span><span class="line"><span class="cl">router = llm.with_structured_output(Route)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># State
</span></span><span class="line"><span class="cl">class State(TypedDict):
</span></span><span class="line"><span class="cl">    input: str
</span></span><span class="line"><span class="cl">    decision: str
</span></span><span class="line"><span class="cl">    output: str
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Nodes
</span></span><span class="line"><span class="cl">def llm_call_1(state: State):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Write a story&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    result = llm.invoke(state[&#34;input&#34;])
</span></span><span class="line"><span class="cl">    return {&#34;output&#34;: result.content}
</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 llm_call_2(state: State):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Write a joke&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    result = llm.invoke(state[&#34;input&#34;])
</span></span><span class="line"><span class="cl">    return {&#34;output&#34;: result.content}
</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 llm_call_3(state: State):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Write a poem&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    result = llm.invoke(state[&#34;input&#34;])
</span></span><span class="line"><span class="cl">    return {&#34;output&#34;: result.content}
</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 llm_call_router(state: State):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Route the input to the appropriate node&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Run the augmented LLM with structured output to serve as routing logic
</span></span><span class="line"><span class="cl">    decision = router.invoke(
</span></span><span class="line"><span class="cl">        [
</span></span><span class="line"><span class="cl">            SystemMessage(
</span></span><span class="line"><span class="cl">                content=&#34;Route the input to story, joke, or poem based on the user&#39;s request.&#34;
</span></span><span class="line"><span class="cl">            ),
</span></span><span class="line"><span class="cl">            HumanMessage(content=state[&#34;input&#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">    return {&#34;decision&#34;: decision.step}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Conditional edge function to route to the appropriate node
</span></span><span class="line"><span class="cl">def route_decision(state: State):
</span></span><span class="line"><span class="cl">    # Return the node name you want to visit next
</span></span><span class="line"><span class="cl">    if state[&#34;decision&#34;] == &#34;story&#34;:
</span></span><span class="line"><span class="cl">        return &#34;llm_call_1&#34;
</span></span><span class="line"><span class="cl">    elif state[&#34;decision&#34;] == &#34;joke&#34;:
</span></span><span class="line"><span class="cl">        return &#34;llm_call_2&#34;
</span></span><span class="line"><span class="cl">    elif state[&#34;decision&#34;] == &#34;poem&#34;:
</span></span><span class="line"><span class="cl">        return &#34;llm_call_3&#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"># Build workflow
</span></span><span class="line"><span class="cl">router_builder = StateGraph(State)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Add nodes
</span></span><span class="line"><span class="cl">router_builder.add_node(&#34;llm_call_1&#34;, llm_call_1)
</span></span><span class="line"><span class="cl">router_builder.add_node(&#34;llm_call_2&#34;, llm_call_2)
</span></span><span class="line"><span class="cl">router_builder.add_node(&#34;llm_call_3&#34;, llm_call_3)
</span></span><span class="line"><span class="cl">router_builder.add_node(&#34;llm_call_router&#34;, llm_call_router)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Add edges to connect nodes
</span></span><span class="line"><span class="cl">router_builder.add_edge(START, &#34;llm_call_router&#34;)
</span></span><span class="line"><span class="cl">router_builder.add_conditional_edges(
</span></span><span class="line"><span class="cl">    &#34;llm_call_router&#34;,
</span></span><span class="line"><span class="cl">    route_decision,
</span></span><span class="line"><span class="cl">    {  # Name returned by route_decision : Name of next node to visit
</span></span><span class="line"><span class="cl">        &#34;llm_call_1&#34;: &#34;llm_call_1&#34;,
</span></span><span class="line"><span class="cl">        &#34;llm_call_2&#34;: &#34;llm_call_2&#34;,
</span></span><span class="line"><span class="cl">        &#34;llm_call_3&#34;: &#34;llm_call_3&#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">router_builder.add_edge(&#34;llm_call_1&#34;, END)
</span></span><span class="line"><span class="cl">router_builder.add_edge(&#34;llm_call_2&#34;, END)
</span></span><span class="line"><span class="cl">router_builder.add_edge(&#34;llm_call_3&#34;, END)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Compile workflow
</span></span><span class="line"><span class="cl">router_workflow = router_builder.compile()
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Show the workflow
</span></span><span class="line"><span class="cl">display(Image(router_workflow.get_graph().draw_mermaid_png()))
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Invoke
</span></span><span class="line"><span class="cl">state = router_workflow.invoke({&#34;input&#34;: &#34;Write me a joke about cats&#34;})
</span></span><span class="line"><span class="cl">print(state[&#34;output&#34;])
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="orchestrator-worker">Orchestrator-worker</h1>
<p>在该配置中,编排器会:</p>
<ul>
<li>将任务拆分为子任务</li>
<li>将子任务委派给工作器</li>
<li>将工作器的输出综合为最终结果</li>
</ul>
<p>“编排器-工作器”工作流提供了更多的灵活性，通常在子任务无法像<a href="https://docs.langchain.org.cn/oss/python/langgraph/workflows-agents#parallelization">并行化</a>那样预先定义时使用。这在编写代码或需要在多个文件中更新内容的工作流中很常见</p>
<h2 id="creating-workers-in-langgraph">creating workers in LangGraph</h2>
<p>LangGraph对于编排器-工作器有内置的支持,<code>Send API</code>允许你动态创建worker并且向其发送特定输入
所有workers都有自己的state,并且所有workers的输出都有写入一个编排器图访问的共享状态键中,这使得编排器能够访问所有workers的输出和综合它们为最终输出</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><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><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</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">langgraph.types</span> <span class="kn">import</span> <span class="n">Send</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"># Graph state</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">State</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">topic</span><span class="p">:</span> <span class="nb">str</span>  <span class="c1"># Report topic</span>
</span></span><span class="line"><span class="cl">    <span class="n">sections</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">Section</span><span class="p">]</span>  <span class="c1"># List of report sections</span>
</span></span><span class="line"><span class="cl">    <span class="n">completed_sections</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="nb">list</span><span class="p">,</span> <span class="n">operator</span><span class="o">.</span><span class="n">add</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>  <span class="c1"># All workers write to this key in parallel</span>
</span></span><span class="line"><span class="cl">    <span class="n">final_report</span><span class="p">:</span> <span class="nb">str</span>  <span class="c1"># Final report</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"># Worker state</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">WorkerState</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">section</span><span class="p">:</span> <span class="n">Section</span>
</span></span><span class="line"><span class="cl">    <span class="n">completed_sections</span><span class="p">:</span> <span class="n">Annotated</span><span class="p">[</span><span class="nb">list</span><span class="p">,</span> <span class="n">operator</span><span class="o">.</span><span class="n">add</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"># Nodes</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">orchestrator</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">State</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Orchestrator that generates a plan for the report&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Generate queries</span>
</span></span><span class="line"><span class="cl">    <span class="n">report_sections</span> <span class="o">=</span> <span class="n">planner</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="n">SystemMessage</span><span class="p">(</span><span class="n">content</span><span class="o">=</span><span class="s2">&#34;Generate a plan for the report.&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">HumanMessage</span><span class="p">(</span><span class="n">content</span><span class="o">=</span><span class="sa">f</span><span class="s2">&#34;Here is the report topic: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;topic&#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="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">return</span> <span class="p">{</span><span class="s2">&#34;sections&#34;</span><span class="p">:</span> <span class="n">report_sections</span><span class="o">.</span><span class="n">sections</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">llm_call</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">WorkerState</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Worker writes a section of the report&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Generate section</span>
</span></span><span class="line"><span class="cl">    <span class="n">section</span> <span class="o">=</span> <span class="n">llm</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="n">SystemMessage</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">content</span><span class="o">=</span><span class="s2">&#34;Write a report section following the provided name and description. Include no preamble for each section. Use markdown formatting.&#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">HumanMessage</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;Here is the section name: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;section&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2"> and description: </span><span class="si">{</span><span class="n">state</span><span class="p">[</span><span class="s1">&#39;section&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">description</span><span class="si">}</span><span class="s2">&#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></span><span class="line"><span class="cl">    <span class="c1"># Write the updated section to completed sections</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span><span class="s2">&#34;completed_sections&#34;</span><span class="p">:</span> <span class="p">[</span><span class="n">section</span><span class="o">.</span><span class="n">content</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">synthesizer</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">State</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Synthesize full report from sections&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># List of completed sections</span>
</span></span><span class="line"><span class="cl">    <span class="n">completed_sections</span> <span class="o">=</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;completed_sections&#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"># Format completed section to str to use as context for final sections</span>
</span></span><span class="line"><span class="cl">    <span class="n">completed_report_sections</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="se">\n\n</span><span class="s2">---</span><span class="se">\n\n</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">completed_sections</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="p">{</span><span class="s2">&#34;final_report&#34;</span><span class="p">:</span> <span class="n">completed_report_sections</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"># Conditional edge function to create llm_call workers that each write a section of the report</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">assign_workers</span><span class="p">(</span><span class="n">state</span><span class="p">:</span> <span class="n">State</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Assign a worker to each section in the plan&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Kick off section writing in parallel via Send() API</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">Send</span><span class="p">(</span><span class="s2">&#34;llm_call&#34;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;section&#34;</span><span class="p">:</span> <span class="n">s</span><span class="p">})</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">state</span><span class="p">[</span><span class="s2">&#34;sections&#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="c1"># Build workflow</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker_builder</span> <span class="o">=</span> <span class="n">StateGraph</span><span class="p">(</span><span class="n">State</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Add the nodes</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker_builder</span><span class="o">.</span><span class="n">add_node</span><span class="p">(</span><span class="s2">&#34;orchestrator&#34;</span><span class="p">,</span> <span class="n">orchestrator</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker_builder</span><span class="o">.</span><span class="n">add_node</span><span class="p">(</span><span class="s2">&#34;llm_call&#34;</span><span class="p">,</span> <span class="n">llm_call</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker_builder</span><span class="o">.</span><span class="n">add_node</span><span class="p">(</span><span class="s2">&#34;synthesizer&#34;</span><span class="p">,</span> <span class="n">synthesizer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Add edges to connect nodes</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker_builder</span><span class="o">.</span><span class="n">add_edge</span><span class="p">(</span><span class="n">START</span><span class="p">,</span> <span class="s2">&#34;orchestrator&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker_builder</span><span class="o">.</span><span class="n">add_conditional_edges</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;orchestrator&#34;</span><span class="p">,</span> <span class="n">assign_workers</span><span class="p">,</span> <span class="p">[</span><span class="s2">&#34;llm_call&#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">orchestrator_worker_builder</span><span class="o">.</span><span class="n">add_edge</span><span class="p">(</span><span class="s2">&#34;llm_call&#34;</span><span class="p">,</span> <span class="s2">&#34;synthesizer&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker_builder</span><span class="o">.</span><span class="n">add_edge</span><span class="p">(</span><span class="s2">&#34;synthesizer&#34;</span><span class="p">,</span> <span class="n">END</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Compile the workflow</span>
</span></span><span class="line"><span class="cl"><span class="n">orchestrator_worker</span> <span class="o">=</span> <span class="n">orchestrator_worker_builder</span><span class="o">.</span><span class="n">compile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Show the workflow</span>
</span></span><span class="line"><span class="cl"><span class="n">display</span><span class="p">(</span><span class="n">Image</span><span class="p">(</span><span class="n">orchestrator_worker</span><span class="o">.</span><span class="n">get_graph</span><span class="p">()</span><span class="o">.</span><span class="n">draw_mermaid_png</span><span class="p">()))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Invoke</span>
</span></span><span class="line"><span class="cl"><span class="n">state</span> <span class="o">=</span> <span class="n">orchestrator_worker</span><span class="o">.</span><span class="n">invoke</span><span class="p">({</span><span class="s2">&#34;topic&#34;</span><span class="p">:</span> <span class="s2">&#34;Create a report on LLM scaling laws&#34;</span><span class="p">})</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">IPython.display</span> <span class="kn">import</span> <span class="n">Markdown</span>
</span></span><span class="line"><span class="cl"><span class="n">Markdown</span><span class="p">(</span><span class="n">state</span><span class="p">[</span><span class="s2">&#34;final_report&#34;</span><span class="p">])</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="evaluator-optimiezer">Evaluator-optimiezer</h1>
<p>评估器-优化器工作流中,一个LLM创建响应,另一个负责评估响应.如果评估器或者human-in-the-loop任务需要改进,就会提供反馈重新响应,循环过程直到生成可被接受的响应</p>
<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><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><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</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 Send
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Graph state
</span></span><span class="line"><span class="cl">class State(TypedDict):
</span></span><span class="line"><span class="cl">    topic: str  # Report topic
</span></span><span class="line"><span class="cl">    sections: list[Section]  # List of report sections
</span></span><span class="line"><span class="cl">    completed_sections: Annotated[
</span></span><span class="line"><span class="cl">        list, operator.add
</span></span><span class="line"><span class="cl">    ]  # All workers write to this key in parallel
</span></span><span class="line"><span class="cl">    final_report: str  # Final report
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Worker state
</span></span><span class="line"><span class="cl">class WorkerState(TypedDict):
</span></span><span class="line"><span class="cl">    section: Section
</span></span><span class="line"><span class="cl">    completed_sections: Annotated[list, operator.add]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Nodes
</span></span><span class="line"><span class="cl">def orchestrator(state: State):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Orchestrator that generates a plan for the report&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Generate queries
</span></span><span class="line"><span class="cl">    report_sections = planner.invoke(
</span></span><span class="line"><span class="cl">        [
</span></span><span class="line"><span class="cl">            SystemMessage(content=&#34;Generate a plan for the report.&#34;),
</span></span><span class="line"><span class="cl">            HumanMessage(content=f&#34;Here is the report topic: {state[&#39;topic&#39;]}&#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">    return {&#34;sections&#34;: report_sections.sections}
</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 llm_call(state: WorkerState):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Worker writes a section of the report&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Generate section
</span></span><span class="line"><span class="cl">    section = llm.invoke(
</span></span><span class="line"><span class="cl">        [
</span></span><span class="line"><span class="cl">            SystemMessage(
</span></span><span class="line"><span class="cl">                content=&#34;Write a report section following the provided name and description. Include no preamble for each section. Use markdown formatting.&#34;
</span></span><span class="line"><span class="cl">            ),
</span></span><span class="line"><span class="cl">            HumanMessage(
</span></span><span class="line"><span class="cl">                content=f&#34;Here is the section name: {state[&#39;section&#39;].name} and description: {state[&#39;section&#39;].description}&#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">    # Write the updated section to completed sections
</span></span><span class="line"><span class="cl">    return {&#34;completed_sections&#34;: [section.content]}
</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 synthesizer(state: State):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Synthesize full report from sections&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # List of completed sections
</span></span><span class="line"><span class="cl">    completed_sections = state[&#34;completed_sections&#34;]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Format completed section to str to use as context for final sections
</span></span><span class="line"><span class="cl">    completed_report_sections = &#34;\n\n---\n\n&#34;.join(completed_sections)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    return {&#34;final_report&#34;: completed_report_sections}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Conditional edge function to create llm_call workers that each write a section of the report
</span></span><span class="line"><span class="cl">def assign_workers(state: State):
</span></span><span class="line"><span class="cl">    &#34;&#34;&#34;Assign a worker to each section in the plan&#34;&#34;&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    # Kick off section writing in parallel via Send() API
</span></span><span class="line"><span class="cl">    return [Send(&#34;llm_call&#34;, {&#34;section&#34;: s}) for s in state[&#34;sections&#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"># Build workflow
</span></span><span class="line"><span class="cl">orchestrator_worker_builder = StateGraph(State)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Add the nodes
</span></span><span class="line"><span class="cl">orchestrator_worker_builder.add_node(&#34;orchestrator&#34;, orchestrator)
</span></span><span class="line"><span class="cl">orchestrator_worker_builder.add_node(&#34;llm_call&#34;, llm_call)
</span></span><span class="line"><span class="cl">orchestrator_worker_builder.add_node(&#34;synthesizer&#34;, synthesizer)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Add edges to connect nodes
</span></span><span class="line"><span class="cl">orchestrator_worker_builder.add_edge(START, &#34;orchestrator&#34;)
</span></span><span class="line"><span class="cl">orchestrator_worker_builder.add_conditional_edges(
</span></span><span class="line"><span class="cl">    &#34;orchestrator&#34;, assign_workers, [&#34;llm_call&#34;]
</span></span><span class="line"><span class="cl">)
</span></span><span class="line"><span class="cl">orchestrator_worker_builder.add_edge(&#34;llm_call&#34;, &#34;synthesizer&#34;)
</span></span><span class="line"><span class="cl">orchestrator_worker_builder.add_edge(&#34;synthesizer&#34;, END)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Compile the workflow
</span></span><span class="line"><span class="cl">orchestrator_worker = orchestrator_worker_builder.compile()
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Show the workflow
</span></span><span class="line"><span class="cl">display(Image(orchestrator_worker.get_graph().draw_mermaid_png()))
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Invoke
</span></span><span class="line"><span class="cl">state = orchestrator_worker.invoke({&#34;topic&#34;: &#34;Create a report on LLM scaling laws&#34;})
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">from IPython.display import Markdown
</span></span><span class="line"><span class="cl">Markdown(state[&#34;final_report&#34;])
</span></span></code></pre></td></tr></table>
</div>
</div><h1 id="agent">Agent</h1>
<p>agent通常实现为使用tool 执行操作的LLM,比workflow具有更高的自主权,适用于问题和解决方案都无法预测的情况</p>
<h2 id="toolnode">ToolNode</h2>
<p>是LangGraph中执行工具的节点,自动处理并行工具执行,错误处理和状态注入</p>
<h3 id="从tool访问graph-state和context">从tool访问graph state和context</h3>
<p>Py:使用注入的ToolRuntime参数读取state和Runtime.context</p>
]]></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>README</title>
      <link>https://tuanzi-wow.cn/frameworks/autogen/readme/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/autogen/readme/</guid>
      <description>README相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="autogen-知识库与全景总结">AutoGen 知识库与全景总结</h1>
<p>本目录存放了关于 <strong>AutoGen（多智能体对话协作框架）</strong> 的全面知识总结、架构解析与工程落地实践指南。</p>
<h2 id="核心文档索引">核心文档索引</h2>
<ul>
<li>📄 <strong><a href="/frameworks/autogen/autogen-learning-notes/">AutoGen学习文档.md</a></strong>（完整全景知识总结报告）
<ul>
<li><strong>第 1 章</strong>：框架背景与发展演变（起源、AG2 与微软官方 v0.4+ 的历史分流）</li>
<li><strong>第 2 章</strong>：核心设计范式（对话即计算 vs 现代化 Actor 模型与事件驱动）</li>
<li><strong>第 3 章</strong>：AutoGen v0.4 分层架构深度剖析（<code>autogen-core</code>, <code>autogen-agentchat</code>, <code>autogen-ext</code>）</li>
<li><strong>第 4 章</strong>：核心构件与关键抽象（Agents, Teams, TerminationConditions）</li>
<li><strong>第 5 章</strong>：多智能体编排与协作拓扑模式（轮询、动态选择器 Selector、蜂群 Swarm、层级式）</li>
<li><strong>第 6 章</strong>：旗舰级通用系统 Magentic-One 架构（双层推理循环 Ledgers 与专家矩阵）</li>
<li><strong>第 7 章</strong>：关键机制（代码沙箱安全隔离、人机协同 HITL、工具调用）</li>
<li><strong>第 8 章</strong>：可视化中枢 AutoGen Studio 原理与实战</li>
<li><strong>第 9 章</strong>：横向对比（AutoGen vs LangGraph vs CrewAI 全维度矩阵）</li>
<li><strong>第 10 章</strong>：工业级代码实战样例（RoundRobin、Selector+代码沙箱、经典 v0.2/AG2 存照）</li>
<li><strong>第 11 章</strong>：生产落地避坑指南与最佳实践</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>指纹识别</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/crewai-fingerprints/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/crewai-fingerprints/</guid>
      <description>指纹识别相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="指纹识别">指纹识别</h1>
<p>提供了一种在整个生命周期内唯一表示和追踪组件的方法,每个agent,crew,task在创建时都会获得一个唯一的,无法手动覆盖的指纹</p>
<h2 id="工作原理">工作原理</h2>
<p>指纹时crewai.security模块中的Fingerprint类的实例,包含:
UUID字符串:唯一标识符,自动生成且无法手动覆盖
创建时间戳:自动设置无法手动覆盖
元数据:可自定义的附加信息字典</p>
<h2 id="基本用法">基本用法</h2>
<h3 id="访问指纹">访问指纹:</h3>
<h3 id="处理指纹元数据">处理指纹元数据:</h3>
<p>向指纹添加元数据以获得更多的上下文信息:</p>
<h3 id="指纹持久性">指纹持久性</h3>
<p>在整个生命周期内保持不变</p>
<h3 id="确定性指纹">确定性指纹</h3>
<p>虽然不能设置UUID和创建时间戳,但是可以使用带有种子的generate方法来创建确定性指纹</p>
<h2 id="高级用法">高级用法</h2>
<h3 id="指纹结构">指纹结构</h3>
<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 crewai.security import Fingerprint
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">fingerprint = agent.fingerprint
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># UUID string - the unique identifier (auto-generated)
</span></span><span class="line"><span class="cl">uuid_str = fingerprint.uuid_str  # e.g., &#34;123e4567-e89b-12d3-a456-426614174000&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Creation timestamp (auto-generated)
</span></span><span class="line"><span class="cl">created_at = fingerprint.created_at  # A datetime object
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Metadata - for additional information (can be customized)
</span></span><span class="line"><span class="cl">metadata = fingerprint.metadata  # A dictionary, defaults to {}
</span></span></code></pre></td></tr></table>
</div>
</div>]]></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>掌握flow的状态(statement)管理</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/crewai-flow-state-management/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/crewai-flow-state-management/</guid>
      <description>掌握flow的状态(statement)管理相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="掌握流程的状态管理">掌握流程的状态管理</h1>
<h2 id="理解流程中状态的作用">理解流程中状态的作用:</h2>
<p>状态系统允许你维护上下文,在步骤之间共享数据,并构建复杂的应用逻辑</p>
<h3 id="为什么状态管理很重要">为什么状态管理很重要:</h3>
<p>1.维持上下文
2.构建复杂的条件逻辑
3.创建持久化应用
4.优雅地处理错误
5.拓展你的应用
6.实现对话式的应用</p>
<h2 id="状态管理基础">状态管理基础</h2>
<h3 id="流程状态生命周期">流程状态生命周期:</h3>
<p>1.初始化
2.修改
3.传输
4.持久化(可选):保存到存储中
5.完成</p>
<h3 id="状态管理的两种方法">状态管理的两种方法:</h3>
<p>1.非结构化状态:灵活性
2.结构化状态:使用Pydantic模型:类型安全性和验证</p>
<h2 id="非结构化状态管理">非结构化状态管理:</h2>
<h3 id="工作原理">工作原理:</h3>
<p>使用self.state访问状态,<strong>行为类似于字典</strong>;可以随时增删改查键;所有状态自动对所有流程方法可用</p>
<h3 id="when">when</h3>
<p>快速原型开发和简单流程;动态演变的状态需求;预先无法确定的结构情况;状态要求简单的流程</p>
<h2 id="结构化状态管理">结构化状态管理</h2>
<h3 id="工作原理-1">工作原理:</h3>
<p>定义一个表述状态结构的Pydantic模型
模型类型作为类型参数传给flow类
通过self.state访问状态,行为类似于Pydantic模型实例
所有字段根据定义的类型进行验证</p>
<h4 id="优势">优势:</h4>
<ol>
<li>类型安全</li>
<li>自我文档化</li>
<li>验证</li>
<li>IDE支持</li>
<li>默认值</li>
</ol>
<h3 id="when-1">when</h3>
<ul>
<li>明确数据模式的复杂流程</li>
<li>多名开发者共同参与的团队项目</li>
<li>需要验证数据验证的应用城西</li>
<li>需要强制执行特定数据类型和约束的流程</li>
</ul>
<h2 id="自动状态id">自动状态ID</h2>
<p>唯一标识符,对于非结构化,通过self.state[&ldquo;id&rdquo;]访问,对于结构化,则通过self.state.id访问
在流程中自动生成,整个生命周期不变,用于记录,跟踪和检索已经持久化的状态</p>
<h2 id="动态状态更新">动态状态更新</h2>
<h3 id="在步骤之间传递数据">在步骤之间传递数据</h3>
<p>流程方法可以返回值,这些值随后作为参数传递给监听方法</p>
<h2 id="持久化流程状态">持久化流程状态</h2>
<h3 id="persist装饰器可以自动执行状态持久化在执行的关键点保存你的流程状态">@persist()装饰器:可以自动执行状态持久化,在执行的关键点保存你的流程状态</h3>
<h4 id="类级别持久化在每次方法执行后保存状态">类级别持久化:在每次方法执行后保存状态</h4>
<h4 id="方法级别持久化更加细粒度的控制将其用于特定方法">方法级别持久化:更加细粒度的控制,将其用于特定方法</h4>
<h4 id="派生forking持久化状态">派生(Forking)持久化状态</h4>
<p><code>@persist</code> 在 <code>kickoff</code> / <code>kickoff_async</code> 上支持两种不同的水合模式。使用 <strong>resume</strong> (<code>inputs[&quot;id&quot;]</code>) 继续同一血缘；使用 <strong>fork</strong> (<code>restore_from_state_id</code>) 从快照启动一个新的血缘</p>
<table>
	<thead>
			<tr>
					<th style="text-align: left"></th>
					<th style="text-align: left">kickoff 后的 <code>state.id</code></th>
					<th style="text-align: left"><code>@persist</code> 的写入位置</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: left"><code>inputs[&quot;id&quot;]</code> (恢复)</td>
					<td style="text-align: left">提供的 id</td>
					<td style="text-align: left">提供的 id (扩展历史记录)</td>
			</tr>
			<tr>
					<td style="text-align: left"><code>restore_from_state_id</code> (派生)</td>
					<td style="text-align: left">新的 id，或者如果已固定则为 <code>inputs[&quot;id&quot;]</code></td>
					<td style="text-align: left">新的 id (源已保留)</td>
			</tr>
	</tbody>
</table>
<h2 id="高级状态模式">高级状态模式</h2>
<h3 id="条件启动和可恢复执行">条件启动和可恢复执行</h3>
<p>flow支持条件@start()和用于HILT/循环场景的可恢复执行</p>
<h3 id="基于状态的条件逻辑">基于状态的条件逻辑</h3>
<p>使用状态在流程中实现复杂的条件逻辑</p>
<h3 id="处理复杂的状态转换">处理复杂的状态转换</h3>
<p>对于复杂的状态转换,可以创建专用方法</p>
<h2 id="与crew一起的状态管理">与Crew一起的状态管理</h2>
<h3 id="将状态传给crew">将状态传给crew</h3>
<p>将参数传给agent的创建方法中</p>
<h3 id="在状态中处理crew输出">在状态中处理crew输出</h3>
<h2 id="状态管理最佳实践">状态管理最佳实践</h2>
<ol>
<li>保持状态聚焦:设计时仅包含必要内容</li>
<li>复杂流程使用结构化状态</li>
<li>记录状态转化</li>
<li>优雅地处理状态错误</li>
<li>使用状态进行进度跟踪</li>
<li>尽可能使用不可变操作</li>
</ol>
]]></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>构建第一个 CrewAI 项目</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/build-first-crew/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/build-first-crew/</guid>
      <description>构建第一个 CrewAI 项目相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h2 id="第一步创建新的crewai项目">第一步:创建新的CrewAI项目</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">crewai create crew research_crew
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="第二步探索项目结构">第二步:探索项目结构</h2>
<p>遵循py项目的最佳实践,通过分离配置文件和实现代码,在不修改源代码的情况下修改团队的行为</p>
<h2 id="第三步配置你的智能体">第三步:配置你的智能体</h2>
<p>通过修改agent.yaml来定义智能体
role;goal;backstory;llm</p>
<h2 id="第四步定义你的任务">第四步:定义你的任务</h2>
<p>任务代表可智能体将要执行的具体工作,包括详细的说明和预期输出
通过修改task.yaml文件
包含description和expected_output,context</p>
<h2 id="第五步配置你的团队">第五步:配置你的团队</h2>
<p>通过crew.py文件来配置</p>
<h2 id="第六步设置你的主脚本">第六步:设置你的主脚本</h2>
<h2 id="第七步设置你的环境变量">第七步:设置你的环境变量</h2>
<p>.env文件</p>
<h2 id="第八步安装依赖项">第八步:安装依赖项</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">crewai install
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="第九步运行你的团队">第九步:运行你的团队</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">crewai run
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="第十步查看你的输出">第十步:查看你的输出</h2>
]]></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>自定义提示词</title>
      <link>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/custom-prompts/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://tuanzi-wow.cn/frameworks/crewai/%E6%8C%87%E5%8D%97/custom-prompts/</guid>
      <description>自定义提示词相关学习笔记与实践要点。</description>
      <content:encoded><![CDATA[<h1 id="自定义提示词">自定义提示词</h1>
<h2 id="why">why</h2>
<p>1.针对LLM进行优化
2.更改语言
3.针对复杂领域进行专业化
4.调整语气和风格
5.支持超个性化用例</p>
<h2 id="了解crewai的提示词系统">了解CrewAI的提示词系统</h2>
<ul>
<li><strong>智能体模板 (Agent templates)</strong> – 控制每个智能体对其指定角色的处理方式。</li>
<li><strong>提示词片段 (Prompt slices)</strong> – 控制任务、工具使用和输出结构等专门行为。</li>
<li><strong>错误处理 (Error handling)</strong> – 指导智能体如何应对失败、异常或超时。</li>
<li><strong>工具专用提示词 (Tool-specific prompts)</strong> – 定义如何调用或使用工具的详细说明。</li>
</ul>
<h2 id="了解默认系统指令">了解默认系统指令</h2>
<p>当通过role,goal,backstory定义agent时,自动注入的一些指令</p>
<h3 id="crewai自动注入的内容">CrewAI自动注入的内容</h3>
<h4 id="针对无工具的智能体">针对无工具的智能体</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&#34;I MUST use these formats, my job depends on it!&#34;
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="针对有工具的智能体">针对有工具的智能体</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></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="s2">&#34;IMPORTANT: Use the following format in your response:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Thought</span><span class="p">:</span> <span class="n">you</span> <span class="n">should</span> <span class="n">always</span> <span class="n">think</span> <span class="n">about</span> <span class="n">what</span> <span class="n">to</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="n">Action</span><span class="p">:</span> <span class="n">the</span> <span class="n">action</span> <span class="n">to</span> <span class="n">take</span><span class="p">,</span> <span class="n">only</span> <span class="n">one</span> <span class="n">name</span> <span class="n">of</span> <span class="p">[</span><span class="n">tool_names</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">Action</span> <span class="ne">Input</span><span class="p">:</span> <span class="n">the</span> <span class="n">input</span> <span class="n">to</span> <span class="n">the</span> <span class="n">action</span><span class="p">,</span> <span class="n">just</span> <span class="n">a</span> <span class="n">simple</span> <span class="n">JSON</span> <span class="n">object</span><span class="o">...</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="针对结构化输出jsonpydantic">针对结构化输出（JSON/Pydantic）</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></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&#34;Ensure your final answer contains only the content in the following format: {output_format}
</span></span><span class="line"><span class="cl">Ensure the final output does not include any code block markers like ```json or ```python.&#34;
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="查看完整提示词">查看完整提示词</h3>
<h3 id="覆盖默认指令">覆盖默认指令</h3>
<p>选项一(推荐):自定义模板
选项二:自定义提示词文件
创建一个custom_prompt.json文件
选项三:为o1模型禁用系统提示词</p>
<h3 id="使用观测性工具进行调试">使用观测性工具进行调试</h3>
<h3 id="生产环境最佳实践">生产环境最佳实践</h3>
<ol>
<li><strong>始终检查生成的提示词</strong>，然后再部署到生产环境。</li>
<li><strong>使用自定义模板</strong>，当您需要全面控制提示词内容时。</li>
<li><strong>集成可观测性工具</strong>以进行持续的提示词监控（参阅 <a href="https://docs.crewai.com.cn/en/observability/overview">可观测性文档</a>）。</li>
<li><strong>使用不同的 LLM 进行测试</strong>，因为默认指令在不同模型间的表现可能不同。</li>
<li><strong>记录您的提示词自定义内容</strong>，以保持团队透明度。</li>
</ol>
<h2 id="管理提示词文件的最佳实践">管理提示词文件的最佳实践</h2>
<ol>
<li><strong>保持文件独立</strong> – 将自定义提示词存储在主代码库之外的专用 JSON 文件中。</li>
<li><strong>版本控制</strong> – 在代码仓库中跟踪更改，确保提示词的调整有清晰的文档记录。</li>
<li><strong>按模型或语言组织</strong> – 使用如 <code>prompts_llama.json</code> 或 <code>prompts_es.json</code> 的命名方案，以便快速识别专用配置。</li>
<li><strong>记录更改</strong> – 提供注释或维护一个 README 文件，详细说明自定义的目的和范围。</li>
<li><strong>最小化改动</strong> – 仅覆盖确实需要调整的特定片段，保持其他部分的默认功能完好无损。</li>
</ol>
]]></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>
