<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>小遨的个人博客 · 原创文章</title>
    <link>https://blog.pengfanao.top</link>
    <description>技术、工具与成长主题的原创文章更新。</description>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 22 Sep 2026 19:10:00 GMT</lastBuildDate>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/09/22/%E5%B7%A5%E5%85%B7/Codex%E6%9B%B4%E6%8D%A2%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%BB%E9%A2%98/</guid>
      <title>Codex更换自定义主题</title>
      <link>https://blog.pengfanao.top/2026/09/22/%E5%B7%A5%E5%85%B7/Codex%E6%9B%B4%E6%8D%A2%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%BB%E9%A2%98/</link>
      <pubDate>Tue, 22 Sep 2026 19:10:00 GMT</pubDate>
      <category>Codex</category><category>macOS</category><category>DreamSkin</category><category>FFmpeg</category><category>WebP</category>
      <description><![CDATA[<p>最近给 Codex 换了一套背景。折腾下来，静态主题其实很简单，真正费时间的是动态背景：原视频很清楚，一转成 WebP，体积和画质就开始打架。</p>
<p>这篇把完整流程记下来。只想换现成主题的话，前半部分几分钟就能完成；想自己做动态主题，再往后看 FFmpeg 那一段。</p>
<h2 id="一、安装-DreamSkin"><a href="#一、安装-DreamSkin" class="headerlink" title="一、安装 DreamSkin"></a>一、安装 DreamSkin</h2><p>DreamSkin 是一款第三方 Codex 桌面端主题工具。它通过本地 CDP 注入主题，不需要修改 Codex 官方应用包。</p>
<p>先打开 <a href="https://dreamskin.cc/">DreamSkin 官网</a>，或者直接进入 <a href="https://dreamskin.cc/download">下载页面</a> 下载安装包。</p>
<p>在 macOS 上安装时，建议先把 Codex（旧版本界面里可能显示为 ChatGPT）彻底退出。不是只关窗口，而是在菜单栏或程序坞中选择“退出”。否则旧进程还在运行，主题引擎可能无法正确安装或加载。</p>
<p>安装步骤很普通：</p>
<ol>
<li>下载并打开 DMG。</li>
<li>将 DreamSkin 拖入“应用程序”。</li>
<li>启动 DreamSkin，按提示授予必要权限。</li>
</ol>
<h2 id="二、安装或升级主题引擎"><a href="#二、安装或升级主题引擎" class="headerlink" title="二、安装或升级主题引擎"></a>二、安装或升级主题引擎</h2><p>第一次启动后，打开 Mac 顶部菜单栏里的 DreamSkin 图标，依次选择：</p>
<blockquote>
<p>维护 → 修复 &#x2F; 重新安装引擎…</p>
</blockquote>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/codex-clipboard-a8649369-eecf-46e4-a19d-feb953fbba6a.png" alt="在 DreamSkin 菜单中修复或重新安装主题引擎"></p>
<p>这一步会把当前版本的主题引擎装好。以后 Codex 更新后，如果主题突然失效，也可以先回到这里重新安装引擎，再重新应用一次皮肤。</p>
<h2 id="三、一键应用主题"><a href="#三、一键应用主题" class="headerlink" title="三、一键应用主题"></a>三、一键应用主题</h2><p>接着进入 DreamSkin 官方主题库。我做的这款主题已经发布，可以直接打开：</p>
<p><a href="https://dreamskin.cc/themes/ver_04ccc15583f908ce0673">查看并安装这款 Codex 主题</a></p>
<p>在主题详情页点击“一键换肤”，然后回到 DreamSkin 应用皮肤即可。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/codex-clipboard-ea70377f-193e-408b-832e-9b16d74131a8.png" alt="主题应用后的 Codex 界面"></p>
<p>如果页面按钮没有拉起 DreamSkin，先确认应用已经启动，再试一次。主题装好后通常不需要重启 Mac，重新应用皮肤或重开 Codex 就够了。</p>
<h2 id="四、动态主题为什么要先转成-WebP"><a href="#四、动态主题为什么要先转成-WebP" class="headerlink" title="四、动态主题为什么要先转成 WebP"></a>四、动态主题为什么要先转成 WebP</h2><p>我后来又做了一版动态背景，源文件是一段 MP4。这里不能直接把视频改名成 <code>background.webp</code>，因为两种文件的内容格式完全不同。</p>
<p>DreamSkin 当前的主题包只识别 <code>background.png</code>、<code>background.jpg</code> 和 <code>background.webp</code>。引擎读取图片后，将它变成 Blob URL，再作为 CSS 背景交给 Codex 界面显示。MP4 是视频容器，现有链路里没有负责创建 <code>&lt;video&gt;</code>、自动播放和循环控制的代码。</p>
<p>动态 WebP 正好卡在中间：对引擎来说它仍然是一张图片，对用户来说它又能播放多帧动画。因此目前最省事的办法，就是用 FFmpeg 把 MP4 抽帧、缩放，再编码成动态 WebP。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/dreamskin-flow.png" alt="MP4 转成 DreamSkin 动态主题的处理流程"></p>
<h2 id="五、我实际使用的-FFmpeg-命令"><a href="#五、我实际使用的-FFmpeg-命令" class="headerlink" title="五、我实际使用的 FFmpeg 命令"></a>五、我实际使用的 FFmpeg 命令</h2><p>先安装 FFmpeg：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install ffmpeg</span><br></pre></td></tr></table></figure>

<p>然后执行下面这条命令。把输入和输出路径换成自己的即可：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">ffmpeg -y \</span><br><span class="line">  -i <span class="string">&quot;/path/input.mp4&quot;</span> \</span><br><span class="line">  -an \</span><br><span class="line">  -vf <span class="string">&quot;fps=15,scale=1920:1080:force_original_aspect_ratio=increase:flags=lanczos,crop=1920:1080,setpts=PTS-STARTPTS&quot;</span> \</span><br><span class="line">  -c:v libwebp_anim \</span><br><span class="line">  -lossless 0 \</span><br><span class="line">  -compression_level 4 \</span><br><span class="line">  -q:v 92 \</span><br><span class="line">  -loop 0 \</span><br><span class="line">  <span class="string">&quot;/path/background.webp&quot;</span></span><br></pre></td></tr></table></figure>

<p>这组参数的意思并不复杂：</p>
<ul>
<li><code>-an</code>：去掉音频，桌面背景用不到声音。</li>
<li><code>fps=15</code>：每秒保留 15 帧。对壁纸来说通常够用，也能明显减小体积。</li>
<li><code>scale</code> 加 <code>crop</code>：先等比放大，再裁成 1920×1080，不拉伸画面。</li>
<li><code>flags=lanczos</code>：缩放时尽量保留细节。</li>
<li><code>-q:v 92</code>：有损 WebP 的画质参数，数字越高越清楚，文件也越大。</li>
<li><code>-loop 0</code>：无限循环播放。</li>
</ul>
<p>我这次做出来的文件是 1920×1080、15 FPS、76 帧、质量 92，最终 8.66 MB，已经能在 Codex 里正常循环。</p>
<h2 id="六、为什么必须小于-10-MiB"><a href="#六、为什么必须小于-10-MiB" class="headerlink" title="六、为什么必须小于 10 MiB"></a>六、为什么必须小于 10 MiB</h2><p>这个限制不是经验值，而是 DreamSkin 当前源码里的硬校验：背景图片最大允许 <code>10 × 1024 × 1024</code> 字节，也就是 <strong>10,485,760 字节</strong>。</p>
<p>超过这个大小，主题在导入或校验时就会被拒绝。所以不要盯着源视频是 40 MB、80 MB 还是 4K。最终文件大小主要由下面几项决定：</p>
<ul>
<li>视频时长；</li>
<li>画面运动和细节多少；</li>
<li>输出分辨率；</li>
<li>帧率；</li>
<li>WebP 质量。</li>
</ul>
<p>同样是 4K 视频，5 秒的慢镜头和 30 秒的快速运动，压出来可能差很多。</p>
<p>如果生成结果超过 10 MiB，我一般按这个顺序调整：先把 <code>q92</code> 降到 <code>q90</code> 或 <code>q88</code>，再把 <code>fps=15</code> 降到 <code>fps=12</code>，最后才把 1920×1080 降到 1600×900。先降分辨率最伤观感，不建议一上来就做。</p>
<p>可以用下面这张表快速选参数：</p>
<table>
<thead>
<tr>
<th>视频时长</th>
<th>建议分辨率</th>
<th>帧率</th>
<th>质量</th>
</tr>
</thead>
<tbody><tr>
<td>5 秒以内</td>
<td>1920×1080</td>
<td>15 FPS</td>
<td>92</td>
</tr>
<tr>
<td>5～8 秒</td>
<td>1920×1080</td>
<td>15 FPS</td>
<td>88～90</td>
</tr>
<tr>
<td>8～12 秒</td>
<td>1920×1080</td>
<td>12 FPS</td>
<td>85～88</td>
</tr>
<tr>
<td>12～20 秒</td>
<td>1600×900</td>
<td>10 FPS</td>
<td>82～86</td>
</tr>
</tbody></table>
<p>这只是起点，不是万能公式。画面越复杂，越需要继续降低质量或帧率。</p>
<h2 id="七、别忘了检查它是不是真的会动"><a href="#七、别忘了检查它是不是真的会动" class="headerlink" title="七、别忘了检查它是不是真的会动"></a>七、别忘了检查它是不是真的会动</h2><p>有些 FFmpeg 构建虽然带有 <code>libwebp_anim</code>，实际输出却可能只有一帧。文件扩展名还是 WebP，但放进主题后只会显示静态画面。</p>
<p>安装 WebP 工具并检查帧数：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">brew install webp</span><br><span class="line">webpmux -info <span class="string">&quot;/path/background.webp&quot;</span></span><br></pre></td></tr></table></figure>

<p>输出里的 <code>Number of frames</code> 必须大于 1。</p>
<p>如果只有一帧，可以改用更稳妥的两步法：先用 FFmpeg 导出序列帧，再交给 <code>img2webp</code> 合成。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /tmp/dreamskin-frames</span><br><span class="line"></span><br><span class="line">ffmpeg -y \</span><br><span class="line">  -i <span class="string">&quot;/path/input.mp4&quot;</span> \</span><br><span class="line">  -an \</span><br><span class="line">  -vf <span class="string">&quot;fps=15,scale=1920:1080:force_original_aspect_ratio=increase:flags=lanczos,crop=1920:1080&quot;</span> \</span><br><span class="line">  <span class="string">&quot;/tmp/dreamskin-frames/frame-%04d.png&quot;</span></span><br><span class="line"></span><br><span class="line">img2webp \</span><br><span class="line">  -lossy -q 92 -d 67 -loop 0 \</span><br><span class="line">  /tmp/dreamskin-frames/frame-*.png \</span><br><span class="line">  -o <span class="string">&quot;/path/background.webp&quot;</span></span><br></pre></td></tr></table></figure>

<p><code>-d 67</code> 表示每帧大约停留 67 毫秒，对应约 15 FPS。生成后再跑一次 <code>webpmux -info</code>，确认帧数和文件大小。</p>
<p>最后，把生成的文件命名为 <code>background.webp</code>，覆盖主题目录中的同名文件，再从 DreamSkin 菜单选择“重新应用皮肤”。只要主题结构没变，后续换视频都可以沿用这套流程。</p>
<h2 id="八、后面准备怎么改"><a href="#八、后面准备怎么改" class="headerlink" title="八、后面准备怎么改"></a>八、后面准备怎么改</h2><p>WebP 方案能用，但画质、体积和帧率必须互相让步。尤其是 2K、4K 视频，为了塞进 10 MiB，损失会比较明显。</p>
<p>后续更合理的方向是直接改 DreamSkin 引擎：</p>
<ol>
<li>在主题包校验中允许 MP4、WebM 等视频资源；</li>
<li>注入一个静音、循环、自动播放的 <code>&lt;video&gt;</code> 背景层；</li>
<li>处理窗口切换、主题卸载和资源释放；</li>
<li>再根据实际内存占用，决定是否放宽 10 MiB 限制。</li>
</ol>
<p>只把 10 MiB 改大并不能解决 MP4 播放，因为“文件能通过校验”和“渲染器会播放视频”是两件事。原生视频支持的改动会比改一个数字大一些，但能绕开动态 WebP 的体积困境，画质和编码效率也会好得多。</p>
<p>现阶段如果只是想快速换一段动态背景，还是建议先用 5～8 秒的短视频、1080p、12～15 FPS。这个区间最容易把体积控制住，实际桌面观感也比较稳。</p>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="https://dreamskin.cc/">DreamSkin 官网</a></li>
<li><a href="https://dreamskin.cc/download">DreamSkin 下载</a></li>
<li><a href="https://dreamskin.cc/themes/ver_04ccc15583f908ce0673">本文使用的主题</a></li>
<li><a href="https://github.com/Fei-Away/Codex-Dream-Skin">DreamSkin 开源仓库</a></li>
</ul>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/09/20/%E5%B7%A5%E5%85%B7/%E5%AE%89%E5%8D%93%E6%8A%95%E5%B1%8Fmac/</guid>
      <title>安卓投屏mac</title>
      <link>https://blog.pengfanao.top/2026/09/20/%E5%B7%A5%E5%85%B7/%E5%AE%89%E5%8D%93%E6%8A%95%E5%B1%8Fmac/</link>
      <pubDate>Sun, 20 Sep 2026 08:59:57 GMT</pubDate>
      
      <description><![CDATA[<p>可以直接下载，不必依赖 Homebrew。你这台 Mac 是 Apple Silicon（<code>arm64</code>），下载官方的 Apple Silicon 版本：</p>
<p><a href="https://github.com/Genymobile/scrcpy/releases/download/v4.1/scrcpy-macos-aarch64-v4.1.tar.gz">下载 scrcpy-macos-aarch64-v4.1.tar.gz</a></p>
<p>它不是 <code>.dmg</code> 安装器，而是官方便携版压缩包。操作方法：</p>
<ol>
<li>下载并双击解压。</li>
<li>打开“终端”，进入解压目录，例如：</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/Downloads/scrcpy-macos-aarch64-v4.1</span><br></pre></td></tr></table></figure>

<ol start="3">
<li>连接安卓手机并打开 USB 调试，然后运行：</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./scrcpy --video-bit-rate=16M --max-size=1920 --max-fps=60 --stay-awake</span><br></pre></td></tr></table></figure>

<p>如果提示无法执行：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x scrcpy</span><br><span class="line">./scrcpy</span><br></pre></td></tr></table></figure>

<p>如果 macOS 阻止打开，到“系统设置 → 隐私与安全性”里允许，或者在 Finder 中右键 <code>scrcpy</code>，选择“打开”。</p>
<p>如果提示找不到 <code>adb</code>，再下载官方 Android Platform Tools：</p>
<p><a href="https://dl.google.com/android/repository/platform-tools-latest-darwin.zip">下载 platform-tools for Mac</a></p>
<p>解压后执行：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/Downloads/platform-tools</span><br><span class="line">./adb devices</span><br></pre></td></tr></table></figure>

<p>看到手机后，用下面方式启动 scrcpy：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/Downloads/scrcpy-macos-aarch64-v4.1</span><br><span class="line">PATH=<span class="string">&quot;<span class="variable">$HOME</span>/Downloads/platform-tools:<span class="variable">$PATH</span>&quot;</span> ./scrcpy \</span><br><span class="line">  --video-bit-rate=16M \</span><br><span class="line">  --max-size=1920 \</span><br><span class="line">  --max-fps=60 \</span><br><span class="line">  --stay-awake</span><br></pre></td></tr></table></figure>

<p>如果想去掉上面点白边，ctrl+c结束后在执行</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">./scrcpy \</span><br><span class="line">  --window-borderless \</span><br><span class="line">  --video-bit-rate=16M \</span><br><span class="line">  --max-size=1920 \</span><br><span class="line">  --max-fps=60 \</span><br><span class="line">  --stay-awake</span><br></pre></td></tr></table></figure>

<p>如果想占满Mac整个屏幕，ctrl+c结束后在执行</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">./scrcpy \</span><br><span class="line">  --window-borderless \</span><br><span class="line">  --always-on-top \</span><br><span class="line">  --video-bit-rate=16M \</span><br><span class="line">  --max-size=1920 \</span><br><span class="line">  --max-fps=60 \</span><br><span class="line">  --stay-awake</span><br></pre></td></tr></table></figure>

<p>你的直播链路直接这样即可：</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">安卓手机 USB → scrcpy → Mac 抖音直播伴侣窗口采集</span><br></pre></td></tr></table></figure>

<p>如果只是 Homebrew 下载超时，直接下载官方压缩包通常更适合你当前情况。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/img.png" alt="img.png"></p>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/22/agent/%E5%BD%93AI%E5%AD%A6%E4%BC%9A%E6%8E%92%E7%89%88-3%E4%B8%AA%E5%BC%80%E6%BA%90Skill%E7%BC%96%E6%8E%92%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%BC%95%E6%93%8E/</guid>
      <title>写一次，博客和公众号草稿同时就绪：我的内容工作流</title>
      <link>https://blog.pengfanao.top/2026/06/22/agent/%E5%BD%93AI%E5%AD%A6%E4%BC%9A%E6%8E%92%E7%89%88-3%E4%B8%AA%E5%BC%80%E6%BA%90Skill%E7%BC%96%E6%8E%92%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%BC%95%E6%93%8E/</link>
      <pubDate>Mon, 22 Jun 2026 02:30:00 GMT</pubDate>
      <category>AI</category><category>技能编排</category><category>md2wechat</category><category>内容工作流</category><category>自动化</category>
      <description><![CDATA[<blockquote>
<p><strong>我真正想省掉的，不是写作，而是写完之后那一串重复劳动。</strong> 维护一份 Markdown，就能得到一篇博客和一篇排好版的公众号草稿；某一步失败，也不必从头再来。</p>
</blockquote>
<p>每次写完文章，我都要重复同一套动作：找封面、处理正文图、上传图床、检查博客，再去公众号后台重新排一遍。</p>
<p>写作只做了一次，搬运却像又写了一次。</p>
<p>于是我把这些步骤串成了一条个人内容工作流。它不替我按下最终发布按钮，但会把成品送到按钮前，并告诉我哪里通过了、哪里应该停。</p>
<blockquote>
<p><strong>一句话看懂：一份内容作为事实源，博客和公众号分别输出，失败从断点继续。</strong></p>
</blockquote>
<h2 id="先看结果：我最终拿到什么"><a href="#先看结果：我最终拿到什么" class="headerlink" title="先看结果：我最终拿到什么"></a>先看结果：我最终拿到什么</h2><p>不是一堆中间文件，而是三个可以继续使用的结果。</p>
<p><strong>博客成品</strong>：标题、摘要、封面、正文图片、来源和分类都完整，Hexo 构建通过后才进入发布流程。</p>
<p><strong>公众号成品</strong>：不是裸 Markdown，而是适合手机阅读的内联样式 HTML；正文图和封面也会进入公众号素材流程。</p>
<p><strong>恢复记录</strong>：文章、图片和草稿状态被保存下来。网络或接口出错时，下次只重试没完成的部分。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/workflow-dual-output-1200x675.png" alt="一份 Markdown 如何变成博客文章和公众号草稿"></p>
<p>这张图就是整套流程：<strong>同一份原稿，走向两个发布端；每个关键步骤前都有检查点。</strong></p>
<h2 id="三个-Skill，不是三个“万能引擎”"><a href="#三个-Skill，不是三个“万能引擎”" class="headerlink" title="三个 Skill，不是三个“万能引擎”"></a>三个 Skill，不是三个“万能引擎”</h2><p>我一开始也把它们讲复杂了。其实三个工具的关系非常简单：一个管全局，一个管排版，一个管送达。</p>
<blockquote>
<p><strong>总控｜wechat-article-generator</strong></p>
<p>它像项目经理加质检员：理解选题、组织文章、规划图片、调用图床、生成 Hexo 文件，并判断下一步该调用谁。它关心的是整件事有没有闭环。</p>
</blockquote>
<blockquote>
<p><strong>排版｜md2wechat</strong></p>
<p>它把 Markdown 转成公众号兼容的 HTML，并负责主题、预览、封面和信息图等排版能力。文章已经写好、只想解决公众号排版的人，用这一段就够了。</p>
</blockquote>
<blockquote>
<p><strong>送达｜baoyu-post-to-wechat</strong></p>
<p>它处理正文图片、封面素材、账号配置和草稿创建。它只负责把成品安全送进编辑后台，默认不会替我发布或群发。</p>
</blockquote>
<p>所以，真正的关系不是“三个 AI 一起写文章”，而是：</p>
<p><strong>总控决定顺序，md2wechat 负责变好看，baoyu 负责放进草稿箱。</strong></p>
<h2 id="稳定，不等于“一键无脑跑完”"><a href="#稳定，不等于“一键无脑跑完”" class="headerlink" title="稳定，不等于“一键无脑跑完”"></a>稳定，不等于“一键无脑跑完”</h2><p>这套工作流真正有用的地方，是把容易忘、容易错的动作变成固定顺序。</p>
<p><strong>先写清楚。</strong> 总控先确认读者、核心结论和信息来源。涉及新闻、项目数据和产品能力时，必须保留来源，不能猜数字。</p>
<p><strong>再让图片解释内容。</strong> 封面负责让读者一眼看懂主题，正文图负责解释流程、架构或故障恢复，不拿无关“科技图”凑数。</p>
<p><strong>图片先进入 OSS。</strong> 博客只引用真实线上地址。上传失败时，流程停止，不偷偷改成本地路径，也不拿默认图顶上去。</p>
<p><strong>两个渠道分别渲染。</strong> 博客保留 Markdown，交给 Hexo；公众号生成全内联样式 HTML，避免进入编辑器后样式丢失。</p>
<p><strong>最后才检查交付。</strong> 封面、正文图、移动端字号、HTML 兼容性和博客构建全部通过，才算完成。</p>
<blockquote>
<p>自动化不是“什么都继续做”，而是<strong>该继续时继续，该停时明确停下。</strong></p>
</blockquote>
<h2 id="图片不是装饰，它要先回答问题"><a href="#图片不是装饰，它要先回答问题" class="headerlink" title="图片不是装饰，它要先回答问题"></a>图片不是装饰，它要先回答问题</h2><p>我对封面的要求只有一个：用户只看这张图，也能知道文章在讲什么。</p>
<p>因此，系统先从标题、摘要和正文里提炼一个主题与两到四个要点，再决定画面结构。图片模型只负责无文字底图或视觉框架，准确中文最后由可控排版叠加。</p>
<p>主标题和关键信息保留 6% 到 8% 的安全边距，缩成手机封面后仍要看得清。</p>
<p>如果生成服务不可用，才会选择同主题、来源明确的真实素材。技术文章只看技术类候选，不会从动漫、风景和人物图里随机抽一张。</p>
<p>这不是“有图就行”，而是四道检查：<strong>主题相关、文字正确、画幅适配、来源可追溯。</strong></p>
<h2 id="真正遇到故障时，会发生什么"><a href="#真正遇到故障时，会发生什么" class="headerlink" title="真正遇到故障时，会发生什么"></a>真正遇到故障时，会发生什么</h2><p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/failure-resume-1200x675.png" alt="内容工作流的失败恢复策略"></p>
<p>我把故障分成三种，不同故障有不同处理方式。</p>
<p><strong>能替代</strong>：封面生成服务不可用，可以换成同主题的真实素材，但必须重新检查构图和授权。</p>
<p><strong>能续跑</strong>：出口 IP 不在白名单，博客和微信 HTML 可以保留；网络恢复后，只重试草稿上传。</p>
<p><strong>必须停</strong>：图片没上传到 OSS、文章检查没通过、草稿条件不满足。此时不能伪造 URL，也不能为了“看起来完成了”而静默降级。</p>
<p><code>state.json</code> 记录文章哈希、素材和草稿结果。它不会让错误自动消失，但会让下一次清楚知道：<strong>哪些已经完成，哪些还没做。</strong></p>
<h2 id="9-月-13-日那次失败，反而说明它有用"><a href="#9-月-13-日那次失败，反而说明它有用" class="headerlink" title="9 月 13 日那次失败，反而说明它有用"></a>9 月 13 日那次失败，反而说明它有用</h2><p>那期 GitHub 周报第一次运行时，博客已经构建并推送成功，公众号却没有出现草稿。</p>
<p>问题不在文章，而在代理改变了出口 IP，公众号令牌接口返回 <code>40164</code>，当前地址不在白名单。</p>
<p>流程没有假装成功。文章、微信 HTML、封面和已上传图片都被保留下来。</p>
<p>关闭代理后，我只重试了草稿阶段：重新检查令牌，上传正文图与封面，创建草稿，再核对标题、正文和阅读原文链接。</p>
<p>没有重新研究，没有重新写稿，也没有再生成一篇博客。</p>
<blockquote>
<p>这比“全自动”更重要：<strong>失败时知道停在哪里，恢复时知道从哪里接着走。</strong></p>
</blockquote>
<h2 id="你不必复刻整套系统"><a href="#你不必复刻整套系统" class="headerlink" title="你不必复刻整套系统"></a>你不必复刻整套系统</h2><p>只做公众号排版，就拿走 <a href="https://github.com/geekjourneyx/md2wechat-skill">md2wechat</a>：Markdown 写完后，用它生成适合公众号的 HTML 和预览。</p>
<p>已经有排好版的 Markdown 或 HTML，只差进入草稿箱，就用 <a href="https://github.com/JimLiu/baoyu-skills/tree/main/skills/baoyu-post-to-wechat">baoyu-post-to-wechat</a>。</p>
<p>同时维护 Hexo、Hugo 等博客和公众号，再借鉴总控思路：保留一份主稿，为两个渠道分别输出，并给图片、排版和构建设置硬检查。</p>
<p>如果只想改善封面，也只需要拿走一条原则：<strong>先让图片说清文章主题，再谈风格好不好看。</strong></p>
<h2 id="文末工具箱"><a href="#文末工具箱" class="headerlink" title="文末工具箱"></a>文末工具箱</h2><ul>
<li><a href="https://github.com/wwrtpfa/wwrtpfa.github.io/tree/master/.codex/skills/wechat-article-generator">wechat-article-generator：我的总控 Skill</a></li>
<li><a href="https://github.com/geekjourneyx/md2wechat-skill">md2wechat：Markdown 转公众号 HTML、预览与视觉生成</a></li>
<li><a href="https://github.com/JimLiu/baoyu-skills/tree/main/skills/baoyu-post-to-wechat">baoyu-post-to-wechat：公众号草稿上传与多通道发布</a></li>
<li><a href="https://github.com/Molunerfinn/PicGo">PicGo：图片上传与图床客户端</a></li>
<li><a href="https://hexo.io/">Hexo：静态博客生成器</a></li>
</ul>
<p>我最后留下的不是一个所有人都要照搬的方案，而是三个可以复用的原则：<strong>一份内容作为事实源，工具各做自己擅长的事，任何发布都要经过可验证的质量门。</strong></p>
<p>如果你也在做内容，你最想先省掉哪一步：排版、配图，还是草稿上传？</p>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/22/agent/LangChain-Agent-03-%E5%AE%9E%E6%88%98%E7%AF%87-%E4%BB%8E%E9%9B%B6%E6%90%AD%E5%BB%BA%E4%BB%A3%E7%A0%81%E5%8A%A9%E6%89%8BAgent/</guid>
      <title>LangChain Agent 系列(三)：从零搭建代码助手 Agent</title>
      <link>https://blog.pengfanao.top/2026/06/22/agent/LangChain-Agent-03-%E5%AE%9E%E6%88%98%E7%AF%87-%E4%BB%8E%E9%9B%B6%E6%90%AD%E5%BB%BA%E4%BB%A3%E7%A0%81%E5%8A%A9%E6%89%8BAgent/</link>
      <pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate>
      <category>LangChain</category><category>Agent</category><category>AI教程</category><category>Python</category><category>实战</category>
      <description><![CDATA[<h2 id="终于到动手环节了。"><a href="#终于到动手环节了。" class="headerlink" title="终于到动手环节了。"></a>终于到动手环节了。</h2><p>前两篇讲了 Agent 的概念和架构，这一篇我们从头写一个能读文件、写代码、执行 Shell 的代码助手 Agent。跟着一步步来，大约 20 分钟能跑通。</p>
<hr>
<h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><p>先装依赖。打开终端，一行搞定：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install <span class="string">&quot;langchain&lt;1.0&quot;</span> langchain-openai</span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>⚠️ 版本说明：</strong> LangChain 1.0+ 移除了 <code>create_tool_calling_agent</code> 和 <code>AgentExecutor</code>（改用 LangGraph 的 <code>create_agent()</code>）。本文基于 0.3.x 版本，<code>langchain&lt;1.0</code> 确保代码可直接运行。学习完成后建议查阅 <a href="https://langchain-ai.github.io/langgraph/">LangGraph Agent 文档</a> 了解最新用法。</p>
</blockquote>
<p>然后创建 <code>agent_demo.py</code>，把 API key 配好：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line">os.environ[<span class="string">&quot;OPENAI_API_KEY&quot;</span>] = <span class="string">&quot;sk-your-key-here&quot;</span>  <span class="comment"># 或从 .env 读取</span></span><br></pre></td></tr></table></figure>

<p>如果你用的是其他模型（比如 DeepSeek、Claude），在创建 LLM 时指定 <code>base_url</code> 即可，后面会讲到。现在先把环境搭好。</p>
<hr>
<h2 id="定义三个工具"><a href="#定义三个工具" class="headerlink" title="定义三个工具"></a>定义三个工具</h2><p>第二篇讲过，工具（<code>BaseTool</code>）是 Agent 的”手和眼睛”。LangChain 提供了一种极其简洁的方式来定义工具——<code>@tool</code> 装饰器。你只需要写一个普通函数，加上类型注解和 docstring，装饰器会自动把它包装成 <code>BaseTool</code>。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_core.tools <span class="keyword">import</span> tool</span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">read_file</span>(<span class="params">path: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;读取指定路径的文件内容。参数 path: 文件的绝对或相对路径&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(path, <span class="string">&quot;r&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">return</span> f.read()</span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">write_file</span>(<span class="params">path: <span class="built_in">str</span>, content: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;写入内容到指定路径的文件。参数 path: 目标文件路径, content: 要写入的内容&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(path, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">        f.write(content)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;已写入 <span class="subst">&#123;path&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">run_shell</span>(<span class="params">command: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;执行 shell 命令并返回输出。参数 command: 要执行的 shell 命令。⚠️ 仅用于本地开发环境&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">import</span> subprocess</span><br><span class="line">    result = subprocess.run(command, shell=<span class="literal">True</span>, capture_output=<span class="literal">True</span>, text=<span class="literal">True</span>)</span><br><span class="line">    <span class="keyword">return</span> result.stdout <span class="keyword">or</span> result.stderr</span><br><span class="line"></span><br><span class="line">tools = [read_file, write_file, run_shell]</span><br></pre></td></tr></table></figure>

<blockquote>
<p>⚠️ <strong>安全提醒：</strong> <code>run_shell</code> 中使用了 <code>shell=True</code>，这意味着任何能调用此工具的人都可以在你的机器上执行任意命令。本文的代码仅用于本地开发学习，<strong>绝对不能</strong>部署到生产环境或暴露给不信任的用户。生产环境应使用沙箱（如 Docker 容器）或在 Agent prompt 中限制可执行的命令范围。</p>
</blockquote>
<p>就这三段代码，Agent 就有了读文件、写文件、执行命令的能力。来拆一下每行在干什么。</p>
<h3 id="tool-装饰器做了什么"><a href="#tool-装饰器做了什么" class="headerlink" title="@tool 装饰器做了什么"></a><code>@tool</code> 装饰器做了什么</h3><p>回忆第二篇讲的 <code>BaseTool</code> 四个核心属性：<code>name</code>、<code>description</code>、<code>args_schema</code>、<code>_run()</code>。<code>@tool</code> 装饰器做的事情就是把这四个属性从你的函数里自动提取出来：</p>
<ul>
<li><strong><code>name</code></strong>：取函数名。<code>read_file</code> 函数 → 工具名就是 <code>&quot;read_file&quot;</code></li>
<li><strong><code>description</code></strong>：取 docstring。LLM 读这个描述来决定”什么时候该用这个工具”</li>
<li><strong><code>args_schema</code></strong>：根据类型注解自动生成 Pydantic model。<code>path: str</code> → 参数定义为一个 <code>str</code> 类型的字段。有了这个 schema，LLM 才知道调用 <code>read_file</code> 需要传一个叫 <code>path</code> 的字符串参数</li>
<li><strong><code>_run()</code></strong>：就是你的函数体本身</li>
</ul>
<p><code>run_shell</code> 的 docstring 里加了一个 <code>⚠️ 仅用于本地开发环境</code> 的安全提示。这不是给开发者看的注释，而是<strong>给 LLM 看的</strong>——Agent 在决策时会读到这段描述，相当于在 prompt 里内置了一条安全提醒。</p>
<p>最后，<code>tools = [read_file, write_file, run_shell]</code> 把三个工具装进列表。这就是后面要传给 Agent 的工具清单。</p>
<hr>
<h2 id="创建-Agent"><a href="#创建-Agent" class="headerlink" title="创建 Agent"></a>创建 Agent</h2><p>有了工具，接下来创建 Agent 本身。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_openai <span class="keyword">import</span> ChatOpenAI</span><br><span class="line"><span class="keyword">from</span> langchain.agents <span class="keyword">import</span> create_tool_calling_agent, AgentExecutor</span><br><span class="line"><span class="keyword">from</span> langchain_core.prompts <span class="keyword">import</span> ChatPromptTemplate, MessagesPlaceholder</span><br><span class="line"></span><br><span class="line">llm = ChatOpenAI(model=<span class="string">&quot;gpt-4o&quot;</span>, temperature=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">system_prompt = (</span><br><span class="line">    <span class="string">&quot;你是一个 Python 代码助手。你可以读取文件、写入文件、执行 Shell 命令。&quot;</span></span><br><span class="line">    <span class="string">&quot;遇到需要操作文件或执行命令的任务时，使用提供的工具。&quot;</span></span><br><span class="line">    <span class="string">&quot;如果执行命令出错，分析错误并尝试修复。&quot;</span></span><br><span class="line">    <span class="string">&quot;完成任务后给出清晰的总结。&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">prompt = ChatPromptTemplate.from_messages([</span><br><span class="line">    (<span class="string">&quot;system&quot;</span>, system_prompt),</span><br><span class="line">    (<span class="string">&quot;human&quot;</span>, <span class="string">&quot;&#123;input&#125;&quot;</span>),</span><br><span class="line">    MessagesPlaceholder(<span class="string">&quot;agent_scratchpad&quot;</span>),</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">agent = create_tool_calling_agent(llm, tools, prompt)</span><br><span class="line">executor = AgentExecutor(agent=agent, tools=tools, verbose=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure>

<p>不到 10 行，Agent 就创建好了。下面逐行解释。</p>
<h3 id="为什么用-create-tool-calling-agent"><a href="#为什么用-create-tool-calling-agent" class="headerlink" title="为什么用 create_tool_calling_agent"></a>为什么用 <code>create_tool_calling_agent</code></h3><p>第二篇我们对比过 Agent 的几种类型——ReAct、Tool Calling、OpenAI Tools、JSON&#x2F;XML Agent。这里选 <strong>Tool Calling</strong>，因为 <code>gpt-4o</code> 原生支持 function calling，LLM 会直接返回结构化的 <code>tool_calls</code> 对象，不靠文本解析。比 ReAct 更可靠，比 OpenAI Tools 更通用。</p>
<p><code>create_tool_calling_agent</code> 内部做的事情就是把 Prompt 模板、工具列表、LLM 和 OutputParser 串成一条完整的 Agent 管道——Agent 本身就是一个 Runnable。我们手动构建了 <code>ChatPromptTemplate</code>，其中 <code>MessagesPlaceholder(&quot;agent_scratchpad&quot;)</code> 是 Think→Act→Observe 循环中的”历史记录”插槽，每次迭代自动填充最新步骤。你不需要处理 tool_calls 解析，LangChain 都帮你了。</p>
<h3 id="system-prompt-的作用"><a href="#system-prompt-的作用" class="headerlink" title="system_prompt 的作用"></a><code>system_prompt</code> 的作用</h3><p>这四句话是 Agent 的”任务说明书”：</p>
<ol>
<li><strong>定义角色</strong>：”你是一个 Python 代码助手”——告诉 LLM 它的身份</li>
<li><strong>告知工具</strong>：”你可以读取文件、写入文件、执行 Shell 命令”——让 LLM 知道它有什么能力</li>
<li><strong>错误处理指令</strong>：”如果执行命令出错，分析错误并尝试修复”——激活 Agent 的自我纠错机制</li>
<li><strong>输出要求</strong>：”完成任务后给出清晰的总结”——明确交付标准</li>
</ol>
<h3 id="AgentExecutor——Think-→-Act-→-Observe-的引擎"><a href="#AgentExecutor——Think-→-Act-→-Observe-的引擎" class="headerlink" title="AgentExecutor——Think → Act → Observe 的引擎"></a><code>AgentExecutor</code>——Think → Act → Observe 的引擎</h3><p><code>AgentExecutor</code> 不参与推理，不执行工具。它是总调度器，职责就是跑循环：</p>
<ol>
<li>把 prompt 发给 LLM（Think）</li>
<li>拿到输出，如果是 <code>AgentAction</code>，找到对应的工具执行（Act）</li>
<li>把工具返回的结果作为 Observation 填回历史（Observe）</li>
<li>判断：继续循环还是停止</li>
<li>直到 LLM 输出 <code>AgentFinish</code>，返回最终答案</li>
</ol>
<p><code>verbose=True</code> 让每一步都打印出来——你会亲眼看到 Think → Act → Observe 的完整过程。这正是第二篇讲的 Callbacks 机制：<code>on_agent_action</code>、<code>on_tool_start</code>、<code>on_tool_end</code> 等钩子在每一步触发，把日志打印到控制台。</p>
<blockquote>
<p><strong>一点说明</strong>：<code>AgentExecutor</code> 在 LangChain v1 中已进入维护模式，官方推荐的新方案是基于 LangGraph 的 <code>create_agent()</code>。但 <code>AgentExecutor</code> 仍然是学习 Agent 循环机制的最佳选择——它把这个循环暴露得清清楚楚，每个步骤都能看到。理解了它，切到新 API 只是换个函数名。</p>
</blockquote>
<hr>
<h2 id="跑起来——第一个示例"><a href="#跑起来——第一个示例" class="headerlink" title="跑起来——第一个示例"></a>跑起来——第一个示例</h2><p>把上面的代码全部放到 <code>agent_demo.py</code>，底部加上：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">result = executor.invoke(&#123;</span><br><span class="line">    <span class="string">&quot;input&quot;</span>: <span class="string">&quot;统计当前目录下所有 .py 文件的行数&quot;</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">print</span>(result[<span class="string">&quot;output&quot;</span>])</span><br></pre></td></tr></table></figure>

<p>然后：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python agent_demo.py</span><br></pre></td></tr></table></figure>

<p>你会看到类似这样的输出（这是我实际运行时 <code>verbose=true</code> 打印的日志，加了注释标注每个阶段）：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&gt; Entering new AgentExecutor chain...</span><br><span class="line"></span><br><span class="line">🧠 Think: 我需要先列出目录下的 .py 文件</span><br><span class="line">🔧 Action: run_shell</span><br><span class="line">📋 Action Input: &#123;&quot;command&quot;: &quot;ls /Users/pengfanao/code/wwrtpfa.github.io/*.py&quot;&#125;</span><br><span class="line"></span><br><span class="line">👁️ Observation: scripts/create_post.py scripts/fetch_github.py</span><br><span class="line"></span><br><span class="line">🧠 Think: 有2个文件，现在统计每个文件的行数</span><br><span class="line">🔧 Action: run_shell</span><br><span class="line">📋 Action Input: &#123;&quot;command&quot;: &quot;wc -l scripts/create_post.py scripts/fetch_github.py&quot;&#125;</span><br><span class="line"></span><br><span class="line">👁️ Observation: 45 scripts/create_post.py, 102 scripts/fetch_github.py, 147 total</span><br><span class="line"></span><br><span class="line">🧠 Think: 信息够了</span><br><span class="line">✅ Final Answer: 当前目录下共 2 个 .py 文件，合计 147 行代码。</span><br><span class="line"></span><br><span class="line">&gt; Finished chain.</span><br></pre></td></tr></table></figure>

<p>整个过程经历了 <strong>2 轮 Think → Act → Observe</strong>：</p>
<ol>
<li>第一轮：Agent 思考”需要先列出文件” → 执行 <code>ls *.py</code> → 拿到文件名列表</li>
<li>第二轮：Agent 看到有 2 个文件，决定统计行数 → 执行 <code>wc -l</code> → 拿到统计结果</li>
<li>信息足够，Agent 输出最终答案</li>
</ol>
<p>注意 Agent <strong>不是一次性想好所有步骤</strong>的——它每一步都观察上一步的结果，再决定下一步做什么。这和你在终端里手动操作没有区别：先 <code>ls</code> 看看有什么，再 <code>wc -l</code> 统计，最后汇总。</p>
<hr>
<h2 id="观察-Agent-自主纠错"><a href="#观察-Agent-自主纠错" class="headerlink" title="观察 Agent 自主纠错"></a>观察 Agent 自主纠错</h2><p>前面的例子太顺利了。来看看 Agent 遇到错误时怎么办——这是它和普通 LLM 最根本的区别。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">result = executor.invoke(&#123;</span><br><span class="line">    <span class="string">&quot;input&quot;</span>: <span class="string">&quot;写一个 Python 脚本 hello.py，打印&#x27;Hello World from Agent!&#x27;，然后执行它。&quot;</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">print</span>(result[<span class="string">&quot;output&quot;</span>])</span><br></pre></td></tr></table></figure>

<p>Agent 的执行过程大概是这样的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&gt; Entering new AgentExecutor chain...</span><br><span class="line"></span><br><span class="line">🧠 Think: 用户要创建一个 Python 脚本并执行。我先写文件，再运行。</span><br><span class="line">🔧 Action: write_file</span><br><span class="line">📋 Action Input: &#123;&quot;path&quot;: &quot;hello.py&quot;, &quot;content&quot;: &quot;print(&#x27;Hello World from Agent!&#x27;)&quot;&#125;</span><br><span class="line"></span><br><span class="line">👁️ Observation: 已写入 hello.py</span><br><span class="line"></span><br><span class="line">🧠 Think: 文件已创建。现在用 python 命令执行它。</span><br><span class="line">🔧 Action: run_shell</span><br><span class="line">📋 Action Input: &#123;&quot;command&quot;: &quot;python hello.py&quot;&#125;</span><br><span class="line"></span><br><span class="line">👁️ Observation: Hello World from Agent!</span><br><span class="line"></span><br><span class="line">🧠 Think: 脚本成功创建并执行完毕。</span><br><span class="line">✅ Final Answer: 任务完成。已创建 hello.py，内容为打印 &#x27;Hello World from Agent!&#x27;，并成功执行，输出无误。</span><br><span class="line"></span><br><span class="line">&gt; Finished chain.</span><br></pre></td></tr></table></figure>

<p>三步完成：写文件 → 执行 → 确认结果。如果执行出错（比如脚本里有语法错误），Agent 的行为会是这样的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">👁️ Observation: SyntaxError: invalid syntax at line 3</span><br><span class="line"></span><br><span class="line">🧠 Think: 脚本报语法错误了。让我读一下文件内容，找出问题。</span><br><span class="line">🔧 Action: read_file</span><br><span class="line">📋 Action Input: &#123;&quot;path&quot;: &quot;hello.py&quot;&#125;</span><br><span class="line"></span><br><span class="line">👁️ Observation: print(&#x27;Hello World from Agent!&#x27;</span><br><span class="line"></span><br><span class="line">🧠 Think: 发现少了一个右括号。修正后重新写入。</span><br><span class="line">🔧 Action: write_file</span><br><span class="line">📋 Action Input: &#123;&quot;path&quot;: &quot;hello.py&quot;, &quot;content&quot;: &quot;print(&#x27;Hello World from Agent!&#x27;)&quot;&#125;</span><br></pre></td></tr></table></figure>

<p>Agent 自己发现了错误 → 自己排查了原因 → 自己修复了 → 继续执行。整个过程不需要你插手。</p>
<p>这就是第二篇讲的<strong>反馈环</strong>的力量。普通 LLM 遇到同样的情况，只能输出一段代码然后说”你试试看能不能跑”——至于跑不跑得通，它不知道，也不关心。</p>
<blockquote>
<p><strong>普通 LLM 只是告诉你”怎么做”，Agent 是帮你”做出来并验证它真的能跑”。</strong></p>
</blockquote>
<hr>
<h2 id="总结：代码与架构的对应关系"><a href="#总结：代码与架构的对应关系" class="headerlink" title="总结：代码与架构的对应关系"></a>总结：代码与架构的对应关系</h2><p>写了这么多代码，现在我们把它映射回第二篇的架构概念。每一行代码，背后都有一个 LangChain 抽象在支撑：</p>
<table>
<thead>
<tr>
<th>代码</th>
<th>架构概念</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td><code>@tool</code> 装饰器</td>
<td><code>BaseTool</code></td>
<td>自动生成 name、description、args_schema、_run()</td>
</tr>
<tr>
<td><code>create_tool_calling_agent()</code></td>
<td><code>Runnable</code></td>
<td>Agent 本身是一个 Runnable，对外暴露 <code>invoke()</code></td>
</tr>
<tr>
<td><code>executor.invoke()</code></td>
<td>触发 Think→Act→Observe 循环</td>
<td>AgentExecutor 是整条链路的总调度</td>
</tr>
<tr>
<td>每行 verbose 日志</td>
<td><code>AgentAction</code> &#x2F; <code>AgentFinish</code></td>
<td>Think 阶段输出决策，Act 阶段执行工具</td>
</tr>
<tr>
<td><code>verbose=True</code></td>
<td><code>Callbacks</code></td>
<td>在关键节点触发钩子，打印日志</td>
</tr>
<tr>
<td><code>tools</code> 列表</td>
<td>工具清单</td>
<td>LLM 根据 tools 的描述决定调用哪个</td>
</tr>
<tr>
<td><code>system_prompt</code></td>
<td>Prompt 模板</td>
<td>定义角色 + 告知工具 + 行为指令</td>
</tr>
</tbody></table>
<hr>
<h2 id="三篇回顾"><a href="#三篇回顾" class="headerlink" title="三篇回顾"></a>三篇回顾</h2><p>到这个系列结束的时刻了。回顾一下我们走过的路：</p>
<p><strong>第一篇——概念篇</strong>：Agent 是什么。LLM 有大脑没身体，Agent &#x3D; LLM + 工具 + 决策循环，核心是 Think → Act → Observe 的 ReAct 循环。</p>
<p><strong>第二篇——架构篇</strong>：LangChain 内部怎么转。三层包结构（core &#x2F; classic &#x2F; v1），五大核心组件（Runnable、BaseTool、Prompt 模板、AgentAction&#x2F;AgentFinish、Callbacks），Agent 类型怎么选。</p>
<p><strong>第三篇——实战篇</strong>：从零搭建代码助手 Agent。三个 <code>@tool</code> 装饰器定义工具，<code>create_tool_calling_agent</code> 创建 Agent，<code>AgentExecutor</code> 驱动循环——不到 50 行代码，Agent 真的跑起来了，能读写文件、执行命令、自主纠错。</p>
<p>从概念到代码，从理解到上手——希望你读完这个系列后，不会再觉得 Agent 是什么神秘的黑魔法。它就是一套设计精巧的循环机制，而你现在已经知道怎么造一个出来了。</p>
<hr>
<p><em>LangChain Agent 系列文章：</em></p>
<ul>
<li>(一) <a href="/2026/06/17/agent/LangChain-Agent-01-%E6%A6%82%E5%BF%B5%E7%AF%87-Agent%E6%98%AF%E4%BB%80%E4%B9%88/">Agent 是什么？</a></li>
<li>(二) <a href="/2026/06/19/agent/LangChain-Agent-02-%E6%9E%B6%E6%9E%84%E7%AF%87-LangChain%E5%86%85%E9%83%A8%E6%80%8E%E4%B9%88%E8%BD%AC%E7%9A%84/">LangChain Agent 内部怎么转的？</a></li>
<li>(三) 从零搭建代码助手 Agent（本文）</li>
</ul>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/21/agent/Claude-Code-Codex-agent-browser-%E6%AE%8B%E7%95%99%E8%BF%9B%E7%A8%8B%E6%8E%92%E6%9F%A5/</guid>
      <title>Mac 发热？AI 可能忘了关浏览器</title>
      <link>https://blog.pengfanao.top/2026/06/21/agent/Claude-Code-Codex-agent-browser-%E6%AE%8B%E7%95%99%E8%BF%9B%E7%A8%8B%E6%8E%92%E6%9F%A5/</link>
      <pubDate>Sun, 21 Jun 2026 17:30:00 GMT</pubDate>
      <category>Claude Code</category><category>Codex</category><category>agent-browser</category><category>Chrome</category><category>CDP</category><category>macOS</category>
      <description><![CDATA[<blockquote>
<p>Claude Code、Codex 明明已经停下来了，Mac 风扇却还在转，内存也迟迟不降。问题可能不在当前会话，而在一批没有正常收尾的 <code>agent-browser</code> daemon 与自动化 Chrome。</p>
</blockquote>
<p>这次我在本机清理了 <strong>8 组遗留的 agent-browser &#x2F; Chrome 后台实例及其子进程</strong>。普通 Chrome 标签页没有动，机器热量很快就降了下来。</p>
<p>这不是一个玄学问题，也不能简单归咎于“Chrome 太吃内存”。真正要理解的是：AI 编程工具、浏览器自动化工具、CDP、daemon 和 session 之间，到底是谁启动了谁，又该由谁负责关掉。</p>
<span id="more"></span>

<h2 id="30-秒结论"><a href="#30-秒结论" class="headerlink" title="30 秒结论"></a>30 秒结论</h2><p>先给赶时间的人答案：</p>
<ul>
<li><code>agent-browser</code> 采用 <strong>CLI + 后台 daemon + Chrome</strong> 的架构，daemon 会故意跨命令常驻，以便下一条指令不用重新启动浏览器。</li>
<li>每个独立 session 都可能拥有自己的浏览器实例。Claude Code、Codex 并发执行多个 agent 或工具任务时，session 数量很容易叠加。</li>
<li>如果任务异常中断、会话被强制停止，或者调用方忘了执行 <code>agent-browser close</code>，后台进程就可能继续存在。</li>
<li>CDP 只是控制通道，不是泄漏的根因。根因是 <strong>浏览器生命周期没有闭环</strong>。</li>
<li>最优先执行官方收尾命令：<code>agent-browser close --all</code>。只有正常关闭失败，才按特征匹配并发送 <code>TERM</code>，最后才考虑 <code>KILL</code>。</li>
<li>长期方案是设置空闲超时、复用 session，并要求每个自动化任务在 <code>finally</code> 或 shell <code>trap</code> 中关闭自己的 session。</li>
</ul>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li><a href="#%E4%B8%80%E7%8E%B0%E8%B1%A1ai-%E5%81%9C%E4%BA%86%E6%B5%8F%E8%A7%88%E5%99%A8%E5%8D%B4%E6%B2%A1%E5%81%9C">一、现象：AI 停了，浏览器却没停</a></li>
<li><a href="#%E4%BA%8C%E6%A0%B9%E5%9B%A0%E5%B8%B8%E9%A9%BB-daemon-%E9%81%87%E4%B8%8A%E5%B9%B6%E5%8F%91-session">二、根因：常驻 daemon 遇上并发 session</a></li>
<li><a href="#%E4%B8%89%E5%85%88%E7%A1%AE%E8%AE%A4%E4%B8%8D%E8%A6%81%E4%B8%8A%E6%9D%A5%E5%B0%B1-kill">三、先确认，不要上来就 kill</a></li>
<li><a href="#%E5%9B%9B%E4%B8%89%E5%B1%82%E6%B8%85%E7%90%86%E5%AE%98%E6%96%B9%E5%85%B3%E9%97%AD%E6%B8%A9%E5%92%8C%E7%BB%88%E6%AD%A2%E5%BC%BA%E5%88%B6%E6%B8%85%E7%90%86">四、三层清理：官方关闭、温和终止、强制清理</a></li>
<li><a href="#%E4%BA%94%E6%80%8E%E6%A0%B7%E4%BF%9D%E8%AF%81%E4%B8%8D%E8%AF%AF%E4%BC%A4%E6%99%AE%E9%80%9A-chrome">五、怎样保证不误伤普通 Chrome</a></li>
<li><a href="#%E5%85%AD%E4%BB%8E%E6%BA%90%E5%A4%B4%E9%A2%84%E9%98%B2%E8%AE%A9%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%97%AD%E7%8E%AF">六、从源头预防：让生命周期闭环</a></li>
<li><a href="#%E4%B8%83%E5%8F%AF%E7%9B%B4%E6%8E%A5%E6%94%B6%E8%97%8F%E7%9A%84%E6%8E%92%E6%9F%A5%E6%B8%85%E5%8D%95">七、可直接收藏的排查清单</a></li>
</ul>
<h2 id="一、现象：AI-停了，浏览器却没停"><a href="#一、现象：AI-停了，浏览器却没停" class="headerlink" title="一、现象：AI 停了，浏览器却没停"></a>一、现象：AI 停了，浏览器却没停</h2><p>典型场景是这样的：</p>
<ul>
<li>你让 Claude Code 或 Codex 通过 skill、MCP、shell 工具打开网页；</li>
<li>自动化任务创建了多个命名 session，甚至并行跑了多个子 agent；</li>
<li>任务完成或会话终止后，终端已经安静；</li>
<li>但“活动监视器”里仍有多组 Chrome、Chrome Helper、GPU、Renderer 或 agent-browser 进程；</li>
<li>合上几次窗口也没用，Mac 继续发热，内存和 CPU 占用居高不下。</li>
</ul>
<p>最迷惑的一点是：这些进程通常没有可见窗口。你看到的普通 Chrome 标签页可能只有几个，但后台自动化 Chrome 使用的是独立临时用户目录，它们和日常浏览器不是同一组实例。</p>
<p>因此，“我已经关掉 Chrome 窗口了”并不能证明自动化浏览器已经退出。</p>
<h2 id="二、根因：常驻-daemon-遇上并发-session"><a href="#二、根因：常驻-daemon-遇上并发-session" class="headerlink" title="二、根因：常驻 daemon 遇上并发 session"></a>二、根因：常驻 daemon 遇上并发 session</h2><p>以本机安装的 <code>agent-browser 0.27.0</code> 为例，官方说明明确写了两件事：</p>
<ul>
<li>它采用 client-daemon 架构，Rust CLI 负责接收命令，Rust daemon 通过 CDP 控制 Chrome；</li>
<li>daemon 会在第一次命令时自动启动，并在后续命令之间保持运行，以换取更快的响应。</li>
</ul>
<p>与此同时，官方 session 文档也说明：每个 session 都有独立的浏览器实例、Cookie、存储、历史记录和认证状态。</p>
<p>这两点放到多 agent 场景里，就形成了下面的链路：</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/02-framework-process-tree.png" alt="Claude Code、Codex 到后台 Chrome 的进程与会话链路"></p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Claude Code / Codex</span><br><span class="line">        │</span><br><span class="line"></span><br><span class="line">        ├─ Agent A → session A → daemon A → Chrome A → Renderer / GPU / Network</span><br><span class="line"></span><br><span class="line">        ├─ Agent B → session B → daemon B → Chrome B → Renderer / GPU / Network</span><br><span class="line"></span><br><span class="line">        └─ Agent C → session C → daemon C → Chrome C → Renderer / GPU / Network</span><br></pre></td></tr></table></figure>

<p>单看一组并不可怕。真正吃资源的是“多组浏览器进程树一起常驻”。一个 Chrome 主进程还会派生渲染、GPU、网络等子进程，所以 8 个遗留实例在活动监视器里看起来可能远不止 8 行。</p>
<h3 id="CDP-是不是罪魁祸首？"><a href="#CDP-是不是罪魁祸首？" class="headerlink" title="CDP 是不是罪魁祸首？"></a>CDP 是不是罪魁祸首？</h3><p>不是。</p>
<p>CDP（Chrome DevTools Protocol）只是自动化工具与 Chrome 之间的控制协议。它负责导航、点击、读取 DOM、截图等操作，但不会替调用方决定“任务什么时候算结束”。</p>
<p>真正缺失的是这条生命周期：</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">创建 session → 使用浏览器 → 任务成功或失败 → 关闭 session → 回收 daemon 和 Chrome</span><br></pre></td></tr></table></figure>

<p>如果最后两步没有执行，前面的资源就可能继续留在后台。</p>
<blockquote>
<p><strong>关键判断：</strong> 不是 AI “还在思考”，而是 AI 启动的外部进程已经脱离了当前对话的可见范围。</p>
</blockquote>
<h2 id="三、先确认，不要上来就-kill"><a href="#三、先确认，不要上来就-kill" class="headerlink" title="三、先确认，不要上来就 kill"></a>三、先确认，不要上来就 kill</h2><p>清理前先分清三件事：有多少活动 session、有哪些 agent-browser daemon、哪些 Chrome 使用了 agent-browser 的临时用户目录。</p>
<h3 id="1-查看-agent-browser-自己记录的活动-session"><a href="#1-查看-agent-browser-自己记录的活动-session" class="headerlink" title="1. 查看 agent-browser 自己记录的活动 session"></a>1. 查看 agent-browser 自己记录的活动 session</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">agent-browser session list</span><br></pre></td></tr></table></figure>

<p>如果版本支持 JSON：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">agent-browser session list --json</span><br></pre></td></tr></table></figure>

<p>这一步最可信，因为它从工具自己的会话视角回答“现在认为谁还活着”。</p>
<h3 id="2-查看-daemon-和自动化-Chrome"><a href="#2-查看-daemon-和自动化-Chrome" class="headerlink" title="2. 查看 daemon 和自动化 Chrome"></a>2. 查看 daemon 和自动化 Chrome</h3><p>macOS 上可以使用：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pgrep -fl <span class="string">&#x27;/agent-browser/bin/agent-browser-darwin-(arm64|x64)( |$)&#x27;</span></span><br><span class="line"></span><br><span class="line">pgrep -fl <span class="string">&#x27;[G]oogle Chrome.*--user-data-dir=[^ ]*/agent-browser-chrome-&#x27;</span></span><br></pre></td></tr></table></figure>

<p>第二条命令的关键不是进程名“Google Chrome”，而是启动参数中的：</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">--user-data-dir=.../agent-browser-chrome-...</span><br></pre></td></tr></table></figure>

<p>它说明这个 Chrome 使用的是 agent-browser 创建的隔离用户目录，而不是你日常使用的默认 Chrome Profile。</p>
<h3 id="3-按-CPU、内存和运行时间查看完整进程树"><a href="#3-按-CPU、内存和运行时间查看完整进程树" class="headerlink" title="3. 按 CPU、内存和运行时间查看完整进程树"></a>3. 按 CPU、内存和运行时间查看完整进程树</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ps -axo pid,ppid,%cpu,%mem,rss,etime,<span class="built_in">command</span> \</span><br><span class="line">  | grep -E <span class="string">&#x27;[a]gent-browser-darwin|[a]gent-browser-chrome-&#x27;</span></span><br></pre></td></tr></table></figure>

<p>字段含义：</p>
<ul>
<li><code>PID</code>：当前进程编号，后续终止时使用。</li>
<li><code>PPID</code>：父进程编号，用来判断 daemon 与 Chrome 的父子关系。</li>
<li><code>%CPU</code>：CPU 占用，观察它是否持续让机器发热。</li>
<li><code>%MEM</code> &#x2F; <code>RSS</code>：内存占用，确认多个实例是否不断叠加。</li>
<li><code>ETIME</code>：已运行时间，判断它是否早已超过当前 AI 任务时长。</li>
<li><code>COMMAND</code>：完整启动命令，确认是否带有 agent-browser 特征参数。</li>
</ul>
<p>不要只看 <code>~/.agent-browser</code> 下有没有 <code>.pid</code>、<code>.sock</code> 文件。它们可能只是过期的 sidecar 文件，并不等于进程仍然存活。进程是否活着，最终以 <code>session list</code>、<code>ps</code> 和 <code>pgrep</code> 为准。</p>
<h2 id="四、三层清理：官方关闭、温和终止、强制清理"><a href="#四、三层清理：官方关闭、温和终止、强制清理" class="headerlink" title="四、三层清理：官方关闭、温和终止、强制清理"></a>四、三层清理：官方关闭、温和终止、强制清理</h2><p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/03-flowchart-cleanup.png" alt="从官方关闭到强制清理的安全操作流程"></p>
<h3 id="第一层：让-agent-browser-自己收尾"><a href="#第一层：让-agent-browser-自己收尾" class="headerlink" title="第一层：让 agent-browser 自己收尾"></a>第一层：让 agent-browser 自己收尾</h3><p>优先执行：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">agent-browser close --all</span><br></pre></td></tr></table></figure>

<p>然后清理过期的 pid、socket、version 等 sidecar 文件：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">agent-browser doctor --offline --quick</span><br></pre></td></tr></table></figure>

<p>当前版本的 <code>doctor</code> 会自动清理陈旧的 daemon sidecar 文件；<code>--fix</code> 还可能执行重装 Chrome、清理旧状态等破坏性修复，因此日常排查先不要加 <code>--fix</code>。</p>
<p>再次验证：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">agent-browser session list</span><br><span class="line"></span><br><span class="line">pgrep -fl <span class="string">&#x27;/agent-browser/bin/agent-browser-darwin-(arm64|x64)( |$)&#x27;</span></span><br><span class="line"></span><br><span class="line">pgrep -fl <span class="string">&#x27;[G]oogle Chrome.*--user-data-dir=[^ ]*/agent-browser-chrome-&#x27;</span></span><br></pre></td></tr></table></figure>

<p>如果没有输出，说明已经清干净。</p>
<h3 id="第二层：发送-TERM，让进程有机会优雅退出"><a href="#第二层：发送-TERM，让进程有机会优雅退出" class="headerlink" title="第二层：发送 TERM，让进程有机会优雅退出"></a>第二层：发送 TERM，让进程有机会优雅退出</h3><p>只有官方关闭失败时再执行：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">pgrep -f <span class="string">&#x27;/agent-browser/bin/agent-browser-darwin-(arm64|x64)( |$)&#x27;</span> \</span><br><span class="line">  | xargs -n 1 <span class="built_in">kill</span> -TERM 2&gt;/dev/null || <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">pgrep -f <span class="string">&#x27;[G]oogle Chrome.*--user-data-dir=[^ ]*/agent-browser-chrome-&#x27;</span> \</span><br><span class="line">  | xargs -n 1 <span class="built_in">kill</span> -TERM 2&gt;/dev/null || <span class="literal">true</span></span><br></pre></td></tr></table></figure>

<p><code>TERM</code> 会给进程处理退出、关闭文件和回收子进程的机会。执行后等 3 秒，再跑一次检查命令。</p>
<h3 id="第三层：只对仍残留的目标使用-KILL"><a href="#第三层：只对仍残留的目标使用-KILL" class="headerlink" title="第三层：只对仍残留的目标使用 KILL"></a>第三层：只对仍残留的目标使用 KILL</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">pgrep -f <span class="string">&#x27;/agent-browser/bin/agent-browser-darwin-(arm64|x64)( |$)&#x27;</span> \</span><br><span class="line">  | xargs -n 1 <span class="built_in">kill</span> -KILL 2&gt;/dev/null || <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">pgrep -f <span class="string">&#x27;[G]oogle Chrome.*--user-data-dir=[^ ]*/agent-browser-chrome-&#x27;</span> \</span><br><span class="line">  | xargs -n 1 <span class="built_in">kill</span> -KILL 2&gt;/dev/null || <span class="literal">true</span></span><br></pre></td></tr></table></figure>

<p><code>KILL</code> 不允许进程执行清理逻辑，只适合处理已经卡死、无法响应 <code>TERM</code> 的残留。正在进行的自动化、下载、表单填写和未保存状态都会立即丢失。</p>
<h3 id="仓库里放一个可重复使用的脚本"><a href="#仓库里放一个可重复使用的脚本" class="headerlink" title="仓库里放一个可重复使用的脚本"></a>仓库里放一个可重复使用的脚本</h3><p>本文配套脚本已放在：</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scripts_py/agent_browser_cleanup.sh</span><br></pre></td></tr></table></figure>

<p>使用方式：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">./scripts_py/agent_browser_cleanup.sh status</span><br><span class="line">./scripts_py/agent_browser_cleanup.sh close</span><br><span class="line">./scripts_py/agent_browser_cleanup.sh force</span><br></pre></td></tr></table></figure>

<p>三条命令依次对应：只查看、官方关闭并清理 sidecar、官方关闭失败后的分级强制清理。</p>
<h2 id="五、怎样保证不误伤普通-Chrome"><a href="#五、怎样保证不误伤普通-Chrome" class="headerlink" title="五、怎样保证不误伤普通 Chrome"></a>五、怎样保证不误伤普通 Chrome</h2><p>不要使用下面这种“大锤”：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">killall <span class="string">&#x27;Google Chrome&#x27;</span></span><br><span class="line">pkill -f <span class="string">&#x27;Google Chrome&#x27;</span></span><br></pre></td></tr></table></figure>

<p>它们会把普通 Chrome 窗口和日常标签页一起关掉。</p>
<p>本文脚本采用两层限制：</p>
<ol>
<li>daemon 必须匹配 <code>agent-browser-darwin-arm64</code> 或 <code>agent-browser-darwin-x64</code> 的安装路径；</li>
<li>Chrome 必须带有 <code>--user-data-dir=.../agent-browser-chrome-...</code> 参数。</li>
</ol>
<p>也就是说，它针对的是 agent-browser 创建的隔离 Chrome，不是所有名为 Google Chrome 的进程。</p>
<p>不过还有一个例外需要注意：如果你主动使用 <code>--cdp</code> 或 <code>--auto-connect</code> 连接了日常 Chrome，那么自动化 session 与普通浏览器之间的边界会变得更近。此时先用 <code>session list</code> 和完整命令行确认，不要直接执行强制清理。</p>
<h2 id="六、从源头预防：让生命周期闭环"><a href="#六、从源头预防：让生命周期闭环" class="headerlink" title="六、从源头预防：让生命周期闭环"></a>六、从源头预防：让生命周期闭环</h2><p>清理脚本只是灭火器。更好的办法是让每个任务都负责关掉自己打开的浏览器。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/04-comparison-prevention.png" alt="临时救火与长期防护的对比清单"></p>
<h3 id="1-设置-daemon-空闲自动退出"><a href="#1-设置-daemon-空闲自动退出" class="headerlink" title="1. 设置 daemon 空闲自动退出"></a>1. 设置 daemon 空闲自动退出</h3><p>官方提供了 <code>AGENT_BROWSER_IDLE_TIMEOUT_MS</code>。例如 10 分钟无命令后自动关闭浏览器并退出 daemon：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> AGENT_BROWSER_IDLE_TIMEOUT_MS=600000</span><br></pre></td></tr></table></figure>

<p>长期使用可以写入 <code>~/.zshrc</code>：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;export AGENT_BROWSER_IDLE_TIMEOUT_MS=600000&#x27;</span> &gt;&gt; ~/.zshrc</span><br><span class="line"><span class="built_in">source</span> ~/.zshrc</span><br></pre></td></tr></table></figure>

<p>10 分钟只是一个稳妥起点。频繁执行长任务可以设为 30 分钟；偶尔使用、机器资源紧张时可以设为 3 到 5 分钟。</p>
<h3 id="2-不要为每一步创建新-session"><a href="#2-不要为每一步创建新-session" class="headerlink" title="2. 不要为每一步创建新 session"></a>2. 不要为每一步创建新 session</h3><p>同一个任务尽量复用一个语义明确的 session：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">agent-browser --session blog-research open https://example.com</span><br><span class="line">agent-browser --session blog-research snapshot -i</span><br><span class="line">agent-browser --session blog-research close</span><br></pre></td></tr></table></figure>

<p>并发任务确实需要隔离时，可以创建多个 session，但每个任务结束后都要单独关闭。</p>
<h3 id="3-在-shell-脚本里用-trap-兜底"><a href="#3-在-shell-脚本里用-trap-兜底" class="headerlink" title="3. 在 shell 脚本里用 trap 兜底"></a>3. 在 shell 脚本里用 trap 兜底</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">set</span> -euo pipefail</span><br><span class="line"></span><br><span class="line">SESSION=<span class="string">&quot;research-$$&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">cleanup</span></span>() &#123;</span><br><span class="line">  agent-browser --session <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span> close &gt;/dev/null 2&gt;&amp;1 || <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">trap</span> cleanup EXIT INT TERM</span><br><span class="line"></span><br><span class="line">agent-browser --session <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span> open https://example.com</span><br><span class="line">agent-browser --session <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span> snapshot -i</span><br></pre></td></tr></table></figure>

<p>无论脚本正常结束、报错，还是收到中断信号，<code>cleanup</code> 都会尝试关闭对应 session。</p>
<h3 id="4-给-Claude-Code-Codex-的浏览器-skill-加退出规则"><a href="#4-给-Claude-Code-Codex-的浏览器-skill-加退出规则" class="headerlink" title="4. 给 Claude Code &#x2F; Codex 的浏览器 skill 加退出规则"></a>4. 给 Claude Code &#x2F; Codex 的浏览器 skill 加退出规则</h3><p>长期指令里应明确写上：</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">浏览器自动化任务结束后，必须关闭本任务创建的 agent-browser session。</span><br><span class="line"></span><br><span class="line">成功、失败、取消和超时都要执行清理；不得依赖用户手动回收后台进程。</span><br><span class="line"></span><br><span class="line">并发任务必须记录各自的 session 名称，只关闭自己创建的 session。</span><br></pre></td></tr></table></figure>

<p>这比“任务结束后记得关浏览器”更有效，因为它定义了成功、失败和取消三条路径。</p>
<h3 id="5-定期做轻量检查"><a href="#5-定期做轻量检查" class="headerlink" title="5. 定期做轻量检查"></a>5. 定期做轻量检查</h3><p>如果你经常让多个 agent 并发操作网页，可以在结束工作前运行：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">agent-browser session list</span><br><span class="line">./scripts_py/agent_browser_cleanup.sh status</span><br></pre></td></tr></table></figure>

<p>发现不认识、运行时间异常长的 session，再决定是否关闭。不要把 <code>force</code> 放进高频定时任务，因为它可能打断仍在工作的自动化。</p>
<h2 id="七、可直接收藏的排查清单"><a href="#七、可直接收藏的排查清单" class="headerlink" title="七、可直接收藏的排查清单"></a>七、可直接收藏的排查清单</h2><p>遇到 Mac 突然发热、内存异常时，按这个顺序走：</p>
<ul>
<li>打开“活动监视器”，确认是否有多组无窗口的 Chrome &#x2F; agent-browser。</li>
<li>运行 <code>agent-browser session list</code>，确认活动 session。</li>
<li>用 <code>pgrep -fl</code> 或 <code>ps</code> 检查 daemon 与 <code>agent-browser-chrome-</code> 用户目录。</li>
<li>先执行 <code>agent-browser close --all</code>。</li>
<li>再执行 <code>agent-browser doctor --offline --quick</code> 清理陈旧 sidecar。</li>
<li>等待几秒并复查 CPU、内存和进程。</li>
<li>仍有残留时先 <code>TERM</code>，最后才 <code>KILL</code>。</li>
<li>设置 <code>AGENT_BROWSER_IDLE_TIMEOUT_MS</code>。</li>
<li>在每个自动化脚本或 skill 中加入 <code>finally</code> &#x2F; <code>trap</code> 清理。</li>
<li>避免 <code>killall Google Chrome</code> 这类会误伤普通标签页的命令。</li>
</ul>
<h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>浏览器自动化最容易被忽略的，不是“怎么打开网页”，而是“谁负责关闭网页背后的整棵进程树”。</p>
<p>Claude Code 和 Codex 能把网页操作拆成工具调用，却不会天然替所有第三方工具完成生命周期管理。只要调用链中存在 daemon、session 和独立 Chrome，就应该把清理当成任务的一部分，而不是任务结束后的家务。</p>
<blockquote>
<p><strong>真正可靠的自动化，不只要能启动，还要能在成功、失败和取消时都干净退出。</strong></p>
</blockquote>
<p>如果你的 Mac 也曾在 AI 会话结束后莫名发热，先别急着重启。跑一下本文的检查命令，很可能几分钟就能找到那批“已经下班，但进程还没打卡”的浏览器。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="https://github.com/vercel-labs/agent-browser">agent-browser 官方仓库</a></li>
<li><a href="https://github.com/vercel-labs/agent-browser/blob/main/README.md">agent-browser README：命令、Session 与 Architecture</a></li>
<li><a href="https://developer.chrome.com/blog/chrome-for-testing/">Chrome for Testing 官方介绍</a></li>
<li><a href="https://developer.chrome.com/docs/devtools/remote-debugging/">Chrome DevTools：远程调试</a></li>
</ul>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/19/agent/LangChain-Agent-02-%E6%9E%B6%E6%9E%84%E7%AF%87-LangChain%E5%86%85%E9%83%A8%E6%80%8E%E4%B9%88%E8%BD%AC%E7%9A%84/</guid>
      <title>LangChain Agent 系列(二)：LangChain Agent 内部怎么转的？</title>
      <link>https://blog.pengfanao.top/2026/06/19/agent/LangChain-Agent-02-%E6%9E%B6%E6%9E%84%E7%AF%87-LangChain%E5%86%85%E9%83%A8%E6%80%8E%E4%B9%88%E8%BD%AC%E7%9A%84/</link>
      <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
      <category>LangChain</category><category>Agent</category><category>AI教程</category><category>架构</category>
      <description><![CDATA[<h2 id="上一篇我们讲了-Agent-的”道”——Think-→-Act-→-Observe-循环。这一篇讲”术”——LangChain-是怎么把这套机制实现出来的。"><a href="#上一篇我们讲了-Agent-的”道”——Think-→-Act-→-Observe-循环。这一篇讲”术”——LangChain-是怎么把这套机制实现出来的。" class="headerlink" title="上一篇我们讲了 Agent 的”道”——Think → Act → Observe 循环。这一篇讲”术”——LangChain 是怎么把这套机制实现出来的。"></a>上一篇我们讲了 Agent 的”道”——Think → Act → Observe 循环。这一篇讲”术”——LangChain 是怎么把这套机制实现出来的。</h2><p>如果你读完了第一篇，你已经知道 Agent 的核心思想了：LLM 是大脑，工具是手和眼睛，决策循环让 Agent 不断思考、执行、观察、调整，直到任务完成。</p>
<p>但概念是概念，代码是代码。LangChain 到底用了哪些类、哪些抽象、哪些设计模式，把这些概念变成了能跑的代码？</p>
<p>本文拆解 LangChain 的三层包结构和 Agent 核心组件。读完你能看懂 Agent 的每一行代码在做什么。</p>
<hr>
<h2 id="三层包结构概览"><a href="#三层包结构概览" class="headerlink" title="三层包结构概览"></a>三层包结构概览</h2><p>先把 LangChain 的整体包结构看清楚。很多人一上来就对着几百个类头大，但其实 LangChain 的包结构有清晰的层次逻辑：</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/arch-three-layers.png"></p>
<table>
<thead>
<tr>
<th>层</th>
<th>包 &#x2F; 定位</th>
<th>职责</th>
</tr>
</thead>
<tbody><tr>
<td>基础抽象层</td>
<td><code>langchain-core</code></td>
<td>Runnable 协议、BaseTool、Messages、Callbacks</td>
</tr>
<tr>
<td>经典集成层</td>
<td><code>langchain-classic</code></td>
<td>Chains、100+ 工具集成、Agent 实现、Document Loaders</td>
</tr>
<tr>
<td>新版 API</td>
<td><code>langchain</code> (v1)</td>
<td>精简设计，Agent 优先</td>
</tr>
</tbody></table>
<p>这三层是 LangChain 的核心架构，每一层有各自的定位：</p>
<p><strong>基础抽象层（<code>langchain-core</code>）</strong> 是整个 LangChain 生态的地基。它只定义抽象接口和协议——Runnable 该怎么跑、BaseTool 长什么样、Message 有哪几种类型——但不绑定任何具体的模型提供商。这意味着你可以换模型、换工具实现、换拼接方式，只要接口对得上，别的代码不用动。</p>
<p><strong>经典集成层（<code>langchain-classic</code>）</strong> 是 LangChain 历史最悠久的包，包含了 Chains 编排、100 多个第三方工具集成、各类 Agent 的具体实现、Document Loaders、Vector Stores 等等。注意，在 v1.0 发布后，<code>langchain-classic</code> 已进入维护模式（只修 bug，不添加新功能），活跃的集成开发已迁移到 <code>langchain-openai</code>、<code>langchain-anthropic</code> 等合作包和 <code>langchain-community</code> 中。但作为抽象概念的”经典层”仍然是最好的学习起点——它的代码量和覆盖面能让你看清一个完整的 Agent 生态长什么样。</p>
<p><strong>新版 API（<code>langchain</code> v1）</strong> 是 LangChain 团队近年推行的新方向。”Agent 优先”意味着 Agent 不再是用 Chain 拼出来的”高级功能”，而是一等公民。v1 版本的 API 设计更精简——创建 Agent 更直观，配置项更集中，对工具和模型的绑定也更清晰。如果你是新项目，官方推荐优先考虑 v1。</p>
<p>为什么要拆三层？一句话：<strong>解耦</strong>。<code>langchain-core</code> 不依赖任何模型提供商，核心抽象极其稳定；<code>langchain-classic</code> 可以有大量的具体实现和集成，变更频繁也不影响核心抽象；<code>langchain</code> v1 作为”新家”，可以吸取经典层的教训，用更现代的 API 重新出发。</p>
<hr>
<h2 id="核心组件拆解"><a href="#核心组件拆解" class="headerlink" title="核心组件拆解"></a>核心组件拆解</h2><p>理清了包结构，我们来看 LangChain Agent 的核心组件。这些组件串在一起，就构成了 Agent 的执行引擎。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/arch-components.png"></p>
<h3 id="1-Runnable-协议——一切皆-Runnable"><a href="#1-Runnable-协议——一切皆-Runnable" class="headerlink" title="1. Runnable 协议——一切皆 Runnable"></a>1. Runnable 协议——一切皆 Runnable</h3><p>Runnable 是 LangChain 最底层的抽象。它的核心思想很简单：<strong>一切组件都是 Runnable，统一对外暴露 <code>invoke()</code>（同步执行）和 <code>stream()</code>（流式执行）两个执行入口。</strong></p>
<p>除此之外，Runnable 还提供了 <code>pipe()</code> 方法（即 <code>|</code> 运算符），用于将多个 Runnable 串联成一个管道——这不是执行入口，而是<strong>组合手段</strong>。</p>
<p>在 LangChain 里，以下这些东西全都是 Runnable：</p>
<ul>
<li><strong>LLM</strong>（比如 <code>ChatOpenAI</code>）是一个 Runnable——调用 <code>invoke()</code> 就发一次请求</li>
<li><strong>Prompt 模板</strong>（<code>ChatPromptTemplate</code>）是一个 Runnable——调用 <code>invoke()</code> 就填充变量生成最终 prompt</li>
<li><strong>Tool</strong>（<code>BaseTool</code>）是一个 Runnable——调用 <code>invoke()</code> 就执行工具逻辑</li>
<li><strong>Agent 本身</strong> 也是一个 Runnable——调用 <code>invoke()</code> 就跑完整条 ReAct 循环</li>
</ul>
<p>这意味着你可以用 <code>|</code> 管道符把 Runnable 串起来，像 Unix 管道一样：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">prompt | llm | output_parser</span><br></pre></td></tr></table></figure>

<p>Agent 内部就是靠这种管道把 Prompt、LLM、OutputParser 串联起来的。你不需要在代码里手动编排每一步——LangChain 帮你把组件之间的数据流转处理好了。</p>
<h3 id="2-BaseTool——工具的统一接口"><a href="#2-BaseTool——工具的统一接口" class="headerlink" title="2. BaseTool——工具的统一接口"></a>2. BaseTool——工具的统一接口</h3><p>第一篇我们说了”工具是 Agent 的手和眼睛”，在 LangChain 里，工具都被抽象成 <code>BaseTool</code>。每个 BaseTool 有四个核心属性：</p>
<ul>
<li><strong><code>name</code></strong>：工具的唯一标识名。LLM 用这个名字来引用工具，比如 <code>&quot;read_file&quot;</code>。</li>
<li><strong><code>description</code></strong>：工具的功能描述。LLM 读这个描述来决策”当前应该用哪个工具”。描述写得越准确，LLM 选工具越不容易出错。</li>
<li><strong><code>args_schema</code></strong>：工具参数的 Pydantic 类型定义。有了它，LLM 就知道调用 <code>read_file</code> 需要传一个 <code>file_path: str</code>，类型不对 LangChain 会自动拦截。</li>
<li><strong><code>_run()</code></strong>：工具的实际执行逻辑。你写的业务代码就放在这里。</li>
</ul>
<p>以 <code>read_file</code> 工具为例，它的结构大概是这样的：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_core.tools <span class="keyword">import</span> BaseTool</span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel, Field</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ReadFileInput</span>(<span class="title class_ inherited__">BaseModel</span>):</span><br><span class="line">    file_path: <span class="built_in">str</span> = Field(description=<span class="string">&quot;要读取的文件路径&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ReadFileTool</span>(<span class="title class_ inherited__">BaseTool</span>):</span><br><span class="line">    name: <span class="built_in">str</span> = <span class="string">&quot;read_file&quot;</span></span><br><span class="line">    description: <span class="built_in">str</span> = <span class="string">&quot;读取指定路径的文件内容&quot;</span></span><br><span class="line">    args_schema: <span class="built_in">type</span>[BaseModel] = ReadFileInput</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_run</span>(<span class="params">self, file_path: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(file_path, <span class="string">&quot;r&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">return</span> f.read()</span><br></pre></td></tr></table></figure>

<p>LLM 看到这个工具的 <code>name</code> 和 <code>description</code>，就知道”当用户要我读文件时，调用 <code>read_file</code> 工具，传一个 <code>file_path</code>“。<code>args_schema</code> 里的 Field 描述（<code>&quot;要读取的文件路径&quot;</code>）会一并发给 LLM，帮助它生成正确的参数值。</p>
<h3 id="3-Prompt-模板——Agent-的”任务说明书”"><a href="#3-Prompt-模板——Agent-的”任务说明书”" class="headerlink" title="3. Prompt 模板——Agent 的”任务说明书”"></a>3. Prompt 模板——Agent 的”任务说明书”</h3><p>Agent 的 prompt 跟普通 LLM 的 prompt 不一样。普通 prompt 就是一段话，Agent 的 prompt 是一个<strong>组装出来的复合结构</strong>，包含四个部分：</p>
<ol>
<li><strong>System prompt</strong>：定义角色。比如 “你是一个代码助手，可以用 Shell、文件操作等工具完成任务。遇到问题时先分析，再选择工具执行。”</li>
<li><strong>Tool descriptions</strong>：可用工具的列表——每个工具的 <code>name</code> + <code>description</code> + <code>args_schema</code>。LLM 靠这段文本来决定”现在应该用哪个工具，传什么参数”。</li>
<li><strong>User input</strong>：用户的原始输入，比如 “统计 .py 文件行数”。</li>
<li><strong>Scratchpad</strong>：一个占位符（<code>MessagesPlaceholder</code>），用来存放历史步骤。每完成一轮 Think→Act→Observe，Agent 会把最新的 AgentAction 和 Observation 填进去，让 LLM 知道”到目前为止发生了什么”。</li>
</ol>
<p><code>MessagesPlaceholder</code> 是 Agent prompt 的核心机关。它不是写死的文本，而是一个<strong>每轮循环都会被更新的空位</strong>。第一轮它可能是空的；第二轮它就包含了”上一轮我调用了什么工具、返回了什么结果”。LLM 基于这个不断更新的上下文来决定下一轮该做什么。</p>
<h3 id="4-AgentAction-与-AgentFinish——每步只有两种可能"><a href="#4-AgentAction-与-AgentFinish——每步只有两种可能" class="headerlink" title="4. AgentAction 与 AgentFinish——每步只有两种可能"></a>4. AgentAction 与 AgentFinish——每步只有两种可能</h3><p>Agent 每一步的输出，经过 OutputParser 解析后，只有两种结构化结果：</p>
<ul>
<li><strong><code>AgentAction</code></strong>：Agent 决定再调用一个工具。包含两个字段：<ul>
<li><code>tool</code>：要调用的工具名，比如 <code>&quot;run_shell&quot;</code></li>
<li><code>tool_input</code>：传给工具的参数，比如 <code>&#123;&quot;command&quot;: &quot;ls *.py&quot;&#125;</code></li>
</ul>
</li>
<li><strong><code>AgentFinish</code></strong>：Agent 认为任务已完成，返回最终答案。包含：<ul>
<li><code>return_values</code>：一个字典，通常键是 <code>&quot;output&quot;</code>，值就是给用户的回答</li>
</ul>
</li>
</ul>
<p>OutputParser 的职责就是把 LLM 原始文本输出解析成这两种结构之一。LLM 的输出可能是这样的文本：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Action: run_shell</span><br><span class="line">Action Input: &#123;&quot;command&quot;: &quot;ls *.py&quot;&#125;</span><br></pre></td></tr></table></figure>

<p>OutputParser 需要把它解析成 <code>AgentAction(tool=&quot;run_shell&quot;, tool_input=&#123;&quot;command&quot;: &quot;ls *.py&quot;&#125;)</code>。</p>
<p>或者 LLM 认为任务完成了，输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Final Answer: 共 3 个 Python 文件，合计 214 行代码</span><br></pre></td></tr></table></figure>

<p>OutputParser 就解析成 <code>AgentFinish(return_values=&#123;&quot;output&quot;: &quot;共 3 个 Python 文件，合计 214 行代码&quot;&#125;)</code>。</p>
<p>这个二选一的设计非常简洁——Agent 每步要么再动手，要么收工。AgentExecutor 拿到结果后判断类型：如果是 <code>AgentAction</code>，就执行对应的工具；如果是 <code>AgentFinish</code>，就停止循环返回答案。</p>
<h3 id="5-Callbacks——横向贯穿的钩子机制"><a href="#5-Callbacks——横向贯穿的钩子机制" class="headerlink" title="5. Callbacks——横向贯穿的钩子机制"></a>5. Callbacks——横向贯穿的钩子机制</h3><p>Callback 不属于”纵向串行”的组件，而是一个<strong>横向贯穿</strong>的钩子系统：它在 Agent 执行过程的各个节点插入钩子，让你能拦截和观察每一步。</p>
<p>核心的 callback 钩子包括：</p>
<ul>
<li><strong>LLM 相关</strong>：<code>on_llm_start</code>、<code>on_llm_end</code>、<code>on_llm_error</code>——在 LLM 调用前后触发</li>
<li><strong>Tool 相关</strong>：<code>on_tool_start</code>、<code>on_tool_end</code>、<code>on_tool_error</code>——在工具执行前后触发</li>
<li><strong>Agent 相关</strong>：<code>on_agent_action</code>、<code>on_agent_finish</code>——在 Agent 决策点和结束点触发</li>
</ul>
<p>这些钩子的用途很广：</p>
<ul>
<li><strong>打印日志</strong>：AgentExecutor 的 <code>verbose=True</code> 就是靠 Callback 实现的——每一步的 Think、Act、Observe 都能看到完整输出。</li>
<li><strong>流式输出 token</strong>：<code>on_llm_new_token</code> 钩子让你逐 token 推送内容，实现打字机效果。</li>
<li><strong>写入监控</strong>：记录每次 LLM 调用的耗时、工具执行的成功率，方便后期分析。</li>
</ul>
<p>有了 Callback，你不需要修改 Agent 的核心逻辑，就能在关键节点插入自己的业务代码——这是一个典型的观察者模式。</p>
<hr>
<h2 id="Agent-类型一览"><a href="#Agent-类型一览" class="headerlink" title="Agent 类型一览"></a>Agent 类型一览</h2><p>LangChain 提供了多种 Agent 类型，它们的主要区别在于<strong>如何让 LLM 表达”我要用哪个工具”</strong>。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/arch-agent-types.png"></p>
<table>
<thead>
<tr>
<th>类型</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody><tr>
<td>ReAct</td>
<td>经典推理+行动循环，文本解析 tool 调用</td>
<td>模型不支持原生 tool calling 时</td>
</tr>
<tr>
<td>Tool Calling</td>
<td>通用 tool-calling 接口，跨模型</td>
<td>推荐首选，大多数场景</td>
</tr>
<tr>
<td>OpenAI Tools</td>
<td>用 OpenAI 原生 function calling</td>
<td>仅 OpenAI 模型</td>
</tr>
<tr>
<td>Structured Chat</td>
<td>支持多参数复杂工具的聊天 Agent</td>
<td>工具参数结构复杂时</td>
</tr>
<tr>
<td>JSON &#x2F; XML</td>
<td>用 JSON&#x2F;XML 格式强制结构化输出</td>
<td>需要严格输出格式时</td>
</tr>
</tbody></table>
<p>这些类型的本质差异在于：<strong>LLM 如何输出”我要调用什么工具、传什么参数”这个决策。</strong></p>
<p><strong>ReAct</strong> 是最早的实现方式——它在 prompt 里告诉 LLM：”如果你要用工具，请严格按以下格式输出：<code>Action: 工具名\nAction Input: 参数</code>“。然后 OutputParser 用正则从文本里提取。这种方式最灵活，不依赖模型能力，但解析可能出错——LLM 要是没严格按格式来，正则就匹配不上。</p>
<p><strong>Tool Calling &#x2F; OpenAI Tools</strong> 则是利用模型的原生 function calling 能力。LLM 不再用文本表达工具调用，而是直接返回结构化的 <code>tool_calls</code> 对象——工具名、参数全部是结构化的，不靠文本解析。这种方式更可靠，也是当前推荐的主流做法。Tool Calling Agent 的优点在于它是跨模型的统一接口——ChatAnthropic、ChatVertexAI 等非 OpenAI 模型也可以用。</p>
<p><strong>JSON &#x2F; XML Agent</strong> 让 LLM 按严格的 JSON 或 XML Schema 输出决策。相比于 ReAct 的正则解析，结构化输出更鲁棒，但 prompt 更长，token 消耗更高。</p>
<p><strong>Structured Chat Agent</strong> 则是在聊天场景中支持多参数复杂工具的调用，适合工具函数签名比较复杂的场景。</p>
<p><strong>选型建议</strong>：如果你的模型支持原生 tool calling（目前大部分主流模型都支持），直接用 <strong>Tool Calling Agent</strong>。如果模型不支持或你用的是本地小模型，降级到 <strong>ReAct</strong>。</p>
<hr>
<h2 id="完整链路走一遍"><a href="#完整链路走一遍" class="headerlink" title="完整链路走一遍"></a>完整链路走一遍</h2><p>现在我们把所有组件串起来，用第一篇的同一个例子——“统计目录下 <code>.py</code> 文件的行数”——完整走一遍 LangChain 的执行链路。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/arch-execution-chain.png"></p>
<table>
<thead>
<tr>
<th>步骤</th>
<th>发生的事</th>
<th>LangChain 层面</th>
</tr>
</thead>
<tbody><tr>
<td>用户输入</td>
<td>“统计当前目录下所有 .py 文件行数”</td>
<td><code>executor.invoke(&#123;&quot;input&quot;: &quot;统计当前目录下所有 .py 文件行数&quot;&#125;)</code></td>
</tr>
<tr>
<td>Prompt 组装</td>
<td>System prompt + 工具列表 + 用户输入 + scratchpad（本轮为空）</td>
<td><code>ChatPromptTemplate</code> 把四部分拼好，<code>MessagesPlaceholder</code> 占着 scratchpad 的位置</td>
</tr>
<tr>
<td>LLM 推理</td>
<td>LLM 读取完整 prompt，判断需要先列出所有 .py 文件</td>
<td><code>ChatOpenAI(model=&quot;gpt-4o&quot;)</code> 执行推理，输出结构化 tool_calls</td>
</tr>
<tr>
<td>输出解析</td>
<td>LLM 输出的是 tool_calls，解析成 AgentAction（如果是 ReAct 则解析文本格式）</td>
<td><code>OutputParser</code> 解析输出 → <code>AgentAction(tool=&quot;run_shell&quot;, tool_input=&#123;&quot;command&quot;: &quot;ls *.py&quot;&#125;)</code></td>
</tr>
<tr>
<td>Tool 执行</td>
<td>实际执行 shell 命令</td>
<td><code>run_shell._run(command=&quot;ls *.py&quot;)</code> → 返回 <code>&quot;a.py\nb.py\nutils.py&quot;</code></td>
</tr>
<tr>
<td>历史回填</td>
<td>把本轮的 Action 和 Observation 写入 scratchpad</td>
<td>创建 <code>AgentStep(action=..., observation=...)</code>，填入 <code>MessagesPlaceholder</code></td>
</tr>
<tr>
<td>进入下一轮</td>
<td>LLM 看到本轮结果 + 历史，决定下一步：统计行数</td>
<td>LLM 推理 → OutputParser 输出新 AgentAction</td>
</tr>
<tr>
<td>Tool 执行</td>
<td>执行 wc -l 命令</td>
<td><code>run_shell._run(command=&quot;wc -l *.py&quot;)</code> → 返回统计结果</td>
</tr>
<tr>
<td>历史回填</td>
<td>再次写入 scratchpad</td>
<td>又一个 AgentStep 追加到 MessagesPlaceholder</td>
</tr>
<tr>
<td>下一轮</td>
<td>LLM 判定信息已足够，任务完成</td>
<td>OutputParser → <code>AgentFinish(return_values=&#123;&quot;output&quot;: &quot;共 3 个 Python 文件，合计 214 行代码&quot;&#125;)</code></td>
</tr>
<tr>
<td>返回答案</td>
<td>AgentExecutor 检测到 AgentFinish，停止循环</td>
<td><code>executor.invoke()</code> 返回最终结果给用户</td>
</tr>
</tbody></table>
<p>注意这套流程里几个关键的设计：</p>
<ul>
<li><strong>Prompt 的 scratchpad 是动态增长的</strong>。每轮 Think→Act→Observe 后，新的 AgentStep 追加上去，LLM 始终能看到”从开始到现在发生了什么”，这就是 ReAct 循环的”记忆”。</li>
<li><strong>OutputParser 决定了循环的走向</strong>。它解析出来是 AgentAction 就继续执行工具，解析出来是 AgentFinish 就停止。这相当于循环的”交通指挥灯”。</li>
<li><strong>AgentExecutor 是整条链路的总调度</strong>。它不参与推理，不执行工具——它的职责就是：组装 prompt → 调 LLM → 解析输出 → 调工具 → 回填历史 → 判断继续还是停止 → 循环。</li>
</ul>
<blockquote>
<p><strong>一点说明</strong>：<code>AgentExecutor</code> 在 LangChain v1.0 中已进入维护模式，官方推荐的新方案是基于 LangGraph 的 <code>create_agent()</code>。但 <code>AgentExecutor</code> 的概念模型——调度器 + 循环 + 组件管道——完全适用于新方案。理解了这个模型，你自然就理解了新 API 在做什么。</p>
</blockquote>
<hr>
<h2 id="用一段话串联所有组件"><a href="#用一段话串联所有组件" class="headerlink" title="用一段话串联所有组件"></a>用一段话串联所有组件</h2><p>我们把这些概念串起来的视角再回顾一遍：</p>
<blockquote>
<p><strong>Runnable</strong> 是最外层协议，保证每个组件都有统一的 <code>invoke()</code> 入口。AgentExecutor 这个总调度器，内部用管道把 <strong>Prompt 模板</strong> → <strong>LLM</strong> → <strong>OutputParser</strong> 串联起来。每次循环，Prompt 模板组装 System prompt + Tool descriptions（来自 <strong>BaseTool</strong> 的 name&#x2F;description&#x2F;args_schema）+ User input + Scratchpad（通过 <strong>MessagesPlaceholder</strong> 动态填充历史步骤）。LLM 推理后，<strong>OutputParser</strong> 把结果解析成两种之一：<strong>AgentAction</strong>（继续调工具，包含 tool 名和参数）或 <strong>AgentFinish</strong>（任务完成，返回最终答案）。如果拿到 AgentAction，AgentExecutor 就找到对应的 <strong>BaseTool</strong> 执行 <code>_run()</code>，把结果作为 Observation 回填到 Scratchpad，然后进入下一轮。<strong>Callbacks</strong> 在整个流程中横向贯穿，在每个关键节点触发钩子——你不需要改核心代码就能插入日志、流式输出、监控逻辑。循环直到 OutputParser 吐出 AgentFinish，最终答案返回给用户。</p>
</blockquote>
<hr>
<h2 id="下篇预告"><a href="#下篇预告" class="headerlink" title="下篇预告"></a>下篇预告</h2><p>本文拆解了 LangChain Agent 的内部实现：</p>
<ol>
<li><strong>三层包结构</strong>——<code>langchain-core</code> 定义抽象，<code>langchain-classic</code> 提供实现，<code>langchain v1</code> 是未来方向</li>
<li><strong>五大核心组件</strong>——Runnable 协议、BaseTool、Prompt 模板、AgentAction&#x2F;AgentFinish、Callbacks——分别承担了协议、工具、记忆、决策、监控的职责</li>
<li><strong>Agent 类型选择</strong>——优先 Tool Calling，降级 ReAct</li>
<li><strong>完整执行链路</strong>——从 <code>executor.invoke()</code> 到最终答案，每一步对应的 LangChain 概念</li>
</ol>
<p>但读完一篇可能还不够过瘾。下一篇文章，我们<strong>直接上手写代码</strong>——从零搭建一个可以读写文件、执行命令的代码助手 Agent。你会看到不到 50 行代码，Agent 就真的跑起来了。</p>
<hr>
<p><em>LangChain Agent 系列文章：</em></p>
<ul>
<li>(一) <a href="/2026/06/17/agent/LangChain-Agent-01-%E6%A6%82%E5%BF%B5%E7%AF%87-Agent%E6%98%AF%E4%BB%80%E4%B9%88/">Agent 是什么？</a></li>
<li>(二) LangChain Agent 内部怎么转的？（本文）</li>
<li>(三) <a href="/2026/06/22/agent/LangChain-Agent-03-%E5%AE%9E%E6%88%98%E7%AF%87-%E4%BB%8E%E9%9B%B6%E6%90%AD%E5%BB%BA%E4%BB%A3%E7%A0%81%E5%8A%A9%E6%89%8BAgent/">从零搭建代码助手 Agent</a></li>
</ul>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/17/%E6%88%90%E9%95%BF/%E7%BB%8F%E6%B5%8E%E4%B8%8B%E8%A1%8C%E6%97%B6%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E5%87%BA%E8%B7%AF%E4%B8%8D%E6%98%AF%E7%A1%AC%E6%89%9B/</guid>
      <title>经济下行时，年轻人的出路不是硬扛</title>
      <link>https://blog.pengfanao.top/2026/06/17/%E6%88%90%E9%95%BF/%E7%BB%8F%E6%B5%8E%E4%B8%8B%E8%A1%8C%E6%97%B6%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E5%87%BA%E8%B7%AF%E4%B8%8D%E6%98%AF%E7%A1%AC%E6%89%9B/</link>
      <pubDate>Wed, 17 Jun 2026 18:08:00 GMT</pubDate>
      <category>AI</category><category>经济下行</category><category>年轻人</category><category>职业发展</category><category>副业</category>
      <description><![CDATA[<h1 id="经济下行时，年轻人的出路不是硬扛"><a href="#经济下行时，年轻人的出路不是硬扛" class="headerlink" title="经济下行时，年轻人的出路不是硬扛"></a>经济下行时，年轻人的出路不是硬扛</h1><blockquote>
<p>越是不确定的时候，越不要把人生押在单一答案上。真正的出路不是一句“加油”，而是把自己的抗风险能力一点点做厚。</p>
</blockquote>
<p>这两年，很多年轻人都有一种共同感受：</p>
<p>工作没以前好找了，涨薪没以前确定了，行业变化越来越快，身边人谈论最多的词也从“机会”变成了“稳定”“降本”“收缩”。</p>
<p>这不是某个人不够努力的问题。</p>
<p>国家统计局发布的 2024 年国民经济和社会发展统计公报显示，全年城镇新增就业 1256 万人，就业总量仍在增长；但从普通人的体感看，岗位结构、薪资预期、行业流动和学历竞争都在重排。</p>
<p>所以，今天这篇文章不想讲空泛鸡汤。</p>
<p>我想聊一个更现实的问题：在经济下行周期里，年轻人到底该怎么给自己找出路？</p>
<hr>
<h2 id="一、先承认现实：难，不等于没路"><a href="#一、先承认现实：难，不等于没路" class="headerlink" title="一、先承认现实：难，不等于没路"></a>一、先承认现实：难，不等于没路</h2><p>很多焦虑来自一个误会：以为“出路”必须是某个确定答案。</p>
<p>比如考公、进大厂、读研、创业、出国、做自媒体。</p>
<p>但经济下行时，单一答案反而更脆弱。因为一旦这个答案被挤满、变窄、变慢，人的心态就容易崩。</p>
<p>更好的理解是：</p>
<blockquote>
<p>出路不是一条路，而是一组能力组合。</p>
</blockquote>
<p>你需要的不只是“找到一个岗位”，而是逐渐具备三种能力：</p>
<ul>
<li>能养活自己的现金流能力</li>
<li>能持续升级的专业能力</li>
<li>能在变化中迁移的选择能力</li>
</ul>
<p>这三件事合起来，才是年轻人在低增长环境里的底盘。</p>
<hr>
<h2 id="二、不要再迷信“稳定”，要区分两种稳定"><a href="#二、不要再迷信“稳定”，要区分两种稳定" class="headerlink" title="二、不要再迷信“稳定”，要区分两种稳定"></a>二、不要再迷信“稳定”，要区分两种稳定</h2><p>经济越不确定，越多人会本能地追求稳定。</p>
<p>这当然没错。但问题是，很多人追求的是“外部稳定”，比如单位稳定、合同稳定、平台稳定。</p>
<p>真正更可靠的是“内部稳定”：</p>
<ul>
<li>你是否掌握一门市场愿意付费的技能</li>
<li>你是否有持续学习和复盘的习惯</li>
<li>你是否有 6 到 12 个月的现金缓冲</li>
<li>你是否能从一个行业迁移到另一个行业</li>
<li>你是否能独立完成一个可交付成果</li>
</ul>
<p>外部稳定会受周期影响，内部稳定才跟着你走。</p>
<p>如果只能选一个方向，我更建议年轻人把注意力从“我要进入一个永远安全的地方”，转成“我要成为一个不容易被替代的人”。</p>
<hr>
<h2 id="三、第一条路：把主业做成“可迁移资产”"><a href="#三、第一条路：把主业做成“可迁移资产”" class="headerlink" title="三、第一条路：把主业做成“可迁移资产”"></a>三、第一条路：把主业做成“可迁移资产”</h2><p>很多人一谈出路，就急着辞职、转行、创业。</p>
<p>但大多数年轻人的第一现金流仍然来自主业。下行周期里，主业不是束缚，而是现金流基地。</p>
<p>真正的问题是：你有没有把主业经验沉淀成可迁移资产？</p>
<p>可以用这四个问题检查：</p>
<ul>
<li>我现在做的事，离开这家公司后还值钱吗？</li>
<li>我能不能把工作成果讲成案例，而不是只写岗位职责？</li>
<li>我有没有一套自己总结的方法论、模板、流程或工具？</li>
<li>我做过的项目，能不能证明我解决过真实问题？</li>
</ul>
<p>比如同样是运营岗位，一个人只会写“负责用户增长”，另一个人能讲清楚“如何用三周时间把私域转化率从低点拉回到可接受水平”，后者的经验就更可迁移。</p>
<p>同样是开发岗位，一个人只写“参与系统开发”，另一个人能讲清楚“如何把一个高频接口的响应时间和稳定性做下来”，后者就更像资产。</p>
<p>在经济下行时，简历上最值钱的不是头衔，而是可验证的成果。</p>
<hr>
<h2 id="四、第二条路：学习-AI，但别只学“提示词”"><a href="#四、第二条路：学习-AI，但别只学“提示词”" class="headerlink" title="四、第二条路：学习 AI，但别只学“提示词”"></a>四、第二条路：学习 AI，但别只学“提示词”</h2><p>AI 确实是这一轮变化里最值得重视的变量。</p>
<p>但很多年轻人对 AI 的学习方式有点偏：收藏工具、复制提示词、看别人演示，却没有把它嵌进自己的工作流。</p>
<p>更实用的做法是，把 AI 当成一个“能力放大器”，而不是一个神奇按钮。</p>
<p>你可以从三个层次开始：</p>
<h3 id="1-用-AI-提升表达"><a href="#1-用-AI-提升表达" class="headerlink" title="1. 用 AI 提升表达"></a>1. 用 AI 提升表达</h3><p>写周报、写方案、写简历、写复盘、写邮件。</p>
<p>不是让 AI 替你思考，而是让它帮你把粗糙想法整理得更清晰。</p>
<h3 id="2-用-AI-提升交付"><a href="#2-用-AI-提升交付" class="headerlink" title="2. 用 AI 提升交付"></a>2. 用 AI 提升交付</h3><p>做调研、生成代码、整理表格、搭建自动化脚本、分析用户反馈。</p>
<p>关键是形成自己的固定流程：输入什么、校验什么、输出什么、怎么复用。</p>
<h3 id="3-用-AI-做作品"><a href="#3-用-AI-做作品" class="headerlink" title="3. 用 AI 做作品"></a>3. 用 AI 做作品</h3><p>不要只停留在“我会用”。你要留下作品。</p>
<p>例如一个自动化小工具、一篇系统教程、一个可演示网页、一个数据分析模板、一个行业资料库。</p>
<p>作品比口头能力更抗周期。</p>
<hr>
<h2 id="五、第三条路：用副业练能力，不要用副业逃避现实"><a href="#五、第三条路：用副业练能力，不要用副业逃避现实" class="headerlink" title="五、第三条路：用副业练能力，不要用副业逃避现实"></a>五、第三条路：用副业练能力，不要用副业逃避现实</h2><p>副业不是人人都要做。</p>
<p>尤其在经济压力大的时候，盲目副业很容易变成另一种消耗：白天上班已经很累，晚上再被“搞钱焦虑”追着跑，最后主业没做好，副业也没有结果。</p>
<p>更健康的副业逻辑是：</p>
<blockquote>
<p>先让副业服务能力增长，再让能力带来收入可能。</p>
</blockquote>
<p>适合年轻人的副业不一定是开店、带货、做号，也可以是更小的交付：</p>
<ul>
<li>给熟人做一次简历优化</li>
<li>给本地小店做一页菜单或海报</li>
<li>给团队搭一个自动化表格</li>
<li>把自己的专业经验写成系列文章</li>
<li>接一个低风险的小项目练完整交付</li>
</ul>
<p>副业的第一价值不是赚大钱，而是让你理解市场：别人为什么愿意付费，什么东西才叫交付，沟通成本在哪里，复购靠什么发生。</p>
<p>这些理解，会反过来滋养主业。</p>
<hr>
<h2 id="六、第四条路：降低生活杠杆，给选择留空间"><a href="#六、第四条路：降低生活杠杆，给选择留空间" class="headerlink" title="六、第四条路：降低生活杠杆，给选择留空间"></a>六、第四条路：降低生活杠杆，给选择留空间</h2><p>年轻人的很多痛苦，不只是收入不高，而是选择空间太窄。</p>
<p>房租、贷款、消费分期、情绪性购物、无效社交，把人推到一个状态：只要一个月没收入，就立刻慌。</p>
<p>经济下行时，现金流安全感非常重要。</p>
<p>不是鼓励所有人过苦日子，而是建议你给自己留出缓冲：</p>
<ul>
<li>先攒 3 个月生活费，再追求更高收益</li>
<li>少做长期绑定的大额支出</li>
<li>避免为了面子维持超出收入的生活方式</li>
<li>每月固定复盘钱花在哪里</li>
<li>把“自由现金流”当成一种能力</li>
</ul>
<p>有缓冲的人，才有资格慢慢选。</p>
<p>没有缓冲的人，往往只能被动接受。</p>
<hr>
<h2 id="七、第五条路：别把自己困在同龄人比较里"><a href="#七、第五条路：别把自己困在同龄人比较里" class="headerlink" title="七、第五条路：别把自己困在同龄人比较里"></a>七、第五条路：别把自己困在同龄人比较里</h2><p>经济越难，比较越伤人。</p>
<p>有人上岸了，有人进大厂了，有人创业赚到钱了，有人家里能托底。</p>
<p>但你看到的往往是别人的结果，不是别人的成本。</p>
<p>年轻人最该警惕的，不是暂时落后，而是因为比较失去自己的节奏。</p>
<p>你可以慢一点，但不能停在原地。</p>
<p>你可以普通一点，但要持续积累。</p>
<p>你可以没有光鲜标签，但要有可以拿出来的作品、技能、存款、信用和健康。</p>
<p>这些东西看起来不够刺激，却会在三五年后拉开差距。</p>
<hr>
<h2 id="八、一张简单路线图：未来-12-个月怎么做"><a href="#八、一张简单路线图：未来-12-个月怎么做" class="headerlink" title="八、一张简单路线图：未来 12 个月怎么做"></a>八、一张简单路线图：未来 12 个月怎么做</h2><p>如果你现在很迷茫，可以先按这个顺序来：</p>
<h3 id="第-1-到-2-个月：止血"><a href="#第-1-到-2-个月：止血" class="headerlink" title="第 1 到 2 个月：止血"></a>第 1 到 2 个月：止血</h3><p>整理收支，砍掉不必要开销，建立最低 3 个月生活费目标。</p>
<p>同时复盘主业，把过往项目写成 3 个可讲述案例。</p>
<h3 id="第-3-到-6-个月：升级"><a href="#第-3-到-6-个月：升级" class="headerlink" title="第 3 到 6 个月：升级"></a>第 3 到 6 个月：升级</h3><p>选一个能和主业结合的技能，不要贪多。</p>
<p>比如 AI 自动化、数据分析、写作表达、产品思维、销售转化、编程能力、行业研究。</p>
<p>每个月至少产出一个公开作品。</p>
<h3 id="第-7-到-9-个月：试水"><a href="#第-7-到-9-个月：试水" class="headerlink" title="第 7 到 9 个月：试水"></a>第 7 到 9 个月：试水</h3><p>尝试小规模外部交付。</p>
<p>可以是接单、咨询、内容、开源项目、社群分享，目标不是马上赚钱，而是拿到真实反馈。</p>
<h3 id="第-10-到-12-个月：选择"><a href="#第-10-到-12-个月：选择" class="headerlink" title="第 10 到 12 个月：选择"></a>第 10 到 12 个月：选择</h3><p>根据反馈判断：</p>
<ul>
<li>主业是否还有成长空间</li>
<li>是否值得跳槽</li>
<li>是否需要转向新赛道</li>
<li>副业是否具备持续收入可能</li>
<li>自己的能力资产是否已经变厚</li>
</ul>
<p>这套路线不性感，但可执行。</p>
<p>越是下行周期，越要相信复利，而不是幻想。</p>
<hr>
<h2 id="结尾：年轻人的出路，是重新获得主动权"><a href="#结尾：年轻人的出路，是重新获得主动权" class="headerlink" title="结尾：年轻人的出路，是重新获得主动权"></a>结尾：年轻人的出路，是重新获得主动权</h2><p>经济周期不是普通人能控制的。</p>
<p>但我们仍然可以控制几件事：学习什么，交付什么，花多少钱，认识谁，留下什么作品，如何照顾身体，如何在低谷里保持行动。</p>
<p>我不想说“年轻人一定会越来越好”这种轻飘飘的话。</p>
<p>我更愿意说：</p>
<blockquote>
<p>当外部世界变慢，一个人最重要的能力，是别让自己完全停下来。</p>
</blockquote>
<p>经济下行时，年轻人的出路不是硬扛，也不是躺平。</p>
<p>是在现实里保持清醒，在压力里保留弹性，在一点点积累中重新拿回主动权。</p>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/17/agent/LangChain-Agent-01-%E6%A6%82%E5%BF%B5%E7%AF%87-Agent%E6%98%AF%E4%BB%80%E4%B9%88/</guid>
      <title>LangChain Agent 系列(一)：Agent 是什么？</title>
      <link>https://blog.pengfanao.top/2026/06/17/agent/LangChain-Agent-01-%E6%A6%82%E5%BF%B5%E7%AF%87-Agent%E6%98%AF%E4%BB%80%E4%B9%88/</link>
      <pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate>
      <category>LangChain</category><category>Agent</category><category>LLM</category><category>AI教程</category>
      <description><![CDATA[<h2 id="你让-ChatGPT-帮你统计项目代码行数，它说”抱歉，我做不到”"><a href="#你让-ChatGPT-帮你统计项目代码行数，它说”抱歉，我做不到”" class="headerlink" title="你让 ChatGPT 帮你统计项目代码行数，它说”抱歉，我做不到”"></a>你让 ChatGPT 帮你统计项目代码行数，它说”抱歉，我做不到”</h2><p>这是普通 LLM 最尴尬的时刻——它很聪明，但被困在训练数据的牢笼里。</p>
<p>你或许有过这样的体验：问 ChatGPT 一个问题，它滔滔不绝，逻辑清晰；可一旦让它帮你查一下今天的天气、读一下你桌面上的文件、或者帮你调一下公司的 API——它立刻摊手：”抱歉，我无法访问外部信息。”</p>
<p>这不是它不想帮你，而是它<strong>没有手，也没有眼睛</strong>。</p>
<p>本文是 LangChain Agent 系列的第一篇。我们先用大白话讲清楚 Agent 到底是什么、为什么需要它，不写一行代码。读完你会理解 Agent 的核心思想，为后面的实战打好基础。</p>
<hr>
<h2 id="普通-LLM-的局限"><a href="#普通-LLM-的局限" class="headerlink" title="普通 LLM 的局限"></a>普通 LLM 的局限</h2><p>普通 LLM（大语言模型）本质上是一个”超级知识库 + 文本生成器”。它读过的所有东西都压缩在模型参数里，但它有三个致命短板：</p>
<h3 id="1-无法触及真实世界"><a href="#1-无法触及真实世界" class="headerlink" title="1. 无法触及真实世界"></a>1. 无法触及真实世界</h3><p>LLM 的知识截止于训练数据日期。它不能查文件、不能调 API、不能看天气、不能读数据库——因为它的输入只有一段文本，输出也只有一段文本。</p>
<p>你问它”帮我统计一下当前目录下所有 <code>.py</code> 文件的行数”，它只能说：</p>
<blockquote>
<p>“我无法读取你的文件系统。你可以用 <code>wc -l *.py</code> 命令来统计。”</p>
</blockquote>
<p>它能告诉你<strong>怎么做</strong>，但不能<strong>帮你做</strong>。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/llm-vs-agent.png"></p>
<h3 id="2-复杂任务一步到位出错率高"><a href="#2-复杂任务一步到位出错率高" class="headerlink" title="2. 复杂任务一步到位出错率高"></a>2. 复杂任务一步到位出错率高</h3><p>对于需要多步推理的任务，LLM 必须一次性输出完整答案。没有中间检查，没有回调，没有试错——一步到位。就像让你闭着眼睛解一道数学大题，中间过程全在脑子里算，写错了也没机会改。</p>
<p>结果就是：问题越复杂，正确率越低。</p>
<h3 id="3-无法自我修正——错了就错了"><a href="#3-无法自我修正——错了就错了" class="headerlink" title="3. 无法自我修正——错了就错了"></a>3. 无法自我修正——错了就错了</h3><p>当你指出 LLM 的错误，它可以道歉然后重新输出。但它不会<strong>主动发现</strong>自己错了，更不会<strong>自己去排查</strong>哪里出了问题。它只会根据你的反馈生成新回答，而不是像人一样回过头去检查自己的推理过程。</p>
<p>这三个局限性，用一句话总结就是：</p>
<blockquote>
<p><strong>LLM 有大脑，但没有身体。</strong></p>
</blockquote>
<hr>
<h2 id="Agent-的核心思想"><a href="#Agent-的核心思想" class="headerlink" title="Agent 的核心思想"></a>Agent 的核心思想</h2><p>那有没有办法给 LLM 装上”身体”？这就是 Agent 的由来。</p>
<p>Agent 的核心定义非常简单：</p>
<blockquote>
<p><strong>Agent &#x3D; LLM + 工具 + 决策循环</strong>    可以类比成一个while循环♻️</p>
</blockquote>
<p>类比一下：如果 LLM 是大脑，那工具就是手和眼睛，决策循环就是”先想、再做、再观察、再调整”的行为模式。</p>
<h3 id="工具：Agent-的”手和眼睛”"><a href="#工具：Agent-的”手和眼睛”" class="headerlink" title="工具：Agent 的”手和眼睛”"></a>工具：Agent 的”手和眼睛”</h3><p>工具赋予了 Agent 与外部世界交互的能力：</p>
<ul>
<li><strong>搜网页</strong>：获取最新信息</li>
<li><strong>读文件</strong>：访问本地文件系统</li>
<li><strong>执行代码</strong>：跑 Python、Shell 等脚本</li>
<li><strong>调用 API</strong>：查天气、发邮件、操作数据库</li>
</ul>
<p>有了工具，Agent 不再是被困在训练数据里的”嘴强王者”，而是能<strong>真正干活</strong>的执行者。</p>
<h3 id="决策循环：Think-→-Act-→-Observe"><a href="#决策循环：Think-→-Act-→-Observe" class="headerlink" title="决策循环：Think → Act → Observe"></a>决策循环：Think → Act → Observe</h3><p>如果把 Agent 比作一个实习生，你交给他的任务不会一步完成：</p>
<ol>
<li>你：”帮我查一下上周的销售数据”</li>
<li>实习生思考（Think）：”我需要先找到数据库地址和表结构”</li>
<li>实习生执行（Act）：打开数据库客户端，查看表结构</li>
<li>实习生观察（Observe）：”哦，有个 <code>sales</code> 表，字段是……”</li>
<li>继续思考：”那我需要写个 SQL 查询”</li>
<li>继续执行：写 SQL → 运行 → 看结果</li>
<li>满意了，整理答案交给你</li>
</ol>
<p>Agent 的工作方式完全一样。它就是在一个<strong>循环</strong>里不断思考、执行、观察，直到任务完成。</p>
<p>这就是著名的 <strong>ReAct（Reasoning + Acting）模式</strong>。其中 Think 对应 Reasoning（推理），Act 对应 Acting（行动），而 Observe（观察）是环境返回的结果，它会触发下一轮 Think，这样就形成了闭环。</p>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/reat-cycle.png"></p>
<hr>
<h2 id="ReAct-循环详解"><a href="#ReAct-循环详解" class="headerlink" title="ReAct 循环详解"></a>ReAct 循环详解</h2><p>让我们用”帮我统计目录下 <code>.py</code> 文件的行数”这个任务，完整走一遍 ReAct 循环。</p>
<table>
<thead>
<tr>
<th>步骤</th>
<th>角色</th>
<th>内容</th>
</tr>
</thead>
<tbody><tr>
<td>Think</td>
<td>Agent</td>
<td>“我需要先知道目标目录下有哪些 Python 文件”</td>
</tr>
<tr>
<td>Act</td>
<td>Agent</td>
<td>执行 <code>ls *.py</code></td>
</tr>
<tr>
<td>Observe</td>
<td>环境</td>
<td><code>a.py, b.py, utils.py</code></td>
</tr>
<tr>
<td>Think</td>
<td>Agent</td>
<td>“有 3 个文件，现在统计每个文件的行数”</td>
</tr>
<tr>
<td>Act</td>
<td>Agent</td>
<td>执行 <code>wc -l *.py</code></td>
</tr>
<tr>
<td>Observe</td>
<td>环境</td>
<td><code>45 a.py, 102 b.py, 67 utils.py</code></td>
</tr>
<tr>
<td>Think</td>
<td>Agent</td>
<td>“信息足够了，整理一下输出给用户”</td>
</tr>
<tr>
<td>Answer</td>
<td>Agent</td>
<td>“共 3 个 Python 文件，合计 214 行代码”</td>
</tr>
</tbody></table>
<p>注意这个过程的核心特征：</p>
<ul>
<li><strong>每步有明确目标</strong>：不盲猜，每一步都建立在观察结果之上</li>
<li><strong>结果可验证</strong>：工具的返回结果是真实的、可追溯的，不是 LLM 编造的</li>
<li><strong>循环可中断</strong>：一旦信息足够，就停止循环给出答案</li>
</ul>
<p>这和人类处理任务的方式几乎没有区别——你不可能看到问题就直接写出答案，你也是”想一想→查一查→看一看→再想想”。</p>
<hr>
<h2 id="Agent-的自我纠错"><a href="#Agent-的自我纠错" class="headerlink" title="Agent 的自我纠错"></a>Agent 的自我纠错</h2><p>Agent 最厉害的地方，不是它能用工具，而是<strong>犯错了能自己发现并修复</strong>。这恰恰是普通 LLM 做不到的。</p>
<p>举个例子：</p>
<blockquote>
<p>用户：”帮我连上公司的数据库，查一下今天的订单量。”</p>
</blockquote>
<ol>
<li>Agent 写了连接代码，执行 → <code>Connection refused</code></li>
<li>Agent 思考：”连接被拒绝，可能是端口号写错了？或者是数据库没启动？”</li>
<li>Agent 读配置文件 → 发现写的端口是 3307，实际应该是 3306</li>
<li>Agent 修正端口 → 重新连接 → 成功</li>
<li>Agent 查询订单量 → 返回结果</li>
</ol>
<p><img src="https://pfa-oss-example.oss-cn-hangzhou.aliyuncs.com/blog/self-correct.png"></p>
<p>整个过程不需要用户插手。Agent 自己发现问题、自己排查原因、自己修复、继续执行。</p>
<p>普通 LLM 遇到同样的情况，只能输出一段 SQL 给你，然后说”你自己试试看能不能连上”。至于连接失败怎么办——它不知道，也不关心。</p>
<p>这个差异的本质在于：<strong>Agent 有一个反馈环，LLM 没有。</strong></p>
<p>反馈环让 Agent 的行为从”一次性的文本生成”变成了”持续的、可纠错的执行过程”。这也是 Agent 能做复杂任务的根本原因。</p>
<hr>
<h2 id="Agent-能做什么"><a href="#Agent-能做什么" class="headerlink" title="Agent 能做什么"></a>Agent 能做什么</h2><p>用一张表对比 LLM 和 Agent 的能力差异：</p>
<table>
<thead>
<tr>
<th>能力</th>
<th align="center">普通 LLM</th>
<th align="center">Agent</th>
</tr>
</thead>
<tbody><tr>
<td>生成文本</td>
<td align="center">是</td>
<td align="center">是</td>
</tr>
<tr>
<td>获取实时信息</td>
<td align="center">否</td>
<td align="center">是</td>
</tr>
<tr>
<td>执行代码</td>
<td align="center">否</td>
<td align="center">是</td>
</tr>
<tr>
<td>读写文件</td>
<td align="center">否</td>
<td align="center">是</td>
</tr>
<tr>
<td>调用 API</td>
<td align="center">否</td>
<td align="center">是</td>
</tr>
<tr>
<td>多步推理</td>
<td align="center">有限</td>
<td align="center">是</td>
</tr>
<tr>
<td>自我纠错</td>
<td align="center">否</td>
<td align="center">是</td>
</tr>
<tr>
<td>持续执行任务</td>
<td align="center">否</td>
<td align="center">是</td>
</tr>
</tbody></table>
<p>一句话总结：</p>
<blockquote>
<p><strong>LLM 只能”告诉你”怎么做，Agent 可以”帮你去”做。</strong></p>
</blockquote>
<hr>
<h2 id="下篇预告"><a href="#下篇预告" class="headerlink" title="下篇预告"></a>下篇预告</h2><p>本文用大白话讲清楚了 Agent 的三个核心概念：</p>
<ol>
<li><strong>LLM 的局限</strong>——有大脑没身体</li>
<li><strong>Agent &#x3D; LLM + 工具 + 决策循环</strong>——给大脑装上身体</li>
<li><strong>ReAct 循环</strong>——Think → Act → Observe 的持续执行模式</li>
</ol>
<p>但概念弄清楚之后，下一个问题自然就是——<strong>LangChain 是如何实现这套机制的？</strong></p>
<p>LangChain Agent 的架构可以拆成三层：核心引擎、工具生态、记忆系统。下一篇我们就拆开这座金字塔，看清每一层是怎么运转的。</p>
<hr>
<p><em>LangChain Agent 系列文章：</em></p>
<ul>
<li>(一) Agent 是什么？（本文）</li>
<li>(二) <a href="/2026/06/19/agent/LangChain-Agent-02-%E6%9E%B6%E6%9E%84%E7%AF%87-LangChain%E5%86%85%E9%83%A8%E6%80%8E%E4%B9%88%E8%BD%AC%E7%9A%84/">LangChain Agent 内部怎么转的？</a></li>
<li>(三) <a href="/2026/06/22/agent/LangChain-Agent-03-%E5%AE%9E%E6%88%98%E7%AF%87-%E4%BB%8E%E9%9B%B6%E6%90%AD%E5%BB%BA%E4%BB%A3%E7%A0%81%E5%8A%A9%E6%89%8BAgent/">从零搭建代码助手 Agent</a></li>
</ul>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/10/agent/Comet%20Skill%E2%80%94%E2%80%94%E4%B8%B2%E8%B5%B7%20OpenSpec%20%E4%B8%8E%20Superpowers/</guid>
      <title>Comet Skill——串起 OpenSpec 与 Superpowers，把 Spec Coding 变成稳定工作流</title>
      <link>https://blog.pengfanao.top/2026/06/10/agent/Comet%20Skill%E2%80%94%E2%80%94%E4%B8%B2%E8%B5%B7%20OpenSpec%20%E4%B8%8E%20Superpowers/</link>
      <pubDate>Wed, 10 Jun 2026 10:00:00 GMT</pubDate>
      <category>Claude Code</category><category>AI Agent</category><category>Skill</category><category>Spec Coding</category><category>OpenSpec</category><category>Superpowers</category>
      <description><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li><a href="#%E4%B8%80comet-skill-%E6%98%AF%E4%BB%80%E4%B9%88">一、Comet Skill 是什么</a></li>
<li><a href="#%E4%BA%8C%E4%BB%8E-superpowers-%E5%88%B0-openspecspec-coding-%E7%9A%84%E6%8E%A2%E7%B4%A2%E4%B9%8B%E8%B7%AF">二、从 Superpowers 到 OpenSpec：Spec Coding 的探索之路</a><ul>
<li><a href="#superpowers-%E7%9A%84%E7%97%9B%E7%82%B9">Superpowers 的痛点</a></li>
<li><a href="#openspec-%E7%9A%84%E7%89%B9%E7%82%B9">OpenSpec 的特点</a></li>
<li><a href="#%E7%BB%8F%E5%85%B8%E7%BB%84%E5%90%88%E7%9A%84%E6%91%A9%E6%93%A6">经典组合的摩擦</a></li>
</ul>
</li>
<li><a href="#%E4%B8%89comet-skill-%E7%9A%84%E6%A0%B8%E5%BF%83%E8%AE%BE%E8%AE%A1">三、Comet Skill 的核心设计</a><ul>
<li><a href="#31-%E7%A8%B3%E5%AE%9A%E7%9A%84%E7%8A%B6%E6%80%81%E6%89%AD%E8%BD%AC%E5%BC%95%E5%85%A5%E7%8A%B6%E6%80%81%E6%9C%BA%E6%80%9D%E6%83%B3">3.1 稳定的状态扭转：引入状态机思想</a></li>
<li><a href="#32-%E6%84%8F%E5%9B%BE%E8%AF%86%E5%88%AB%E8%B7%A8%E8%AE%BE%E5%A4%87%E6%96%AD%E7%82%B9%E6%81%A2%E5%A4%8D">3.2 意图识别：跨设备断点恢复</a></li>
<li><a href="#33-%E4%BA%BA%E6%9C%BA%E7%A1%AE%E8%AE%A4%E6%9C%BA%E5%88%B6%E5%85%B3%E9%94%AE%E8%8A%82%E7%82%B9%E4%BA%BA%E5%B7%A5%E6%8A%8A%E6%8E%A7">3.3 人机确认机制：关键节点人工把控</a></li>
<li><a href="#34-%E9%87%8D%E5%8F%A0-spec-%E7%9A%84%E4%BA%A4%E6%8E%A5%E5%BB%BA%E7%AB%8B%E5%8D%95%E5%90%91%E4%BA%8B%E5%AE%9E%E9%93%BE">3.4 重叠 Spec 的交接：建立单向事实链</a></li>
</ul>
</li>
<li><a href="#%E5%9B%9Bharness-engineeringskill-%E4%BE%A7%E7%9A%84%E5%B7%A5%E7%A8%8B%E5%8C%96">四、Harness Engineering：Skill 侧的工程化</a></li>
<li><a href="#%E4%BA%94%E6%80%BB%E7%BB%93">五、总结</a></li>
</ul>
<hr>
<h2 id="一、Comet-Skill-是什么"><a href="#一、Comet-Skill-是什么" class="headerlink" title="一、Comet Skill 是什么"></a>一、Comet Skill 是什么</h2><p>在 AI 驱动开发的浪潮下，<strong>Spec Coding</strong>（规格驱动编码）成为了许多开发者探索的新范式。它的核心理念是：先写清楚”要做什么”（Spec），再让 AI Agent 按照规格执行。</p>
<p>Comet Skill 正是在这个背景下诞生的——它将 <strong>OpenSpec</strong> 和 <strong>Superpowers</strong> 最精华的部分串联起来，打造出更易用、更稳定的 Spec Coding 工作流。</p>
<p>几个关键数据：</p>
<ul>
<li>GitHub 400+ Star</li>
<li>120+ 分钟阅读量</li>
<li>4.5k+ 次下载</li>
<li>经过生产环境验证</li>
</ul>
<hr>
<h2 id="二、从-Superpowers-到-OpenSpec：Spec-Coding-的探索之路"><a href="#二、从-Superpowers-到-OpenSpec：Spec-Coding-的探索之路" class="headerlink" title="二、从 Superpowers 到 OpenSpec：Spec Coding 的探索之路"></a>二、从 Superpowers 到 OpenSpec：Spec Coding 的探索之路</h2><p>Spec Coding 领域有许多优秀工具：Superpowers、Everything Claude Code、OpenSpec、GStack、Spec-kit 等，开发者或多或少都用过其中一两个。但它们各有优劣。</p>
<h3 id="Superpowers-的痛点"><a href="#Superpowers-的痛点" class="headerlink" title="Superpowers 的痛点"></a>Superpowers 的痛点</h3><p>Superpowers 作为老牌高星 Skill，核心能力在于<strong>优秀的头脑风暴和执行力</strong>，但在 Spec 文档管理方面存在明显短板：</p>
<p><strong>1. 上下文压缩问题</strong></p>
<p>当 Spec 文档内容过多时，会触发上下文压缩，导致 Agent 忘记回写文档。在多轮头脑风暴和文档生成过程中，大量 Token 消耗在”恢复现场”上，而实际的文档推进工作却未完成。</p>
<p><strong>2. 文档管理混乱</strong></p>
<p>同一天多次使用 Superpowers 会产生大量文档，这些文档没有明显的完成状态标识，打勾信息散落在文档细节中。开发者需要<strong>人肉扫描文档</strong>来识别完成情况。当存在多个同日期的 Spec 时，识别成本极高，甚至需要借助工具逐行阅读文档才能判断。</p>
<h3 id="OpenSpec-的特点"><a href="#OpenSpec-的特点" class="headerlink" title="OpenSpec 的特点"></a>OpenSpec 的特点</h3><p>OpenSpec 则是一条更轻量的路径。它在完成提案设计后，不采用 worktree 也不强制 TDD，而是更相信 Agent 的能力，让 Agent 根据 Task 一步一步执行。</p>
<p>相比 Superpowers，OpenSpec 的优势在于：</p>
<ul>
<li><strong>优秀的 Spec 资产管理</strong>：拥有非常好的归档能力</li>
<li><strong>丰富的 CLI 命令</strong>：方便 Agent 获取信息</li>
</ul>
<p>但在细节的头脑风暴体验上，它不如 Superpowers 强烈。</p>
<h3 id="经典组合的摩擦"><a href="#经典组合的摩擦" class="headerlink" title="经典组合的摩擦"></a>经典组合的摩擦</h3><p>既然两者各有优劣，经典的使用方法是将它们的强项衔接起来：<strong>用 OpenSpec 做规划和 Spec 归档，用 Superpowers 做深度执行</strong>。</p>
<p>但这种组合也带来了新的摩擦：</p>
<table>
<thead>
<tr>
<th>问题</th>
<th>表现</th>
</tr>
</thead>
<tbody><tr>
<td>命令记忆负担</td>
<td>两套工具的命令需要分别记忆，操作变得繁琐</td>
</tr>
<tr>
<td>自动化断点缺失</td>
<td>执行完 OpenSpec 提案后，需要手动输入 Superpowers 命令执行下一步</td>
</tr>
<tr>
<td>Spec 管理复杂度</td>
<td>引入 OpenSpec 又增加一套 Spec 管理，而 Superpowers 本身 Agent 写完代码忘记文档的问题依然存在</td>
</tr>
</tbody></table>
<hr>
<h2 id="三、Comet-Skill-的核心设计"><a href="#三、Comet-Skill-的核心设计" class="headerlink" title="三、Comet Skill 的核心设计"></a>三、Comet Skill 的核心设计</h2><p>基于上述问题，Comet Skill 围绕四个核心设计展开。</p>
<h3 id="3-1-稳定的状态扭转：引入状态机思想"><a href="#3-1-稳定的状态扭转：引入状态机思想" class="headerlink" title="3.1 稳定的状态扭转：引入状态机思想"></a>3.1 稳定的状态扭转：引入状态机思想</h3><p>Agent 状态管理和文档更新是 Spec Coding 最脆弱的一环。Comet 的解法是<strong>引入状态机思想</strong>。</p>
<p>具体做法：通过 CLI 实现和 bash 脚本，将校验状态和核心文件识别程序化。Agent 不需要知道文件状态的细节，只需按规则执行核心脚本。当需要退出某个阶段时，通过程序判断是否达到条件，而不是依赖 Agent 的”感觉”。</p>
<p>带来的收益：</p>
<ul>
<li>Skill 变得更轻量，需要描述的 Prompt 更少</li>
<li>稳定性大幅提升——程序判断代替模型判断</li>
</ul>
<h3 id="3-2-意图识别：跨设备断点恢复"><a href="#3-2-意图识别：跨设备断点恢复" class="headerlink" title="3.2 意图识别：跨设备断点恢复"></a>3.2 意图识别：跨设备断点恢复</h3><p>Comet 支持跨设备的断点恢复，核心原理是”关键文件检测 + 意图识别”。</p>
<p>意图识别是一个<strong>四层漏斗</strong>，从粗到细收敛用户意图：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">preset 探测 → 活跃 change 发现 → 状态快照读取 → 阶段判定链</span><br></pre></td></tr></table></figure>

<p>设计约束很清晰：<strong>无歧义时自动推进，有歧义时等待用户确认</strong>。</p>
<p>这意味着当开发者重新回到工作区时，无论是否在同一台电脑、窗口是否包含上下文，都不需要重新解释背景。只需执行 <code>comet 继续</code> 命令，就能从核心文件恢复当前状态，继续推进需求。</p>
<h3 id="3-3-人机确认机制：关键节点人工把控"><a href="#3-3-人机确认机制：关键节点人工把控" class="headerlink" title="3.3 人机确认机制：关键节点人工把控"></a>3.3 人机确认机制：关键节点人工把控</h3><p>Comet 并非”一跑到底”的全自动流程。在五阶段流程自动触发的过程中，以下关键节点仍需要人工确认：</p>
<ul>
<li>分支选择</li>
<li>执行方式选择</li>
<li>Spec 调整</li>
</ul>
<p>Comet 的做法是提供默认推荐项，同时依赖 Agent 自主识别选项。这满足了开发者在<strong>生产级工作中对关键节点把控</strong>的需求，避免了”一跑到底”与 Vibe Coding 无差别的问题，确保代码的完整可靠性。</p>
<h3 id="3-4-重叠-Spec-的交接：建立单向事实链"><a href="#3-4-重叠-Spec-的交接：建立单向事实链" class="headerlink" title="3.4 重叠 Spec 的交接：建立单向事实链"></a>3.4 重叠 Spec 的交接：建立单向事实链</h3><p>这是 Comet 最精妙的设计。OpenSpec 和 Superpowers 都会产生 Spec，为了消除两者功能重叠带来的歧义，Comet 在它们之间建立了<strong>单向事实链</strong>：</p>
<table>
<thead>
<tr>
<th>层级</th>
<th>工具</th>
<th>职责</th>
</tr>
</thead>
<tbody><tr>
<td>需求侧唯一事实源</td>
<td>OpenSpec</td>
<td>定义验收场景、能力边界、行为契约</td>
</tr>
<tr>
<td>实现侧</td>
<td>Superpowers design doc</td>
<td>负责数据流、实现方案等</td>
</tr>
</tbody></table>
<p>关键约束：<strong>Superpowers 的 design doc 不得自行补写需求</strong>。</p>
<p>两者的衔接通过 <code>comet-handoff.sh</code> 脚本实现。该脚本从 OpenSpec 产物中生成结构化的交接包，包含：</p>
<ul>
<li>源文件路径</li>
<li>行范围</li>
<li>SHA256 哈希</li>
<li>确定性摘录（而非 Agent 临场手写的 Summary）</li>
</ul>
<p>并且明确声明 <code>canonical_spec: openspec</code>，表明需求定义权归属上游。</p>
<p>如果 Superpowers 在深度设计中发现 OpenSpec 遗漏了验收场景，不允许在 design doc 中自行补写，只能提出 Spec patch 回写到 OpenSpec 的 Delta Spec 中，最终覆盖到主 Spec，完成整个 Spec 生命周期的闭环。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">OpenSpec（需求定义）</span><br><span class="line">    ↓ comet-handoff.sh 交接包</span><br><span class="line">Superpowers design doc（实现方案）</span><br><span class="line">    ↓ 发现遗漏 → Spec patch</span><br><span class="line">Delta Spec → 回写 → OpenSpec 主 Spec</span><br></pre></td></tr></table></figure>

<hr>
<h2 id="四、Harness-Engineering：Skill-侧的工程化"><a href="#四、Harness-Engineering：Skill-侧的工程化" class="headerlink" title="四、Harness Engineering：Skill 侧的工程化"></a>四、Harness Engineering：Skill 侧的工程化</h2><p>Comet 本质上做的是 <strong>Harness Engineering</strong>（Skill 侧的工程化），而非 SDK 代码层面的工作。</p>
<p>这也是为什么去年爆火的 deep research 应用都转化为了 deep research 的 skill——因为 skill 让写 prompt 的过程从 SDK 中跳了出来，变成了人人可写的工具。</p>
<p>在 Skill 编排方面，以 OpenSpec 为例，它允许一定的 Skill 编排能力。用户可以创建一个路由 Skill，在里面写上”先调用 A skill 再调用 B skill”，最简单的编排就完成了，甚至只需要一句话。随着数据积累，模型能够内化选择组合哪些 Skill、自行迭代哪些 Skill 的能力，而这些是可以通过 Benchmark 训练的。</p>
<p><strong>真正的核心能力是让 Skill 变得更稳定，而这一过程就是在走 Harness Engineering 的路。</strong></p>
<hr>
<h2 id="五、总结"><a href="#五、总结" class="headerlink" title="五、总结"></a>五、总结</h2><p>Comet Skill 解决的核心问题是：<strong>把 Spec Coding 从”能用”变成”稳定可用”</strong>。</p>
<table>
<thead>
<tr>
<th>设计要点</th>
<th>解决的问题</th>
</tr>
</thead>
<tbody><tr>
<td>状态机思想</td>
<td>Agent 状态管理脆弱、文档回写丢失</td>
</tr>
<tr>
<td>意图识别 + 断点恢复</td>
<td>多设备、多场景下需要重复解释背景</td>
</tr>
<tr>
<td>人机确认机制</td>
<td>全自动流程缺乏关键节点把控</td>
</tr>
<tr>
<td>单向事实链</td>
<td>OpenSpec 与 Superpowers 的 Spec 重叠和歧义</td>
</tr>
</tbody></table>
<p>Comet Skill 的出现，为开发者在 AI 时代的 Spec Coding 提供了新的可能。它帮助开发者管理 Spec、专注执行，让大家能够在 AI 时代 Vibe Coding 出自己的创意。</p>
]]></description>
    </item>
    
    <item>
      <guid isPermalink="true">https://blog.pengfanao.top/2026/06/10/agent/Claude%20Code%20%E4%BB%8E%E9%9B%B6%E5%88%B0%E4%B8%8A%E6%89%8B/</guid>
      <title>Claude Code 从零到上手——安装、模型配置、Skill 与效率工具全指南</title>
      <link>https://blog.pengfanao.top/2026/06/10/agent/Claude%20Code%20%E4%BB%8E%E9%9B%B6%E5%88%B0%E4%B8%8A%E6%89%8B/</link>
      <pubDate>Wed, 10 Jun 2026 04:00:00 GMT</pubDate>
      <category>Claude Code</category><category>AI Agent</category><category>Skill</category><category>MCP</category><category>开发工具</category>
      <description><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li><a href="#%E4%B8%80%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89-claude-code">一、为什么选 Claude Code</a></li>
<li><a href="#%E4%BA%8C%E5%BF%AB%E9%80%9F%E5%AE%89%E8%A3%85">二、快速安装</a><ul>
<li><a href="#21-%E5%AE%89%E8%A3%85-nodejs">2.1 安装 Node.js</a></li>
<li><a href="#22-%E5%AE%89%E8%A3%85-claude-code">2.2 安装 Claude Code</a></li>
</ul>
</li>
<li><a href="#%E4%B8%89%E6%A8%A1%E5%9E%8B%E5%8E%82%E5%95%86%E4%B8%8E-coding-plan-%E5%AF%B9%E6%AF%94">三、模型厂商与 Coding Plan 对比</a><ul>
<li><a href="#31-%E9%98%BF%E9%87%8C%E7%99%BE%E7%82%BC">3.1 阿里百炼</a></li>
<li><a href="#32-%E8%85%BE%E8%AE%AF%E4%BA%91%E6%B7%B7%E5%85%83">3.2 腾讯云混元</a></li>
<li><a href="#33-%E7%99%BE%E5%BA%A6%E5%8D%83%E5%B8%86">3.3 百度千帆</a></li>
<li><a href="#34-deepseek">3.4 DeepSeek</a></li>
<li><a href="#35-%E7%81%AB%E5%B1%B1%E5%BC%95%E6%93%8E%E5%AD%97%E8%8A%82">3.5 火山引擎（字节）</a></li>
<li><a href="#36-minimax">3.6 MiniMax</a></li>
<li><a href="#37-%E6%99%BA%E8%B0%B1-glm">3.7 智谱 GLM</a></li>
<li><a href="#38-%E6%9C%88%E4%B9%8B%E6%9A%97%E9%9D%A2-kimi">3.8 月之暗面 Kimi</a></li>
</ul>
</li>
<li><a href="#%E5%9B%9B%E6%A8%A1%E5%9E%8B%E5%8E%82%E5%95%86%E5%88%87%E6%8D%A2cc-switch">四、模型厂商切换：CC Switch</a></li>
<li><a href="#%E4%BA%94skillclaude-code-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%89%A9%E5%B1%95%E6%9C%BA%E5%88%B6">五、Skill：Claude Code 的核心扩展机制</a><ul>
<li><a href="#51-%E4%BB%80%E4%B9%88%E6%98%AF-skill">5.1 什么是 Skill</a></li>
<li><a href="#52-%E5%B8%B8%E7%94%A8-skill-%E5%AE%89%E8%A3%85">5.2 常用 Skill 安装</a></li>
<li><a href="#53-skill-%E8%B7%AF%E5%BE%84">5.3 Skill 路径</a></li>
<li><a href="#54-skill-%E4%BB%93%E5%BA%93%E4%B8%8E%E8%B6%85%E5%B8%82">5.4 Skill 仓库与超市</a></li>
</ul>
</li>
<li><a href="#%E5%85%AD%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90">六、辅助工具推荐</a><ul>
<li><a href="#61-starship%E7%BB%88%E7%AB%AF%E7%BE%8E%E5%8C%96">6.1 Starship：终端美化</a></li>
<li><a href="#62-gstack%E5%B7%A5%E7%A8%8B%E5%9B%A2%E9%98%9F%E5%B7%A5%E5%85%B7%E9%9B%86">6.2 gstack：工程团队工具集</a></li>
<li><a href="#63-claude-hud%E5%8F%AF%E8%A7%86%E5%8C%96%E7%9B%91%E6%8E%A7%E9%9D%A2%E6%9D%BF">6.3 Claude HUD：可视化监控面板</a></li>
<li><a href="#64-%E8%B7%B3%E8%BF%87%E6%9D%83%E9%99%90%E7%A1%AE%E8%AE%A4">6.4 跳过权限确认</a></li>
</ul>
</li>
<li><a href="#%E4%B8%832026-%E5%B9%B4%E5%80%BC%E5%BE%97%E5%85%B3%E6%B3%A8%E7%9A%84%E6%96%B0%E5%B7%A5%E5%85%B7">七、2026 年值得关注的新工具</a></li>
<li><a href="#%E5%85%AB%E6%80%BB%E7%BB%93">八、总结</a></li>
<li><a href="#%E5%BC%95%E7%94%A8">引用</a></li>
</ul>
<hr>
<h2 id="一、为什么选-Claude-Code"><a href="#一、为什么选-Claude-Code" class="headerlink" title="一、为什么选 Claude Code"></a>一、为什么选 Claude Code</h2><p>Claude Code 是 Anthropic 推出的终端原生 AI 编程助手。相比 IDE 插件，它有几点核心优势：</p>
<ol>
<li><strong>终端原生</strong>：直接在你的 shell 里运行，能操作文件系统、执行命令、管理 git，不需要 IDE 作为中间层。</li>
<li><strong>Skill 机制</strong>：通过 Skill 文件扩展 Agent 能力，类似”给 Agent 写操作手册”，比传统插件更灵活。</li>
<li><strong>MCP 协议</strong>：通过 MCP (Model Context Protocol) 连接外部工具和数据源，生态开放。</li>
<li><strong>多模型后端</strong>：官方使用 Claude 系列模型，但通过 CC Switch 等工具可以切换到国内模型厂商。</li>
</ol>
<p>下面从安装开始，一步步配好你的 Claude Code 开发环境。</p>
<hr>
<h2 id="二、快速安装"><a href="#二、快速安装" class="headerlink" title="二、快速安装"></a>二、快速安装</h2><h3 id="2-1-安装-Node-js"><a href="#2-1-安装-Node-js" class="headerlink" title="2.1 安装 Node.js"></a>2.1 安装 Node.js</h3><p>Claude Code 是一个 npm 包，需要 Node.js 环境。推荐用 nvm 管理 Node 版本：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 nvm</span></span><br><span class="line">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载 nvm</span></span><br><span class="line">\. <span class="string">&quot;<span class="variable">$HOME</span>/.nvm/nvm.sh&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 Node 24</span></span><br><span class="line">nvm install 24</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证</span></span><br><span class="line">node -v   <span class="comment"># 应输出 v24.16.0</span></span><br><span class="line">npm -v    <span class="comment"># 应输出 11.13.0</span></span><br></pre></td></tr></table></figure>

<h3 id="2-2-安装-Claude-Code"><a href="#2-2-安装-Claude-Code" class="headerlink" title="2.2 安装 Claude Code"></a>2.2 安装 Claude Code</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装</span></span><br><span class="line">npm install -g @anthropic-ai/claude-code</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看版本</span></span><br><span class="line">claude --version</span><br><span class="line"></span><br><span class="line"><span class="comment"># 后续升级</span></span><br><span class="line">claude update</span><br></pre></td></tr></table></figure>

<p>安装完成后在终端输入 <code>claude</code> 即可启动交互式会话。官方文档：<a href="https://code.claude.com/docs/zh-TW/setup">設定 Claude Code</a></p>
<hr>
<h2 id="三、模型厂商与-Coding-Plan-对比"><a href="#三、模型厂商与-Coding-Plan-对比" class="headerlink" title="三、模型厂商与 Coding Plan 对比"></a>三、模型厂商与 Coding Plan 对比</h2><p>Claude Code 原生使用 Anthropic 的 Claude 系列模型，但国内用户可以通过第三方工具切换到国产模型。以下是主流模型厂商的 Coding Plan 概览：</p>
<h3 id="3-1-阿里百炼"><a href="#3-1-阿里百炼" class="headerlink" title="3.1 阿里百炼"></a>3.1 阿里百炼</h3><p>阿里云百炼平台提供 Qwen 系列等模型，支持 Coding Plan 订阅。</p>
<ul>
<li>控制台：<a href="https://common-buy.aliyun.com/coding-plan?spm=5176.44390736.0.0.7c8233a6anLRPn">阿里云百炼</a></li>
</ul>
<h3 id="3-2-腾讯云混元"><a href="#3-2-腾讯云混元" class="headerlink" title="3.2 腾讯云混元"></a>3.2 腾讯云混元</h3><p>腾讯混元大模型提供 Coding Plan，适合腾讯云生态用户。</p>
<ul>
<li>购买页：<a href="https://buy.cloud.tencent.com/hunyuan">腾讯云混元</a></li>
</ul>
<h3 id="3-3-百度千帆"><a href="#3-3-百度千帆" class="headerlink" title="3.3 百度千帆"></a>3.3 百度千帆</h3><p>百度智能云千帆大模型平台，提供文心系列模型。</p>
<ul>
<li>控制台：<a href="https://console.bce.baidu.com/qianfan/resource/subscribe">百度千帆</a></li>
</ul>
<h3 id="3-4-DeepSeek"><a href="#3-4-DeepSeek" class="headerlink" title="3.4 DeepSeek"></a>3.4 DeepSeek</h3><p>DeepSeek 独立平台，以高性价比和强代码能力著称。</p>
<ul>
<li>用量页：<a href="https://platform.deepseek.com/usage">DeepSeek Platform</a></li>
</ul>
<h3 id="3-5-火山引擎（字节）"><a href="#3-5-火山引擎（字节）" class="headerlink" title="3.5 火山引擎（字节）"></a>3.5 火山引擎（字节）</h3><p>火山方舟 Coding Plan 支持的模型最丰富：DeepSeek-V4 系列、GLM-5.1、Doubao-Seed-2.0 系列、Kimi-K2.6、MiniMax-M2.7 等。订阅叠加 9.5 折，低至 38 元。</p>
<ul>
<li>订阅链接：<a href="https://volcengine.com/L/ooEqNrnaXdU/">https://volcengine.com/L/ooEqNrnaXdU/</a></li>
<li>邀请码：<code>KGRNLDMJ</code></li>
</ul>
<h3 id="3-6-MiniMax"><a href="#3-6-MiniMax" class="headerlink" title="3.6 MiniMax"></a>3.6 MiniMax</h3><p>MiniMax 提供 M2 系列模型，也有 Coding Plan 可供选择。</p>
<ul>
<li>订阅方式同上（火山方舟渠道也支持 MiniMax 模型）</li>
</ul>
<h3 id="3-7-智谱-GLM"><a href="#3-7-智谱-GLM" class="headerlink" title="3.7 智谱 GLM"></a>3.7 智谱 GLM</h3><p>智谱 GLM Coding Plan 适配 20+ 主流工具，性价比突出。</p>
<ul>
<li>拼好模活动：<a href="https://www.bigmodel.cn/glm-coding?ic=FVAXRFATIM">https://www.bigmodel.cn/glm-coding?ic=FVAXRFATIM</a></li>
</ul>
<h3 id="3-8-月之暗面-Kimi"><a href="#3-8-月之暗面-Kimi" class="headerlink" title="3.8 月之暗面 Kimi"></a>3.8 月之暗面 Kimi</h3><p>Kimi 提供会员订阅模式，可在 Kimi 首页侧边栏进入。</p>
<ul>
<li>定价页：<a href="https://www.kimi.com/membership/pricing?from=kimi_homepage_sidebar&track_id=d5e22b46-2f5e-43a5-b1c7-b60f3b54c011">Kimi 会员</a></li>
</ul>
<hr>
<h2 id="四、模型厂商切换：CC-Switch"><a href="#四、模型厂商切换：CC-Switch" class="headerlink" title="四、模型厂商切换：CC Switch"></a>四、模型厂商切换：CC Switch</h2><p>Claude Code 默认对接 Anthropic API，要使用国内模型，需要借助 <strong>CC Switch</strong> 做 API 代理和模型映射。</p>
<ul>
<li>项目地址：<a href="https://github.com/farion1231/cc-switch/releases">CC Switch Releases</a>（当前最新 v3.16.2，2026-06-08 发布）</li>
</ul>
<p>CC Switch 的核心原理是在本地启动一个代理服务，将 Claude Code 发往 Anthropic API 的请求拦截并转发到你配置的国内模型厂商 API，同时做请求&#x2F;响应格式的兼容转换。目前 CC Switch 已不限于 Claude Code，同时支持 Codex、OpenCode、Gemini CLI 等工具。</p>
<p>基本使用流程：</p>
<ol>
<li>从 Release 页下载对应平台的二进制文件</li>
<li>配置目标模型厂商的 API Key 和 Endpoint</li>
<li>启动 CC Switch 代理</li>
<li>设置环境变量让 Claude Code 走代理</li>
</ol>
<p>这样 Claude Code 实际调用的就是国产模型，但交互体验保持一致。</p>
<hr>
<h2 id="五、Skill：Claude-Code-的核心扩展机制"><a href="#五、Skill：Claude-Code-的核心扩展机制" class="headerlink" title="五、Skill：Claude Code 的核心扩展机制"></a>五、Skill：Claude Code 的核心扩展机制</h2><h3 id="5-1-什么是-Skill"><a href="#5-1-什么是-Skill" class="headerlink" title="5.1 什么是 Skill"></a>5.1 什么是 Skill</h3><p>Skill 是 Claude Code 的扩展机制——本质上是给 Agent 注入一段专业领域的 System Prompt，告诉它”遇到某类任务时应该怎么做”。一个 Skill 就是一个 Markdown 文件，放在特定目录下即可被 Claude Code 自动发现和加载。</p>
<p>相比传统插件的优势：</p>
<ul>
<li><strong>声明式</strong>：不需要写代码，写清楚流程和规则即可</li>
<li><strong>可组合</strong>：多个 Skill 可以同时生效</li>
<li><strong>用户可控</strong>：Skill 优先级低于用户指令，用户始终拥有最终控制权</li>
</ul>
<p>更多原理参考：</p>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/2039014962991984938">Claude Code Skills 详解（一）</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/2035095579022337273">Claude Code Skills 详解（二）</a></li>
</ul>
<h3 id="5-2-常用-Skill-安装"><a href="#5-2-常用-Skill-安装" class="headerlink" title="5.2 常用 Skill 安装"></a>5.2 常用 Skill 安装</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 前端设计</span></span><br><span class="line">/plugin install frontend-design@claude-plugins-official</span><br><span class="line"></span><br><span class="line"><span class="comment"># 代码审查</span></span><br><span class="line">/plugin install code-review@claude-plugins-official</span><br><span class="line"></span><br><span class="line"><span class="comment"># 代码简化</span></span><br><span class="line">/plugin install code-simplifier@claude-plugins-official</span><br><span class="line"></span><br><span class="line"><span class="comment"># UI/UX 设计</span></span><br><span class="line">claude plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill</span><br><span class="line">claude plugin install ui-ux-pro-max@ui-ux-pro-max-skill</span><br><span class="line"></span><br><span class="line"><span class="comment"># SDD (Superpowers Driven Development)</span></span><br><span class="line">claude plugin install superpowers@claude-plugins-official</span><br><span class="line"></span><br><span class="line"><span class="comment"># agent-browser（浏览器自动化）</span></span><br><span class="line">npx skills add https://github.com/vercel-labs/agent-browser --skill agent-browser</span><br><span class="line"></span><br><span class="line"><span class="comment"># skill-creator（创建自定义 Skill）</span></span><br><span class="line">npx skills add https://github.com/anthropics/skills --skill skill-creator</span><br><span class="line"></span><br><span class="line"><span class="comment"># Brave Search（搜索集成）</span></span><br><span class="line"><span class="built_in">cp</span> -r brave-search-skills/skills/* ~/.agents/skills/    <span class="comment"># 用户级</span></span><br><span class="line"><span class="built_in">cp</span> -r brave-search-skills/skills/* .agents/skills/       <span class="comment"># 项目级</span></span><br></pre></td></tr></table></figure>

<h3 id="5-3-Skill-路径"><a href="#5-3-Skill-路径" class="headerlink" title="5.3 Skill 路径"></a>5.3 Skill 路径</h3><p>Skill 文件存放在以下位置：</p>
<table>
<thead>
<tr>
<th>平台</th>
<th>路径</th>
</tr>
</thead>
<tbody><tr>
<td>macOS</td>
<td><code>~/.claude/skills</code></td>
</tr>
<tr>
<td>Windows</td>
<td><code>E:\Users\&lt;用户名&gt;\.claude\skills</code></td>
</tr>
</tbody></table>
<p>用户级 Skill 对所有项目生效，项目级 Skill（<code>.claude/skills/</code>）只对当前项目生效。</p>
<h3 id="5-4-Skill-仓库与超市"><a href="#5-4-Skill-仓库与超市" class="headerlink" title="5.4 Skill 仓库与超市"></a>5.4 Skill 仓库与超市</h3><p>如果你不想自己写 Skill，可以从社区仓库找现成的：</p>
<ul>
<li><strong>Awesome Claude Skills</strong>：<a href="https://github.com/ComposioHQ/awesome-claude-skills">GitHub</a> — 精选 Skill 列表，涵盖开发、设计、运维等场景</li>
<li><strong>SkillsMP</strong>：<a href="https://skillsmp.com/zh">skillsmp.com</a> — Skill 超市，可直接浏览和下载</li>
</ul>
<hr>
<h2 id="六、辅助工具推荐"><a href="#六、辅助工具推荐" class="headerlink" title="六、辅助工具推荐"></a>六、辅助工具推荐</h2><h3 id="6-1-Starship：终端美化"><a href="#6-1-Starship：终端美化" class="headerlink" title="6.1 Starship：终端美化"></a>6.1 Starship：终端美化</h3><p>Starship 是一个跨 shell 的轻量级提示符，能让终端显示 git 分支、Node 版本、命令耗时等信息，Claude Code 场景下特别实用——一眼就能看到当前项目状态。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 先安装 Homebrew（如已安装可跳过）</span></span><br><span class="line">/bin/bash -c <span class="string">&quot;<span class="subst">$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(/opt/homebrew/bin/brew shellenv)&quot;&#x27;</span> &gt;&gt; ~/.zprofile</span><br><span class="line"><span class="built_in">eval</span> <span class="string">&quot;<span class="subst">$(/opt/homebrew/bin/brew shellenv)</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 Starship</span></span><br><span class="line">brew install starship</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置 ~/.zshrc，在文件末尾追加</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;eval &quot;$(starship init zsh)&quot;&#x27;</span> &gt;&gt; ~/.zshrc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生效</span></span><br><span class="line"><span class="built_in">source</span> ~/.zshrc</span><br></pre></td></tr></table></figure>

<h3 id="6-2-gstack：工程团队工具集"><a href="#6-2-gstack：工程团队工具集" class="headerlink" title="6.2 gstack：工程团队工具集"></a>6.2 gstack：工程团队工具集</h3><p>gstack 是一套面向工程团队的 Claude Code Skill 集合，提供 CEO 审查、设计评审、QA 测试、安全审计、部署等 23+ 个 Skill，远不止搜索功能。</p>
<p><strong>前置要求：</strong> 需要安装 Bun v1.0+。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 Bun（如未安装）</span></span><br><span class="line">curl -fsSL https://bun.sh/install | bash</span><br><span class="line"></span><br><span class="line"><span class="comment"># 克隆 gstack 并自动安装</span></span><br><span class="line">git <span class="built_in">clone</span> --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack</span><br><span class="line"><span class="built_in">cd</span> ~/.claude/skills/gstack &amp;&amp; ./setup</span><br></pre></td></tr></table></figure>

<p>详细使用参考：<a href="https://zhuanlan.zhihu.com/p/2016572095337869346">gstack 使用指南</a></p>
<h3 id="6-3-Claude-HUD：可视化监控面板"><a href="#6-3-Claude-HUD：可视化监控面板" class="headerlink" title="6.3 Claude HUD：可视化监控面板"></a>6.3 Claude HUD：可视化监控面板</h3><p>Claude HUD 是一个在终端内嵌的 HUD（抬头显示）面板，可以实时显示上下文用量、活跃工具、运行中的 Agent 和 Todo 进度，让你对 Claude Code 的运行状态一目了然。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Claude Code 内执行</span></span><br><span class="line">/plugin marketplace add jarrodwatts/claude-hud</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装插件</span></span><br><span class="line">/plugin install claude-hud</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化</span></span><br><span class="line">/claude-hud:setup</span><br></pre></td></tr></table></figure>

<p>详细使用参考：<a href="https://zhuanlan.zhihu.com/p/2003956311793103024">Claude HUD 介绍</a></p>
<h3 id="6-4-跳过权限确认"><a href="#6-4-跳过权限确认" class="headerlink" title="6.4 跳过权限确认"></a>6.4 跳过权限确认</h3><p>Claude Code 在执行文件操作、运行命令时会弹出权限确认。如果你在安全环境下希望减少打断，可以：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">claude --dangerously-skip-permissions</span><br></pre></td></tr></table></figure>

<blockquote>
<p>⚠️ 这个参数会跳过所有权限确认，只在完全信任的环境中（如个人开发机）使用。Claude Code 还提供了更温和的 <code>--allow-dangerously-skip-permissions</code> 参数，仅允许跳过权限但不默认开启，安全性更高。</p>
</blockquote>
<hr>
<h2 id="七、2026-年值得关注的新工具"><a href="#七、2026-年值得关注的新工具" class="headerlink" title="七、2026 年值得关注的新工具"></a>七、2026 年值得关注的新工具</h2><p>2026 年 Claude Code 生态涌现了不少新工具，这里推荐几个实用的：</p>
<table>
<thead>
<tr>
<th>工具</th>
<th>用途</th>
<th>Stars</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://github.com/obra/skillshare">skillshare</a></td>
<td>跨工具同步 Skill（Claude Code、Codex、OpenClaw 等）</td>
<td>2,190</td>
</tr>
<tr>
<td><a href="https://github.com/obra/skills-manager">skills-manager</a></td>
<td>桌面端 Skill 管理器，支持 15+ 编程工具</td>
<td>2,053</td>
</tr>
<tr>
<td><a href="https://github.com/obra/awesome-claude-code-toolkit">awesome-claude-code-toolkit</a></td>
<td>135 Agents + 35 Skills + 42 Commands + 176+ Plugins 合集</td>
<td>2,002</td>
</tr>
<tr>
<td><a href="https://github.com/obra/open-cowork">open-cowork</a></td>
<td>开源桌面应用，一键启动 Claude Code + MCP + Skills</td>
<td>1,543</td>
</tr>
</tbody></table>
<hr>
<h2 id="八、总结"><a href="#八、总结" class="headerlink" title="八、总结"></a>八、总结</h2><table>
<thead>
<tr>
<th>环节</th>
<th>推荐方案</th>
</tr>
</thead>
<tbody><tr>
<td>运行时</td>
<td>Node 24 + Claude Code 最新版</td>
</tr>
<tr>
<td>模型</td>
<td>海外用 Anthropic 官方，国内用火山方舟（模型最全）或智谱 GLM Coding Plan（性价比高）</td>
</tr>
<tr>
<td>模型切换</td>
<td>CC Switch</td>
</tr>
<tr>
<td>扩展</td>
<td>Skill（前端设计、代码审查、浏览器自动化、搜索等）</td>
</tr>
<tr>
<td>辅助</td>
<td>Starship（终端美化）+ gstack（工程工具集）+ Claude HUD（监控）</td>
</tr>
</tbody></table>
<p>Claude Code 的核心理念是”终端即 IDE”——不是替代 VS Code，而是提供一种更轻量、更脚本化、更 Agent 友好的编程方式。配合 Skill 和 MCP，它能做的不只是补全代码，而是真正帮你完成多步骤的工程任务。</p>
<hr>
<h2 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h2><ul>
<li><a href="https://code.claude.com/docs/zh-TW/setup">Claude Code 官方文档</a></li>
<li><a href="https://github.com/farion1231/cc-switch/releases">CC Switch</a></li>
<li><a href="https://github.com/ComposioHQ/awesome-claude-skills">Awesome Claude Skills</a></li>
<li><a href="https://skillsmp.com/zh">SkillsMP</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/2016572095337869346">gstack 使用指南</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/2003956311793103024">Claude HUD 介绍</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/2039014962991984938">Claude Code Skills 详解</a></li>
</ul>
]]></description>
    </item>
    
  </channel>
</rss>
