<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Python on niuzj</title>
    <link>https://niuzj.org/tags/python/</link>
    <description>Recent content in Python on niuzj</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Thu, 09 Apr 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://niuzj.org/tags/python/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>给 AI Agent 装一个状态机：用 Playbook 模式编排多步骤工作流</title>
      <link>https://niuzj.org/posts/agent-playbook-state-machine/</link>
      <pubDate>Thu, 09 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://niuzj.org/posts/agent-playbook-state-machine/</guid>
      <description>&lt;p&gt;前段时间在做 AI Agent 的时候遇到一个问题：用户说&amp;quot;帮我做个 UGC 视频&amp;quot;，Agent 需要分三步走——先生成脚本，再生图，最后生视频。每一步都要构建工作流模块、运行、展示结果给用户确认，不满意还得回退重做。&lt;/p&gt;&#xA;&lt;p&gt;一开始想的是把整个流程写进 system prompt 里，让 LLM 自己记住做到哪了。试了一下发现不靠谱——对话一长，LLM 就忘了自己在第几步，有时候跳步，有时候重复。&lt;/p&gt;&#xA;&lt;p&gt;后来研究了一圈主流框架（LangGraph、Burr、CrewAI、PocketFlow），发现大家解决这个问题的思路都一样：&lt;strong&gt;给 Agent 装一个状态机&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;什么是状态机&#34;&gt;什么是状态机&lt;/h2&gt;&#xA;&lt;p&gt;状态机就是一张图：节点是&amp;quot;状态&amp;quot;，边是&amp;quot;转移条件&amp;quot;。系统在任意时刻只能处于一个状态，满足某个条件后跳到下一个状态。&lt;/p&gt;&#xA;&lt;p&gt;用 UGC 视频制作举例：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;collect_requirements → build_script → run_script → review_script&#xA;                          ↑                              │&#xA;                          └──── approved=false ───────────┘&#xA;                                                          │ approved=true&#xA;                                                          ↓&#xA;                        build_image → run_image → review_image&#xA;                          ↑                              │&#xA;                          └──── approved=false ───────────┘&#xA;                                                          │ approved=true&#xA;                                                          ↓&#xA;                        build_video → run_video → review_video → done&#xA;                          ↑                              │&#xA;                          └──── approved=false ───────────┘&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;每个 review 节点有两条出边：&lt;code&gt;approved=true&lt;/code&gt; 走下一个模块，&lt;code&gt;approved=false&lt;/code&gt; 回退到 build 重做。这就是状态机的全部——状态 + 转移条件。&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解 Python asyncio：从事件循环到 AI Agent 并发调用</title>
      <link>https://niuzj.org/posts/python-asyncio-agent/</link>
      <pubDate>Wed, 15 Oct 2025 00:00:00 +0000</pubDate>
      <guid>https://niuzj.org/posts/python-asyncio-agent/</guid>
      <description>&lt;p&gt;写 AI Agent 的时候，你一定遇到过这个场景：LLM 返回了两个 tool call，比如同时查天气和查日历。它们之间没有依赖关系，完全可以并发执行。但如果你用 &lt;code&gt;await&lt;/code&gt; 一个一个等，白白浪费了时间。&lt;/p&gt;&#xA;&lt;p&gt;这篇文章从事件循环讲起，搞清楚 &lt;code&gt;await&lt;/code&gt; 和 &lt;code&gt;asyncio.create_task&lt;/code&gt; 的本质区别，最后落到 Agent 开发中的实际用法。&lt;/p&gt;&#xA;&lt;h2 id=&#34;事件循环asyncio-的心脏&#34;&gt;事件循环：asyncio 的心脏&lt;/h2&gt;&#xA;&lt;p&gt;事件循环（Event Loop）是 asyncio 的核心调度器。你可以把它想象成一个单线程的任务调度中心——它维护一个任务队列，不断地检查：哪个任务可以往前推进了？&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; asyncio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;say&lt;/span&gt;(msg, delay):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; asyncio&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(delay)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(msg)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; say(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; say(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;asyncio&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;run(main())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 总耗时 2 秒：hello(1s) -&amp;gt; world(1s)，串行执行&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;asyncio.run(main())&lt;/code&gt; 做了三件事：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;创建一个事件循环&lt;/li&gt;&#xA;&lt;li&gt;把 &lt;code&gt;main()&lt;/code&gt; 作为入口协程扔进去&lt;/li&gt;&#xA;&lt;li&gt;驱动事件循环直到 &lt;code&gt;main()&lt;/code&gt; 完成&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;关键点：&lt;strong&gt;事件循环是单线程的&lt;/strong&gt;。它不是靠多线程实现并发，而是靠&amp;quot;在等待 IO 的时候切换到别的任务&amp;quot;来实现并发。当一个协程 &lt;code&gt;await asyncio.sleep(1)&lt;/code&gt; 的时候，事件循环知道这个任务要等 1 秒，就去执行别的任务了。&lt;/p&gt;&#xA;&lt;h2 id=&#34;await挂起当前协程等结果回来&#34;&gt;await：挂起当前协程，等结果回来&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;await&lt;/code&gt; 的语义很明确：&lt;strong&gt;挂起当前协程，等待目标协程完成，拿到返回值后继续往下走。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch_weather&lt;/span&gt;(city: str) &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; dict:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; asyncio&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sleep(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)  &lt;span style=&#34;color:#75715e&#34;&gt;# 模拟 API 调用&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: city, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;temp&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;22°C&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 串行：先查北京，等结果回来，再查上海&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    beijing &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; fetch_weather(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;北京&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shanghai &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; fetch_weather(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;上海&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(beijing, shanghai)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 总耗时 2 秒&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这就像你在餐厅点菜，跟服务员说&amp;quot;先上第一道菜，等我吃完了再上第二道&amp;quot;。效率很低，但逻辑简单，适合有依赖关系的场景。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
