<?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>LangGraph on On My Way</title>
    <link>https://tuanzi-wow.cn/tags/langgraph/</link>
    <description>Recent content in LangGraph on On My Way</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <atom:link href="https://tuanzi-wow.cn/tags/langgraph/index.xml" rel="self" type="application/rss+xml" />
    <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>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>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>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>
  </channel>
</rss>
