<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[求索空间]]></title> 
<atom:link href="https://blog.askerlab.com/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[以问题为起点的探索空间]]></description>
<link>https://blog.askerlab.com/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>使用 Herdr 和 Happy 进行 Agent 会话恢复</title>
    <link>https://blog.askerlab.com/?post=360</link>
    <description><![CDATA[<h2>背景</h2>
<p>在服务器上跑 Claude Code / Codex 这类 CLI Agent，最烦的两件事：</p>
<ol>
<li><strong>SSH 一断，会话就没了。</strong> 用 <code>nohup</code> / <code>screen</code> / <code>tmux</code> 能挂后台，但下次回来还得找半天哪个 pane 在跑什么。</li>
<li><strong>手机上没法操作。</strong> 出门在外想继续给 Agent 派活、批权限、看结果，纯 SSH 客户端体验很差。</li>
</ol>
<p>之前的方案是 tmux + Claude Code，能恢复会话，但手机上仍只能敲命令行。这次试了 <strong>Herdr + Happy</strong> 的组合，把「终端会话保持」和「手机端 Agent 控制」分开解决：</p>
<ul>
<li><strong>Herdr</strong>：Rust 写的终端多路复用器，专注管理 Agent 会话，自带左侧 Agent 状态栏、分屏、跨 SSH 断开恢复。</li>
<li><strong>Happy</strong>：给 Claude Code / Codex 套一层 wrapper，把会话同步到手机 App，可以远程发消息、批权限、看 diff。</li>
</ul>
<p>架构大致是：</p>
<pre><code>Herdr Pane
   └── happy claude
          └── Claude Code
                ↕
           Happy 手机 App</code></pre>
<p>下图是 Herdr 的界面，左侧是多个 Agent 区域，中间是 Agent 的展示区域：</p>
<p><img src="https://blog.askerlab.com/content/uploadfile/202609/41ac1788787040.png" alt="" /></p>
<p>这是 Happy 的 App 页面，可以看到同一个 Claude Code 会话已经同步到手机，底部能直接发消息：</p>
<img src="images/happy-app-jie-mian.jpg" alt="Happy App 界面" width="360" />
<h2>安装</h2>
<h3>一、安装 Herdr</h3>
<p>在 Ubuntu 服务器上（先 SSH 登录）：</p>
<pre><code class="language-bash">curl -fsSL https://herdr.dev/install.sh | sh</code></pre>
<p>重新登录，或者：</p>
<pre><code class="language-bash">source ~/.profile</code></pre>
<p>验证：</p>
<pre><code class="language-bash">herdr --version
which herdr</code></pre>
<p>如果提示找不到命令，直接把 <code>~/.local/bin</code> 加进 PATH：</p>
<pre><code class="language-bash">echo 'export PATH="$HOME/.local/bin:$PATH"' &gt;&gt; ~/.profile
source ~/.profile</code></pre>
<p>Herdr 是单个 Rust 二进制，支持 Ubuntu x86_64 和 ARM64，不需要 Docker，也不用单独装数据库。</p>
<h3>二、确认 Claude Code 已安装</h3>
<p>Herdr 不自带 Claude Code，只是管理终端和 Agent。</p>
<pre><code class="language-bash">claude --version</code></pre>
<p>还没登录的话直接运行 <code>claude</code> 按提示授权。你自己原来的 API / 模型配置不受影响，Herdr 不会改变模型或 API 地址。</p>
<p>Codex 同理：<code>codex --version</code>。</p>
<h3>三、安装 Agent 集成</h3>
<p>Claude Code 跑过、生成了 <code>~/.claude</code> 目录之后：</p>
<pre><code class="language-bash">herdr integration install claude
# 用 Codex 的话
herdr integration install codex

herdr integration status</code></pre>
<p>集成主要用于保存 Claude/Codex 的 Session ID，服务器或 Herdr 重启后可以恢复原对话。</p>
<h3>四、安装 Happy</h3>
<p>Happy 要求 Node.js 20+：</p>
<pre><code class="language-bash">node --version
npm install -g happy

happy --version
happy doctor</code></pre>
<blockquote>
<p><strong>npm 包现在叫 <code>happy</code>，旧教程里的 <code>happy-coder</code> 已迁移。</strong></p>
</blockquote>
<p>登录并绑定手机：</p>
<pre><code class="language-bash">happy auth login</code></pre>
<p>然后在手机装 Happy App（iOS / Android 都有），按终端里的二维码或登录提示完成绑定。</p>
<h2>使用</h2>
<h3>启动 Herdr</h3>
<p>进入项目目录：</p>
<pre><code class="language-bash">cd /你的项目目录
herdr</code></pre>
<p>Herdr 会自动拉起后台 Server、创建默认 Workspace、打开终端界面。</p>
<h3>在 Herdr 里启动 Happy 包装的 Claude</h3>
<p><strong>关键步骤</strong>：不要直接运行 <code>claude</code>，而是用 <code>HERDR_AGENT</code> 环境变量 + <code>happy</code> 启动：</p>
<pre><code class="language-bash">HERDR_AGENT=claude happy claude</code></pre>
<p>Codex 用：</p>
<pre><code class="language-bash">HERDR_AGENT=codex happy codex</code></pre>
<p><code>HERDR_AGENT</code> 是 Herdr 官方给 Wrapper / 沙箱程序准备的识别提示——告诉 Herdr「虽然前台进程叫 happy，但请按 claude 识别」。少了这个变量，左侧 Agents 栏会是空的（Herdr 看到的前台程序是 <code>happy</code>，不在它的 Agent 列表里）。</p>
<p>为了方便，在 <code>~/.bashrc</code> 里加两个别名：</p>
<pre><code class="language-bash">alias hclaude='HERDR_AGENT=claude happy claude'
alias hcodex='HERDR_AGENT=codex happy codex'</code></pre>
<p>之后直接 <code>hclaude</code> 就行。</p>
<p>恢复之前的 Happy 会话：</p>
<pre><code class="language-bash">happy daemon list
HERDR_AGENT=claude happy resume &lt;Happy会话ID&gt;</code></pre>
<h3>常用快捷键</h3>
<table>
<thead>
<tr>
<th>操作</th>
<th>快捷键</th>
</tr>
</thead>
<tbody>
<tr>
<td>右侧分屏</td>
<td><code>Ctrl+B</code>，松开，再按 <code>V</code></td>
</tr>
<tr>
<td>下方分屏</td>
<td><code>Ctrl+B</code>，松开，再按 <code>-</code></td>
</tr>
<tr>
<td>新标签页</td>
<td><code>Ctrl+B</code>，松开，再按 <code>C</code></td>
</tr>
<tr>
<td>切换工作区</td>
<td><code>Ctrl+B</code>，松开，再按 <code>W</code></td>
</tr>
<tr>
<td>帮助</td>
<td><code>Ctrl+B</code>，松开，再按 <code>?</code></td>
</tr>
<tr>
<td><strong>断开但保持运行</strong></td>
<td><code>Ctrl+B</code>，松开，再按 <code>Q</code></td>
</tr>
</tbody>
</table>
<h3>断开 SSH 但保持运行</h3>
<p>不要用 <code>exit</code> 结束 Claude，直接按 <code>Ctrl+B</code> 然后 <code>Q</code>。退出 Herdr 界面后关掉 PuTTY/SSH 窗口即可——</p>
<ul>
<li>PuTTY 关了</li>
<li>Herdr Server 继续在跑</li>
<li>Happy / Claude Code 继续在跑</li>
<li><code>npm run dev</code> 之类的开发服务也继续在跑</li>
</ul>
<p>下次重新 SSH 上去（<strong>必须用启动 Herdr 时的同一个 Ubuntu 用户</strong>），执行 <code>herdr</code>，就回到原来的界面。</p>
<h3>手机访问</h3>
<p>手机装 Happy App，登录同一账号，就能看到所有通过 <code>happy claude</code> 启动的会话，可以：</p>
<ul>
<li>继续给 Claude 发任务</li>
<li>同意 / 拒绝权限请求</li>
<li>查看代码变更</li>
<li>接收「完成 / 等待确认」通知</li>
<li>在手机和终端之间切换控制权</li>
</ul>
<p>Happy 默认走官方中继服务器同步，手机不需要连家里内网；官方称会话端到端加密。不想让流量经过第三方的话，可以后续自建 Happy Server。</p>
<h2>踩过的坑</h2>
<h3>1. Herdr 左侧 Agents 栏看不到 Happy 里的 Claude</h3>
<p><strong>现象</strong>：在 Herdr 里跑 <code>happy claude</code>，左侧 Agents 是空的。</p>
<p><strong>原因</strong>：Herdr 原生只识别 <code>claude</code>、<code>codex</code> 这些命令本身，运行 <code>happy claude</code> 时它看到的前台进程是 <code>happy</code>，不在支持列表里。</p>
<p><strong>排查</strong>：</p>
<pre><code class="language-bash">pgrep -af 'happy|claude|codex'
herdr agent list</code></pre>
<p>能看到 happy/claude 进程、但 <code>herdr agent list</code> 为空，就确认是包装进程遮挡。</p>
<p><strong>解决</strong>：用前面提到的 <code>HERDR_AGENT=claude happy claude</code> 启动。</p>
<p>需要注意：即便这样，<strong>手机端把会话切到远程模式后，Herdr 状态栏里的 working/blocked 状态可能偶尔不准</strong>——Herdr 和 Happy 没有官方直接集成，只能根据终端内容推断。手机控制本身不受影响。</p>
<h3>2. Happy 不能接管已经在跑的 <code>claude</code> 进程</h3>
<p>如果你先在 Herdr 里直接运行了 <code>claude</code>，事后是没法让 Happy 无缝接管这个进程的——它没经过 Happy wrapper。必须从一开始就用 <code>happy claude</code> 启动。</p>
<h3>3. 家用服务器别把 SSH 22 端口直接暴露公网</h3>
<p>如果服务器在家里，外出时先连 WireGuard，再访问内网 IP。SSH 直接暴露公网被扫是家常便饭。</p>
<h2>常用管理命令</h2>
<pre><code class="language-bash">herdr status              # Herdr 状态
herdr agent list          # Agent 列表
herdr integration status  # 集成状态
herdr update              # 更新 Herdr
herdr server stop         # 真正停掉所有窗口和 Agent（慎用）</code></pre>
<p>最后一条会结束 Pane 里正在运行的 Claude / Codex / 开发服务器，正常使用别执行。</p>
<h2>总结</h2>
<p>这套组合下来，我的分工是：</p>
<ul>
<li><strong>Herdr</strong>：多项目状态总览、终端分屏、跨 SSH 断开的会话保持</li>
<li><strong>Happy</strong>：手机端操作 Claude、收通知、批权限</li>
<li><strong>测试、日志、开发服务器</strong>：跑在 Herdr 的其他 Pane 里</li>
</ul>
<p>相比之前的 tmux 方案，最大提升是手机上能真正「用」Agent 了，不只是「看」Agent。代价是多了一层 Happy 中继，介意的可以自建 Happy Server。</p>]]></description>
    <pubDate>Mon, 07 Sep 2026 20:40:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/?post=360</guid>
</item>
<item>
    <title>claude code 多agents实践</title>
    <link>https://blog.askerlab.com/claude_code_multi_agents</link>
    <description><![CDATA[<h1>背景</h1>
<p>上礼拜一个做技术的朋友问我：&quot;你们搞的这个 Claude Code 多 agent，到底是个啥？我看网上又是 agent teams 又是 workflows 的，还有人说必须上 Claude Opus 才能跑，Kimi K3 行不行？&quot;</p>
<p>我想了想，这事一句话说不清楚，干脆写篇文章统一回复。</p>
<p>先说结论：<code>学会多 agent 的最好方式，是亲手做一个真项目</code>。你看十篇教程，不如跟着我把一个真实项目搭一遍。这篇文章我拿一个已经跑通了的真实案例来讲——一套&quot;四 agent + wiki 协作&quot;的项目初始化体系，你跟着做一遍，agent teams、workflows、ultracode 这些概念自然就明白了。</p>
<h1>1 三个概念，一句话说清</h1>
<p>先把术语掰扯清楚，不然后面全是糊涂账。</p>
<h2>1.1 Agent Teams：一群 AI 分工干活</h2>
<p>一个人干项目，需求、设计、开发、测试、上线全包，累不说，还容易顾此失彼。Agent Teams 就是把这个&quot;一个人&quot;拆成&quot;一队人&quot;：</p>
<blockquote>
<p>我实际项目里的配置：</p>
<ul>
<li>pm agent：管需求，写需求文档，评审设计是否符合需求</li>
<li>developer agent：管设计和写码，写设计文档，过评审后写生产代码</li>
<li>tester agent：管测试，写用例，跑测试，打回不合格代码</li>
<li>releaser agent：管上线，走安全审核清单，写上线记录</li>
</ul>
</blockquote>
<p>这四个 agent 不是随口说说，是真的在 <code>.claude/agents/</code> 下各建一个 <code>.md</code> 文件，写清楚职责边界。比如 tester 的边界就是&quot;不改生产代码，测试失败只能写证据打回，不能顺手修&quot;。</p>
<h2>1.2 Workflows：给 AI 排好 SOP</h2>
<p>光有分工还不够，还得有流程。Workflows 就是规定&quot;谁先干、谁后干、怎么交接&quot;的 SOP。</p>
<p>我项目里的核心流程是一条状态机：</p>
<pre><code>讨论中 → 待评审 → 已批准 → 设计中 → 设计评审中 → 开发中 → 测试中 → 待上线 → 已上线</code></pre>
<p>每个状态对应&quot;下一步谁干什么&quot;：</p>
<table>
<thead>
<tr>
<th>状态</th>
<th>下一步动作</th>
</tr>
</thead>
<tbody>
<tr>
<td>讨论中</td>
<td>主会话跟用户对齐需求</td>
</tr>
<tr>
<td>已批准</td>
<td>主会话判轨道：全轨=并行派 dev+tester，简轨=直接派 dev</td>
</tr>
<tr>
<td>设计评审中</td>
<td>自动并行派 pm+tester 评审</td>
</tr>
<tr>
<td>开发中</td>
<td>等 developer 交代码</td>
</tr>
<tr>
<td>测试中</td>
<td>派 tester 跑测试套件</td>
</tr>
<tr>
<td>待上线</td>
<td>用户确认后派 releaser</td>
</tr>
</tbody>
</table>
<p>这个流程写在 <code>wiki/workflow.md</code> 里，是主会话和 4 个 agent 的共同契约。</p>
<h2>1.3 Ultracode：多模型混编作战</h2>
<p>很多人以为跑 agent teams 必须全上 Claude Opus，其实不用。Ultracode 的思路是&quot;谁的活儿给谁&quot;——主会话（编排器）用 Claude，子 agent 按任务类型选模型。</p>
<p>我实际项目里 UI 测试就指定了 Kimi K3：</p>
<blockquote>
<p>tester agent 定义里写死：主会话派 UI 测试单时指定 <code>model: claude-sonnet-kimik3</code>，因为 Kimi K3 是多模态模型，能直读截图复核视觉细节；纯后端测试就不限定模型。</p>
</blockquote>
<p>这就是 ultracode 的核心逻辑：<strong>主模型负责编排和判断，子模型按能力混编</strong>。</p>
<h1>2 什么时候该用，什么时候别瞎折腾</h1>
<p>不是所有人都需要上多 agent，我见过有人把&quot;写一个 Hello World&quot;都拆成三个 agent，纯属脱裤子放屁。</p>
<h2>2.1 三种该用的场景</h2>
<p>1）<strong>任务可拆解，且质量要求高</strong></p>
<p>比如开发一个新功能，需求、设计、编码、测试、上线每个环节都有明确产出物，且产出物质量直接影响下游。我项目里一个 REQ 从讨论到上线，要走完 8 个状态，每个状态都有产出物：需求文档、设计文档、测试用例、结果报告、上线记录。</p>
<p>2）<strong>需要多角色评审，防单一视角盲区</strong></p>
<p>设计文档为什么要 pm+tester 双评审？因为 pm 只看&quot;是否符合需求意图&quot;，tester 只看&quot;可测性+边界覆盖&quot;。一个人看容易漏，两个人交叉看才能兜住。</p>
<p>3）<strong>有标准流程，且需要留痕追溯</strong></p>
<p>上线这事不能马虎。我项目里 releaser 必须走 deploy skill 的安全审核清单：密钥、日志泄密、注入、存储写路径、依赖、文档同步，六项逐项过，缺一项就打回。这个流程不标准化，早晚出事故。</p>
<h2>2.2 三种不该用的误区</h2>
<p>1）<strong>任务太简单</strong></p>
<p>改个错别字、调个样式，直接干就完了，走全流程纯属浪费。所以我项目里有&quot;简轨&quot;机制：单模块小改、bug 修复、配置文档类改动，跳过设计文档和设计评审，直接进开发。<code>简轨只豁免设计环节，测试和上线审核永不豁免</code>。</p>
<p>2）<strong>上下文太短，信息密度低</strong></p>
<p>一个 50 行脚本能解决的事，拆成 agent 反而增加沟通成本。agent 之间靠 wiki 文件交接，每个交接都有读写开销。</p>
<p>3）<strong>成本敏感且非关键路径</strong></p>
<p>多 agent 意味着多轮 LLM 调用。我项目里一个全轨 REQ 走完，pm、dev、tester、releaser 加起来十几轮调用。如果是非核心功能，或者预算紧张，直接单 agent 干就完了。</p>
<h1>3 实战项目：四 agent + wiki 协作体系</h1>
<p>下面讲真东西。这是我一个真实项目里跑通的体系，你跟着做一遍就明白多 agent 怎么玩了。</p>
<h2>3.1 项目背景与需求拆解</h2>
<p>这个项目要解决什么问题？一句话：<strong>让 Claude Code 在任何新项目里，按手册一步步搭出&quot;主会话编排 + 四 agent + wiki 驱动&quot;的协作体系</strong>。</p>
<p>需求拆解成 8 步：</p>
<p>1）建目录骨架：<code>wiki/</code>、<code>logs/</code>、<code>.claude/agents/</code>、<code>.claude/skills/</code><br />
2）写 4 个 agent 定义：pm、developer、tester、releaser<br />
3）写协作 SOP：<code>wiki/workflow.md</code><br />
4）写文档骨架 + 模板：readme、四份模板、看板、经验汇总<br />
5）写 skills + 脚本 + settings 改造<br />
6）写项目级 CLAUDE.md<br />
7）按项目扩展（全新项目 vs 已有项目两条路径）<br />
8）验收：跑脚本 + 模拟一个 REQ 走通全流程</p>
<p>前 6 步是机械搭建，第 7 步才需要动脑子——读新项目实际代码，把现状逆写进 wiki。</p>
<h2>3.2 怎么设计 agent 分工</h2>
<p>四个 agent 的定义文件，每份都有 frontmatter 和职责边界。以 developer 为例，关键设计：</p>
<pre><code class="language-yaml">---
name: developer
description: 开发 agent。根据已批准需求先写设计文档并接受评审，评审通过后写生产代码。不跳过设计评审直接写代码——除非派单注明「简轨」。
tools: Read, Write, Edit, Glob, Grep, Bash
---</code></pre>
<p>注意两个设计点：</p>
<ul>
<li><strong>description 里写明例外</strong>：&quot;除非派单注明简轨&quot;。这是给主会话看的，告诉它什么时候可以跳设计。</li>
<li><strong>tools 明确限制</strong>：developer 有 Bash 能跑测试，但没有 WebFetch，防止它跑偏去查外部资料。</li>
</ul>
<p>职责按严格顺序写死：</p>
<pre><code>1. 设计（全轨）：读需求 → 写设计文档 → 看板翻「设计评审中」→ 停下来等评审
2. 修订：评审打回 → 按意见修订 → 等复审
3. 开发：评审通过 → 写生产代码 → 自测 → 看板翻「测试中」
4. 经验沉淀：上线后回填设计文档 §8 + experience.md</code></pre>
<p>边界条款是防越权的第一道闸：</p>
<pre><code>- 评审通过前不写生产代码（唯一例外是简轨）
- 不改 wiki/pm/ 的需求内容、不写 wiki/tests/、不写 wiki/release/
- 发现需求本身有问题 → 打回给主会话，不擅自扩大或缩小范围</code></pre>
<h2>3.3 workflow 怎么排</h2>
<p>workflow 的核心是状态机，但有两个关键设计值得单独说。</p>
<p><strong>第一，简轨与全轨的分叉。</strong></p>
<p>不是所有需求都值得走全流程。主会话在「已批准」时先判轨道：</p>
<table>
<thead>
<tr>
<th>走简轨</th>
<th>走全轨</th>
</tr>
</thead>
<tbody>
<tr>
<td>单模块小改、影响面清晰</td>
<td>跨模块/跨系统改动</td>
</tr>
<tr>
<td>bug 级修复</td>
<td>新增外部接口/供应商接入</td>
</tr>
<tr>
<td>纯脚本、配置、依赖升级</td>
<td>存储结构/权限/安全边界变更</td>
</tr>
<tr>
<td>文档型/流程型改动</td>
<td>数据口径、统计逻辑变更</td>
</tr>
<tr>
<td>——</td>
<td>任何「拿不准」的情况（拿不准 = 全轨）</td>
</tr>
</tbody>
</table>
<p>这个设计解决了&quot;流程太重&quot;的痛点。我项目里 60% 的 REQ 走简轨，省掉了设计文档和两轮评审，但测试和上线审核一个不少。</p>
<p><strong>第二，讨论区与经验沉淀。</strong></p>
<p>需求讨论期的草稿放 <code>wiki/discuss/</code>，结论一出即流向正式文档或删除，执行完不留痕。这是防止&quot;草稿堆积&quot;的机制。</p>
<p>经验沉淀分两级：</p>
<ul>
<li>每个 REQ 上线后，developer 回填设计文档第 8 节「经验与教训」</li>
<li>可复用的一行经验追加到 <code>wiki/dev/experience.md</code>，供改代码前快速检索</li>
</ul>
<p>这个设计让团队知识不随项目结束而流失。</p>
<p>说到经验沉淀，我想起 2018 年做摄像机项目时，团队里有个老工程师离职，带走了红外遥控协议的所有踩坑记录，新接手的兄弟又重新踩了一遍，光 38kHz 载波频率就调了三天。从那以后我就明白：<code>经验不沉淀，等于团队在白干</code>。这套 wiki + experience.md 的机制，就是给 AI 团队也装上&quot;经验不落地&quot;的保险。</p>
<h2>3.4 跑出来的效果与踩过的坑</h2>
<p>这套体系在我项目里跑了 20+ 个 REQ，效果：</p>
<ul>
<li>需求平均交付周期从 3 天降到 1.5 天（简轨的功劳）</li>
<li>上线事故率降为 0（releaser 安全审核清单的功劳）</li>
<li>文档与代码漂移率大幅降低（my-check skill + freshness 脚本的功劳）</li>
</ul>
<p>踩过的坑也不少：</p>
<p>1）<strong>agent 越权改文件</strong></p>
<p>边界条款靠指令约束，无法强制封锁。有一次 developer 顺手改了 tester 的用例文件，被 code review 抓到。后来加了条款：发现越权行为应纠正并回报用户，严重时在 agent 定义里补针对性条款。</p>
<p>2）<strong>UI 测试截图存证难</strong></p>
<p>脚本测试能验证接口契约，但页面渲染对不对，脚本说了不算。后来给 tester 加了 browser-test skill，用 headless Chrome 实测并截图。Chrome 二进制固定在 <code>tests/third_party/chrome/</code>，版本与 chromedriver 配套。</p>
<p>3）<strong>多模型混编的上下文一致性</strong></p>
<p>Kimi K3 和 Claude 混编时，发现 Kimi 对某些 prompt 结构的理解有偏差。解决办法：主会话派单时，给 Kimi 的 prompt 更结构化，关键信息用列表而非段落。</p>
<h1>4 模型选择：Kimi K3、GLM 5.3 能不能用？</h1>
<p>这是最多人问的问题。我特意去查了最新官方文档（2026-09-06 时点），给你列个明白账。</p>
<h2>4.1 当前可用模型状态</h2>
<table>
<thead>
<tr>
<th>模型</th>
<th>上下文窗口</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>Claude Opus 4.x</td>
<td>200K</td>
<td>编排能力强，判断准</td>
<td>主会话（编排器）</td>
</tr>
<tr>
<td>Kimi K3</td>
<td>1M</td>
<td>多模态，视觉理解强</td>
<td>UI 测试、长上下文任务</td>
</tr>
<tr>
<td>Kimi K2.7 Code</td>
<td>256K</td>
<td>编程专用，高速变体可选</td>
<td>开发任务</td>
</tr>
<tr>
<td>GLM 5.3</td>
<td>1M</td>
<td>推理强，Code Bench 超 Opus 4.8</td>
<td>开发、测试</td>
</tr>
<tr>
<td>GLM 5.2</td>
<td>1M</td>
<td>基础模型稳定</td>
<td>备用</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>数据来源</strong><br />
Kimi 平台文档：platform.kimi.ai/docs（2026-09-06 时点）<br />
Z.ai 文档：docs.z.ai/guides/llm/（2026-09-06 时点）<br />
GLM 5.3 在 Z.ai Code Bench 达 34.5%，超过 Claude Opus 4.8，低于 Claude Fable 5</p>
</blockquote>
<h2>4.2 怎么混编：主 agent 用 Claude，子 agent 按能力选</h2>
<p>我的配置原则：</p>
<p>1）<strong>主会话（编排器）用 Claude Opus</strong></p>
<p>编排器要判断状态机流转、拆解任务、裁决评审分歧，这些都需要强推理能力。Claude 在这方面最稳。</p>
<p>2）<strong>子 agent 按任务类型选</strong></p>
<ul>
<li>developer：编码任务可用 GLM 5.3 或 Kimi K2.7 Code，实测 GLM 5.3 在 Terminal-Bench 3.0 达 28.3，比 GLM 5.2 的 4.6 提升 6 倍</li>
<li>tester：UI 测试指定 Kimi K3（多模态，能看截图）；纯后端测试不限定</li>
<li>pm/releaser：文档和审核任务，Claude Sonnet 即可</li>
</ul>
<p>3）<strong>混编注意事项</strong></p>
<ul>
<li>不同模型对 prompt 结构的理解有差异，派单 prompt 要结构化</li>
<li>上下文窗口要匹配任务长度，1M 窗口的模型适合长文档分析</li>
<li>价格差异大，高频调用的子 agent 可用性价比更高的模型</li>
</ul>
<h2>4.3 一个实际配置示例</h2>
<p>我项目里 <code>.claude/settings.json</code> 的权限白名单按技术栈配，Python 项目加 <code>Bash(python3:*)</code>、<code>Bash(pytest:*)</code>，Node 项目加 <code>Bash(npm:*)</code>。这是减少权限询问打断的关键。</p>
<p>PostToolUse 钩子注册 <code>remind-doc-sync.sh</code>，代码文件被编辑后自动提醒同步 wiki。这是防文档漂移的兜底机制。</p>
<p>==<strong>多 agent 不挑模型，挑的是分工</strong>==</p>
<h1>5 保姆级上手步骤</h1>
<p>废话不多说，跟着做。</p>
<h2>5.1 准备工作</h2>
<p>1）安装 Claude Code（略）<br />
2）准备一个 git 仓库（新项目 <code>git init</code>，已有项目直接进）<br />
3）在项目根目录启动 Claude Code</p>
<h2>5.2 初始化四 agent 体系</h2>
<p>把整个初始化手册交给 Claude Code，说&quot;按这份手册初始化项目协作体系&quot;。手册里包含每一步要创建的文件的完整内容，全部去业务化。</p>
<blockquote>
<p>这份手册我沉淀成了模板，放在项目 wiki 的 design 目录下，已去业务化。你拿去就能用，占位符用 <code>&lt;尖括号&gt;</code> 标注，替换成你项目的真实信息即可。手册核心内容我在第 3 节已经拆解过了，跟着做就行。</p>
</blockquote>
<p>如果你只要最小可用版本，手动建这几个文件：</p>
<pre><code class="language-bash">mkdir -p wiki/{discuss,pm,dev,tests,release,templates} \
         .claude/agents .claude/skills</code></pre>
<p>然后逐个创建：</p>
<ul>
<li><code>.claude/agents/pm.md</code></li>
<li><code>.claude/agents/developer.md</code></li>
<li><code>.claude/agents/tester.md</code></li>
<li><code>.claude/agents/releaser.md</code></li>
<li><code>wiki/workflow.md</code></li>
<li><code>wiki/pm/to-do.md</code></li>
</ul>
<p>每个文件的内容按我第 3 节讲的思路写，关键是职责边界和状态机。</p>
<h2>5.3 跑通第一个 REQ</h2>
<p>模拟一个需求走全流程：</p>
<p>1）在 <code>wiki/discuss/</code> 建草稿，跟主会话讨论<br />
2）派 pm 升格为正式需求文档<br />
3）主会话判轨道（简单需求走简轨）<br />
4）派 developer 开发<br />
5）派 tester 测试<br />
6）派 releaser 上线<br />
7）developer 回填经验</p>
<p>跑通后删掉模拟文件，看板清零。</p>
<h2>5.4 常见问题排查</h2>
<table>
<thead>
<tr>
<th>问题</th>
<th>排查方向</th>
</tr>
</thead>
<tbody>
<tr>
<td>agent 不干活</td>
<td>看板状态对不对？状态字段驱动派单</td>
</tr>
<tr>
<td>agent 越权改文件</td>
<td>agent 定义里的边界条款是否明确？</td>
</tr>
<tr>
<td>文档与代码漂移</td>
<td>my-check skill 是否调用？freshness 脚本是否定期跑？</td>
</tr>
<tr>
<td>模型调用失败</td>
<td>API key 配置是否正确？模型名称是否最新？</td>
</tr>
</tbody>
</table>
<h1>最后</h1>
<p>回到开头那句话：<code>学会多 agent 的最好方式，是亲手做一个真项目</code>。</p>
<p>这篇文章给你的不是理论，是一套我跑通了的完整方案。你拿去初始化一个新项目，或者改造一个已有项目，跟着状态机走一遍，agent teams、workflows、ultracode 这些概念就全活了。</p>
<p>别光看，动手做。做完一个 REQ，你就明白我在说什么了。</p>
<p>如果你在做过程中卡住了，或者跑出了更好的经验，欢迎回来交流。这套体系不是终点，是起点——你完全可以根据项目特点加 agent、改状态机、换模型组合。<code>工具是死的，用法是活的</code>。</p>]]></description>
    <pubDate>Sun, 06 Sep 2026 22:32:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/claude_code_multi_agents</guid>
</item>
<item>
    <title>Qwen3.8-27B评测：MoE之外的Dense路线 | 本地部署完整指南</title>
    <link>https://blog.askerlab.com/qwen3_8_27b_review</link>
    <description><![CDATA[<h1>1 背景</h1>
<p>这两天随便打开一个技术群、X、小红书，满屏都是 <code>Qwen3.8-27B</code>。我手边没有单卡 80G 的机器，没法立刻拉下来跑一轮自己的 benchmark，但这不妨碍把它从架构、科普、部署、实测到趋势一次性捋清楚。毕竟评测不是只有&quot;我亲自跑了&quot;这一种写法，把别人跑出来的数和官方卡摆在一起，照样能看明白。</p>
<blockquote>
<p>这周圈子里被 Qwen3.8-27B 刷屏刷到反胃。同期智谱在 6 月放的 GLM-5.2（753B 的后训练对话版）几乎没溅起水花。并不是它不行，是 753B 只能挂云端，普通人摸不着。大家兴奋的点出奇一致：不是&quot;又出了一个更大的&quot;，而是&quot;这个我能搬回家自己跑&quot;。</p>
</blockquote>
<p>这里有个反常识的细节值得先点出来：Qwen 这一代其实同时发了更大的 <code>Qwen3.8-2.4T-A95B</code>——总参数 2.4 万亿、激活 95B 的 MoE 巨兽。但刷屏的不是它，是 27B。原因可以归纳成一句话：</p>
<p><code>大模型真正的分水岭不在参数天花板，而在能不能搬回家。</code></p>
<p>2.4T 那个只能挂云端，普通玩家摸不着；27B 这个量化一下能塞进一张消费级显卡。所以本文就围绕这个&quot;能搬回家&quot;的 27B 展开。</p>
<h1>2 Qwen3.8-27B 到底是个什么模型</h1>
<h2>2.1 关键参数一表看清</h2>
<table>
<thead>
<tr>
<th>项目</th>
<th>数值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>参数量</td>
<td>27B（约 280 亿）</td>
<td>稠密模型，全参数激活</td>
</tr>
<tr>
<td>架构</td>
<td>Dense（非 MoE）</td>
<td>Gated DeltaNet + Gated Attention 混合，64 层</td>
</tr>
<tr>
<td>精度</td>
<td>BF16（原版）/ FP8（量化版）</td>
<td>FP8 block size 128，近乎无损</td>
</tr>
<tr>
<td>模态</td>
<td>Image-Text-to-Text</td>
<td>原生支持图像 + 视频理解</td>
</tr>
<tr>
<td>上下文</td>
<td>原生 262,144（256K）</td>
<td>YaRN 可扩到 1,000,000</td>
</tr>
<tr>
<td>思考模式</td>
<td>可开关</td>
<td><code>reasoning_effort</code> 调 xhigh/medium/low</td>
</tr>
<tr>
<td>License</td>
<td>Apache-2.0</td>
<td>商用友好</td>
</tr>
<tr>
<td>发布</td>
<td>2026 年 8 月</td>
<td>HF 下载量已破百万</td>
</tr>
</tbody>
</table>
<p>几个注意点：</p>
<ul>
<li><code>Gated DeltaNet + Gated Attention 混合架构</code>。这不是纯 Transformer，而是把线性注意力（DeltaNet）和标准注意力按 16:1 的比例混着用。好处是长上下文省显存，256K 原生上下文能落地，架构本身功不可没。</li>
<li><code>原生多模态</code>。支持图文、视频，这点对本地玩家很关键：一个模型顶过去&quot;语言模型 + 视觉模型&quot;两套，比如DeepSeek和GLM因为都只支持文本模型，在问答、Agent支持上还需要单独对接一个视觉模型，兼容性不好。</li>
<li><code>思考模式可关</code>。默认开思考，按请求关；还能用 <code>reasoning_effort</code> 调推理深度。等于一个模型既能当快思考的对话助手，也能当慢思考的推理机。</li>
</ul>
<h2>2.2 为什么这次火的是 27B，不是 2.4T</h2>
<p>把同代两个型号摆一起，逻辑就清楚了：</p>
<table>
<thead>
<tr>
<th>型号</th>
<th>总参</th>
<th>激活</th>
<th>架构</th>
<th>谁能跑</th>
</tr>
</thead>
<tbody>
<tr>
<td>Qwen3.8-27B</td>
<td>27B</td>
<td>27B</td>
<td>Dense</td>
<td>量化后单张消费级/专业卡</td>
</tr>
<tr>
<td>Qwen3.8-2.4T-A95B</td>
<td>2.4T</td>
<td>95B</td>
<td>MoE</td>
<td>云端集群，普通人摸不到</td>
</tr>
<tr>
<td>GLM-5.2（智谱同期）</td>
<td>753B</td>
<td>—（未公布）</td>
<td>MoE</td>
<td>云端，普通人摸不到</td>
</tr>
</tbody>
</table>
<p>MoE 的坑就在这：总参 2.4T 听着吓人，激活 95B 看着也还行，但<strong>所有专家的权重都得载进显存</strong>，如果不量化的话，大概需要 4800G显存（大概需要60 个 H100 显卡，每张 80G 显存），就算是量化到INT4，也需要1200GB显存（大概15 个 H100）。激活省的是算力不是显存。结果就是<code>算得动，但装不下</code>。普通人根本没机会本地部署，自然没&quot;火花&quot;。</p>
<p>27B Dense 反过来：参数不大，全激活，量化到 INT4 权重只要约 14GB，一张 RTX 4090/5090 就能塞下。<code>能被普通人亲手跑起来的模型，才有刷屏的资格。</code></p>
<h1>3 科普扫盲：跑一个大模型到底在烧什么</h1>
<p>有很多同学可能还不太清楚大模型的一些基本词汇，这里给没怎么碰过本地部署的同学补底子。很多人看到&quot;27B、FP8、MoE、vLLM&quot;就头大，其实拆开就四件事：精度、架构、引擎、显存。</p>
<h2>3.1 模型精度：BF16 / FP8 / INT4 差在哪</h2>
<p>精度就是&quot;每个参数用多少 bit 存&quot;。bit 越少，显存越省，但精度损失越大。</p>
<table>
<thead>
<tr>
<th>精度</th>
<th>bit</th>
<th>相对显存</th>
<th>质量损失</th>
<th>谁在用</th>
</tr>
</thead>
<tbody>
<tr>
<td>BF16</td>
<td>16</td>
<td>100%</td>
<td>无（训练原生）</td>
<td>训练、专业卡推理</td>
</tr>
<tr>
<td>FP8</td>
<td>8</td>
<td>~50%</td>
<td>近乎无损</td>
<td>H100 等新卡、官方量化</td>
</tr>
<tr>
<td>INT4（AWQ/GPTQ）</td>
<td>4</td>
<td>~25%</td>
<td>轻微</td>
<td>消费级主流</td>
</tr>
<tr>
<td>GGUF Q4_K_M</td>
<td>~4.5</td>
<td>~28%</td>
<td>轻微</td>
<td>llama.cpp，CPU/GPU 通吃</td>
</tr>
</tbody>
</table>
<p>经验法则：<code>BF16 是原汁原味，FP8 是性价比之王，INT4 是消费级的救命稻草。</code>Qwen3.8-27B 官方直接给了 FP8 版，且声明性能与原版几乎一致——对本地玩家来说，FP8 就是首选，不用自己再量化。</p>
<h3>那是不是越省越好？</h3>
<p>不是。<code>显存省的是空间，质量亏的是脑子。</code>可以想象一下，一个参数，如果是BF16，则是用65535的长度表示，如果是INT4，那只有128来表示，量化以后，以小模型检测为例，人形位置可能会波动，精度损失在闲聊里看不出，在硬任务上肉眼可见：</p>
<table>
<thead>
<tr>
<th>任务类型</th>
<th>INT4 相对 BF16 的表现</th>
</tr>
</thead>
<tbody>
<tr>
<td>闲聊 / 翻译 / 总结</td>
<td>几乎无差别</td>
</tr>
<tr>
<td>数学多步推理</td>
<td>误差累积，末位答案易错</td>
</tr>
<tr>
<td>写代码</td>
<td>边界条件、变量名小错变多</td>
</tr>
<tr>
<td>长上下文（&gt;32K）</td>
<td>越往后越丢细节、混淆</td>
</tr>
<tr>
<td>指令遵循（输出 JSON 等）</td>
<td>格式遵循率下降</td>
</tr>
</tbody>
</table>
<p>总结：<br />
1）<code>简单任务看不出，硬任务见真章</code>。闲聊 INT4 和 BF16 没区别，但跑 SWE-bench 那种真实改代码，分数会掉几个点。<br />
2）<code>量化方法决定损失下限</code>。AWQ/GPTQ 是&quot;聪明量化&quot;，护住关键权重；暴力 RTN 四舍五入损失最大。GGUF Q4_K_M 是混合精度（重要层多给 bit），所以比纯 INT4 稳——这也是它平均 ~4.5bit 而非纯 4bit 的原因。<br />
3）<code>官方量化 &gt; 社区量化</code>。Qwen3.8-27B 的 FP8 是官方调过的，敏感层（attention 等）保高精度，所以敢说&quot;几乎无损&quot;；INT4 多半是社区后做的，没官方背书，质量看具体仓库。</p>
<p>所以选型是道匹配题：<code>闲聊翻译省事用 INT4，代码数学跑 Agent 用 FP8，做基准评测要原汁原味用 BF16。</code>Qwen3.8-27B 官方把 FP8 这道甜点做好了，本地玩家没理由不先用，INT4 是 24G 消费卡装不下 FP8 时的退路，不是首选。</p>
<h2>3.2 Dense 还是 MoE，凭什么 Dense 这次赢麻了</h2>
<h3>先把两个词说人话：内存 vs 显存</h3>
<p>很多人混着用&quot;内存&quot;和&quot;显存&quot;，本地部署里这俩天差地别。</p>
<p>1）<code>内存（RAM）</code>：电脑主板上那根内存条，容量大、便宜、但慢。常见 16/32/64GB，CPU 使用，一般通过PCIE连接。<br />
2）<code>显存（VRAM）</code>：焊在显卡上的专用存储，容量小、贵、但极快。游戏卡 8–24GB，专业卡 48/80GB；一张 RTX 4090 的 24GB 显存就要一万多，H100 的 80GB 显存十万级。GPU 用它。</p>
<p>大模型推理主要靠 GPU 现炒现卖，所以模型权重必须放进<strong>显存</strong>里，不是内存。你内存插到 256GB 也没用，因为GPU 从内存取数据要绕 PCIe 走，慢几十倍，等于让大厨跑仓库拿料，菜早凉了。</p>
<blockquote>
<p><strong>一句话</strong><br />
<code>内存是大仓库，显存是灶台。</code>模型推理要现炒现卖，料必须在灶台上，放仓库里现搬来不及。</p>
</blockquote>
<p>这就是为什么本地玩家盯着显存看、不看内存——<code>显存比内存金贵十倍，也小十倍</code>，这才是真正的瓶颈。</p>
<h3>Dense 和 MoE 到底差在哪</h3>
<ul>
<li><code>Dense</code>：一个全才医生，所有本事都在脑子里。看任何一个病人，全部知识一起上。27B 参数全在显存里，用多少占多少，简单粗暴。</li>
<li><code>MoE</code>：一座医院，养着上百个专科专家。每次看病只请其中几个出诊，所以&quot;激活&quot;小、算得快。但问题是：<code>所有专家都得在医院里候着</code>，因为来什么病人事先不知道，专科医生不能临时叫。结果就是激活省了、驻留没省。</li>
</ul>
<p><code>一句话记住：MoE 省的是算力（出诊的人少），不省显存（候诊的人一个没少）。</code></p>
<h3>为什么 MoE 在本地放不下</h3>
<p>拿这次同代的两个型号算笔账（BF16，每参数 2 字节）：</p>
<table>
<thead>
<tr>
<th>型号</th>
<th>总参</th>
<th>全部权重占显存</th>
<th>单卡 80G 要几张</th>
<th>普通人够得着吗</th>
</tr>
</thead>
<tbody>
<tr>
<td>Qwen3.8-27B</td>
<td>27B</td>
<td>~54GB</td>
<td>1 张</td>
<td>量化后 4090 能跑</td>
</tr>
<tr>
<td>Qwen3.8-2.4T-A95B</td>
<td>2.4T</td>
<td>~4800GB</td>
<td>约 60 张</td>
<td>想都别想</td>
</tr>
</tbody>
</table>
<p>2.4T 的坑就在这：就算每次只激活 95B（出诊的医生少），2.4T 全部权重（所有医生）都得驻留显存，2.4万亿 × 2 字节 ≈ 4800GB。单张卡 80GB，要 60 张才装得下，这不是个人玩家的事，是数据中心的事。<code>激活小只让它算得动，没让它装得下。</code></p>
<h3>算力 vs 显存，哪个是生死线</h3>
<p>本地部署有两条约束，分量完全不同：<br />
1）<code>算力不够</code>：推理慢，每秒吐 5 个字。能忍，等就是了。<br />
2）<code>显存不够</code>：直接 OOM（out of memory），模型压根加载不进来，一个字都吐不出来。直接出局。</p>
<p>所以本地化第一关是&quot;装得下&quot;，不是&quot;跑得快&quot;。<code>本地化的天敌不是算力不够，是显存装不下。</code>这就是 27B Dense 能火、2.4T MoE 火不起来的根因——27B 量化后塞进一张消费卡，2.4T 量化十倍也还是数据中心专属。</p>
<blockquote>
<p><strong>但&quot;Dense 赢 MoE&quot;只在独显场景成立</strong><br />
上面说的是<strong>独显（显存=小容量 VRAM）</strong>的情况。换到<strong>统一内存一体机</strong>（DGX Spark / Mac Studio / AMD Ryzen AI Max+395，显存=内存、容量上百 GB），结论反过来：中等 MoE（如激活 5.1B 的 Ling-3.0-flash）激活小、读得少，反而比 Dense 快得多——实测单机 35 tok/s vs 27B Dense 的 4.37 tok/s（详见 5.2）。<code>一句话：独显小显存选 Dense，大统一内存选 MoE。</code></p>
</blockquote>
<h2>3.3 推理引擎：vLLM / SGLang / llama.cpp 怎么选</h2>
<table>
<thead>
<tr>
<th>引擎</th>
<th>定位</th>
<th>核心优化</th>
<th>适合场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>vLLM</td>
<td>高吞吐服务</td>
<td>PagedAttention</td>
<td>多并发 API、生产服务</td>
</tr>
<tr>
<td>SGLang</td>
<td>复杂应用</td>
<td>RadixAttention + 复杂调度</td>
<td>Agent、多轮、结构化输出</td>
</tr>
<tr>
<td>llama.cpp</td>
<td>CPU+GPU 混合</td>
<td>GGUF 量化</td>
<td>消费级、单机、离线</td>
</tr>
<tr>
<td>Transformers</td>
<td>研究</td>
<td>无特殊优化</td>
<td>调试、改模型</td>
</tr>
</tbody>
</table>
<p>一句话选型：<code>要对外服务选 vLLM，要跑 Agent 选 SGLang，只有一张游戏卡选 llama.cpp，要改模型本身才上 Transformers。</code></p>
<h2>3.4 显存占用：27B 到底要吃多少卡</h2>
<p>下面这张表是我按&quot;权重显存 = 参数量 × 每参数字节&quot;算的理论值，再加 KV cache 余量估的（实际还看上下文长度和并发）：</p>
<table>
<thead>
<tr>
<th>精度</th>
<th>27B 权重显存</th>
<th>典型硬件</th>
<th>本地可行性</th>
</tr>
</thead>
<tbody>
<tr>
<td>BF16</td>
<td>~54GB</td>
<td>H100 / A100 80G 单卡</td>
<td>需专业卡</td>
</tr>
<tr>
<td>FP8（官方）</td>
<td>~27GB</td>
<td>RTX 5090 32G / L40S 48G</td>
<td>5090 或专业卡</td>
</tr>
<tr>
<td>INT4（AWQ/GPTQ）</td>
<td>~14GB</td>
<td>RTX 4090 24G</td>
<td>消费级 ✓</td>
</tr>
<tr>
<td>GGUF Q4_K_M</td>
<td>17.77GB</td>
<td>4090 24G 或 32G 内存</td>
<td>消费级 ✓</td>
</tr>
</tbody>
</table>
<p>注意 FP8 的尴尬：27GB 权重对大多数消费卡偏大。但有个特例——<code>RTX 5090 是 32GB GDDR7</code>，是消费级里唯一能装下官方 FP8（27GB）、跑近乎无损精度的卡。4090（24GB）装不下 FP8，得退到 INT4 或 GGUF。这个细节很多刷屏帖没讲清，容易让人按老经验以为消费卡都只能 INT4。</p>
<h1>4 怎么部署：HuggingFace 和魔塔社区两条路</h1>
<h2>4.1 拉模型</h2>
<p>国内推荐走魔塔社区（ModelScope），速度快不用梯子；海外或要最新版走 HuggingFace。</p>
<p>HuggingFace：</p>
<pre><code class="language-bash">pip install -U "huggingface_hub[cli]"
huggingface-cli download Qwen/Qwen3.8-27B-FP8 --local-dir ./Qwen3.8-27B-FP8</code></pre>
<p>魔塔社区（ModelScope）：</p>
<pre><code class="language-bash">pip install modelscope
modelscope download --model qwen/Qwen3.8-27B-FP8 --local_dir ./Qwen3.8-27B-FP8</code></pre>
<blockquote>
<p><strong>先下 FP8 还是 BF16？</strong><br />
本地玩家直接下 <code>Qwen3.8-27B-FP8</code>。BF16 原版 54GB 起步，没专业卡别折腾；FP8 官方量化、近乎无损、还自带各引擎示例，性价比最高。</p>
</blockquote>
<h2>4.2 起服务</h2>
<p>vLLM（最常用，OpenAI 兼容 API）：</p>
<pre><code class="language-bash">pip install vllm
vllm serve "Qwen/Qwen3.8-27B-FP8"</code></pre>
<p>调用：</p>
<pre><code class="language-bash">curl -X POST "http://localhost:8000/v1/chat/completions" \
  -H "Content-Type: application/json" \
  --data '{"model": "Qwen/Qwen3.8-27B-FP8", "messages": [{"role": "user", "content": "用一句话介绍你自己"}]}'</code></pre>
<p>SGLang（跑 Agent / 复杂调度）：</p>
<pre><code class="language-bash">pip install sglang
python3 -m sglang.launch_server --model-path "Qwen/Qwen3.8-27B-FP8" --host 0.0.0.0 --port 30000</code></pre>
<p>Transformers（调试 / 研究）：</p>
<pre><code class="language-python">from transformers import pipeline
pipe = pipeline("image-text-to-text", model="Qwen/Qwen3.8-27B-FP8")
messages = [
    {"role": "user", "content": [
        {"type": "image", "url": "https://example.com/photo.jpg"},
        {"type": "text", "text": "这张图里是什么？"}
    ]}
]
print(pipe(text=messages))</code></pre>
<p>推理参数官方也给了建议，直接抄：</p>
<ul>
<li>思考模式：<code>temperature=1.0, top_p=0.95, top_k=20</code></li>
<li>非思考模式：<code>temperature=0.7, top_p=0.80, presence_penalty=1.5</code></li>
</ul>
<h2>4.3 消费级硬件的退路：GGUF 量化</h2>
<p>只有一张消费卡（4090 24G / 5090 32G）或干脆纯 CPU 跑，就走 llama.cpp + GGUF。GGUF 版通常由社区（如 bartowski、unsloth）出，命令：</p>
<pre><code class="language-bash"># 拉社区 GGUF（具体仓库名以 ModelScope/HF 搜索为准）
huggingface-cli download bartowski/Qwen3.8-27B-GGUF --include "*Q4_K_M*" --local-dir ./gguf

# 起服务
./llama-server -m Qwen3.8-27B-Q4_K_M.gguf -ngl 99 -c 32768 --port 8080</code></pre>
<p><code>-ngl 99</code> 把层全卸到 GPU，<code>-c 32768</code> 给上下文。纯 CPU 就把 <code>-ngl</code> 调小甚至 0，靠内存扛，慢，但能跑。</p>
<h1>5 实际测试：能力与速度的数据</h1>
<h2>5.1 官方基准（有出处，含横向对比）</h2>
<p>数据来源：HuggingFace <code>Qwen/Qwen3.8-27B</code> 官方模型卡。</p>
<table>
<thead>
<tr>
<th>基准</th>
<th>Qwen3.8-27B</th>
<th>Qwen3.6-27B</th>
<th>Opus4.6 Max</th>
<th>这个数代表什么</th>
</tr>
</thead>
<tbody>
<tr>
<td>SWE-bench Pro</td>
<td><strong>61.7</strong></td>
<td>53.5</td>
<td>53.4</td>
<td>真实软件工程任务修复率</td>
</tr>
<tr>
<td>LiveCodeBench v6</td>
<td><strong>90.3</strong></td>
<td>83.9</td>
<td>88.8</td>
<td>实时编程题</td>
</tr>
<tr>
<td>GPQA Diamond</td>
<td>89.2</td>
<td>87.8</td>
<td><strong>91.3</strong></td>
<td>研究生级科学问答</td>
</tr>
<tr>
<td>OSWorld-Verified</td>
<td><strong>84.3</strong></td>
<td>63.9</td>
<td>72.7</td>
<td>操作系统级 Agent 任务</td>
</tr>
<tr>
<td>AndroidWorld</td>
<td><strong>81.9</strong></td>
<td>70.3</td>
<td>62.0</td>
<td>安卓 Agent 任务</td>
</tr>
<tr>
<td>MathVision（with CI）</td>
<td><strong>94.6</strong></td>
<td>—</td>
<td>—</td>
<td>视觉数学题</td>
</tr>
</tbody>
</table>
<p>几个直观解读：<br />
1）<code>SWE-bench Pro 61.7</code> 不只是真能改代码——它把闭源的 Opus4.6 Max（53.4）按在地上摩擦。27B 开源反超闭源旗舰，放两年前不敢想。<br />
2）<code>OSWorld-Verified 84.3</code> 和 <code>AndroidWorld 81.9</code> 说明它能当 Agent 操作电脑和手机，这是官方强调&quot;长期代理任务&quot;的底气，且远超上一代 27B（63.9 / 70.3）。<br />
3）<code>MathVision 94.6</code> 印证多模态不是噱头，看图做数学题接近满分。<br />
4）唯一没拿第一的是 GPQA Diamond（89.2 vs Opus 91.3），科学推理还差闭源一点——但差距已经很小。</p>
<h2>5.2 实测数据：真实 DGX Spark 基准</h2>
<blockquote>
<p><strong>数据来源</strong><br />
本节数据引自 <strong>Lonely__MH《大模型本地部署完整指南》（2026-08-17）</strong>。原文主角是蚂蚁百灵 Ling-3.0-flash，Qwen3.8-27B 作为<strong>参照模型</strong>进行单机压测。我提取了 Qwen3.8-27B 的关键数字，并结合我的带宽分析加以解读。</p>
</blockquote>
<h3>硬件与模型配置</h3>
<table>
<thead>
<tr>
<th>项目</th>
<th>配置</th>
</tr>
</thead>
<tbody>
<tr>
<td>硬件</td>
<td>NVIDIA DGX Spark（GB10 芯片，<strong>121.6GB 统一内存</strong>）</td>
</tr>
<tr>
<td>架构</td>
<td>ARM64 + Ubuntu 24.04</td>
</tr>
<tr>
<td>引擎</td>
<td>vLLM（BF16 全精度）</td>
</tr>
<tr>
<td>模型</td>
<td>Qwen3.8-27B Dense BF16</td>
</tr>
</tbody>
</table>
<h3>实测吞吐与延迟</h3>
<table>
<thead>
<tr>
<th>并发数</th>
<th>聚合吞吐</th>
<th>单路吐字速率</th>
<th>平均首字延迟（TTFT）</th>
<th>P95 首字延迟</th>
</tr>
</thead>
<tbody>
<tr>
<td>c=1</td>
<td>4.34 tok/s</td>
<td><strong>4.37 tok/s</strong></td>
<td>294.8 ms</td>
<td>297.5 ms</td>
</tr>
<tr>
<td>c=2</td>
<td>8.55 tok/s</td>
<td>4.32 tok/s</td>
<td>540.0 ms</td>
<td>626.5 ms</td>
</tr>
<tr>
<td>c=4</td>
<td>16.84 tok/s</td>
<td>4.26 tok/s</td>
<td>658.2 ms</td>
<td>694.3 ms</td>
</tr>
<tr>
<td>c=8</td>
<td>32.61 tok/s</td>
<td>4.15 tok/s</td>
<td>1033.5 ms</td>
<td>1085.4 ms</td>
</tr>
</tbody>
</table>
<h3>为什么只有 4 tok/s？带宽是罪魁祸首</h3>
<p>原文原文一句话点破天机：</p>
<blockquote>
<p><code>55.6GB 权重访存瓶颈</code>——由于 Qwen3.8-27B 为 27.8B 全激活全精度模型，每步解码需搬运全部 55.6 GB 权重，受限于单机内存带宽，其吞吐构成了衡量 MoE 稀疏能效比的黄金基线。</p>
</blockquote>
<p>这个 <code>4.37 tok/s</code> 和我前面的带宽公式完全对上：</p>
<ul>
<li><code>55.6 ÷ 4.37 ≈ 12.7 TB/s</code>（并发后有效带宽）</li>
<li>但<strong>单并发</strong>时，DGX Spark 的<strong>统一内存带宽（~几百 GB/s）远低独显</strong>，被 BF16 全量压垮，所以只有 4 tok/s。</li>
</ul>
<p>这印证了我的判断：<code>低带宽 + BF16 = 极慢，MoE 激活小是优势</code>（Ling-3.0-flash 单并发 35 tok/s，是 Qwen3.8-27B 的 8 倍）。</p>
<h3>给本地玩家的启示</h3>
<p>1）<code>DGX Spark 是"能装下"但"不算快"</code>。121.6GB 统一内存能塞下 BF16，但带宽被统一内存拖垮，decode 慢。适合&quot;能跑、不用等&quot;的离线任务，不适合实时对话。</p>
<p>2）<code>MoE 在统一内存上反而赢麻了</code>。Ling-3.0-flash（124B 总参，5.1B 激活）单机 35 tok/s，是 Qwen3.8-27B 的 8 倍，这验证了我第 3.2 节的判断：<strong>MoE 省算力不省显存，但在&quot;显存就是内存&quot;的统一内存机器上，激活小直接转化为快</strong>。</p>
<p>3）<code>消费级玩家别指望 BF16</code>。想快要么上高带宽独显（5090 的 FP8，~85 tok/s 理论值），要么走 MoE 路线。</p>
<h1>6 趋势预判：大模型本地化这件事，已经不可逆了</h1>
<p>把时间线拉长看，这件事其实不新鲜。历史反复在演同一个剧本：</p>
<blockquote>
<p>1990 年代，计算机是机房里的大家伙，普通人摸不到。后来 PC 普及，算力搬进了家。<br />
2010 年代，智能语音、图像识别都在云端。后来手机 NPU 起来，端侧能跑了。<br />
2026 年，大模型正站在同一个拐点上——从&quot;只能调 API&quot;到&quot;能塞进一张显卡&quot;。</p>
</blockquote>
<p>Qwen3.8-27B 刷屏，本质是这个拐点到了的信号弹。几条具体判断：</p>
<p>1）<code>27B 会成为本地部署的甜点档</code>。再小（7B 级）能力不够看，再大（70B+）消费级装不下，27B 量化后正好卡在 24G 卡的舒适区。<br />
2）<code>电脑的下一个标配可能是"能跑大模型的显卡"</code>。就像当年独立显卡为了游戏普及，下一轮可能是为了本地大模型。买电脑看显存会变成常识。<br />
3）<code>统一内存一体机会兴起，MoE 在一体机上翻盘</code>。DGX Spark、Mac Studio、AMD Ryzen AI Max+395 这类&quot;显存=内存&quot;的一体机正在普及——容量上百 GB，装得下 124B 的 MoE。这时激活小的 MoE 反而比 Dense 快（实测 Ling-3.0-flash 35 tok/s 是 27B Dense 的 8 倍）。<code>独显小显存看 Dense，统一内存大容量看 MoE</code>，两种硬件走两条路。<br />
4）<code>多模态原生会成为默认形态</code>。单独下个语言模型再接视觉模块的拼装路线，会被&quot;一个模型全吃&quot;的原生多模态替代——Qwen3.8-27B 就是这个形态的样本。</p>
<p>==<strong>大模型本地化不是要不要做的问题，是哪台电脑先装上的问题。</strong>==</p>
<h1>风险 / 注意事项</h1>
<p>1）<code>显存别只看参数</code>。27B 听着不大，BF16 要 54GB，FP8 要 27GB——4090 24G 跑 FP8 会 OOM，但 5090 32G 刚好能装。买卡前先按第 3.4 节算一遍。<br />
2）<code>长上下文吃 KV cache</code>。256K 上下文很香，但真开到那么长，KV cache 显存会暴涨，并发和长度得取舍。<br />
3）<code>GGUF 版本滞后</code>。社区量化通常比官方慢半拍，要最新版还是得盯 HF/ModelScope 官方。<br />
4）<code>第三方实测要自己跑</code>。刷屏帖里的速度数硬件各异，照搬没意义，拉下来用 vLLM 自带 benchmark 跑一遍才是自己的数。<br />
5）<code>License 确认</code>。Qwen3.8-27B 是 Apache-2.0 商用友好，但衍生量化版的 License 以各自仓库为准，商用前看一眼。</p>]]></description>
    <pubDate>Wed, 19 Aug 2026 21:00:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/qwen3_8_27b_review</guid>
</item>
<item>
    <title>claude code安装说明</title>
    <link>https://blog.askerlab.com/claude_code_install</link>
    <description><![CDATA[<h1>背景</h1>
<p>Claude Code 是 Anthropic 官方推出的命令行 AI 编程助手，可以直接在终端里帮你写代码、查 bug、读项目。但因为它走的是 Anthropic 官方 API，国内用户直接使用有门槛。我在自己的 Windows 电脑和云服务器（Linux）上都跑通了，也帮几个朋友配置过，发现整个过程对于不熟悉命令行的同学来说，坑还挺多的。本文从一台新电脑的视角出发，手把手带你装好 Claude Code，并接入第三方 API 中转服务，让国内用户也能流畅使用。</p>
<p>本文将覆盖：Windows、Mac、Linux 三个平台。</p>
<h1>1 前置准备：安装 Node.js</h1>
<p>Claude Code 是通过 npm 分发的，npm 是 Node.js 自带的包管理器。所以第一步：装 Node.js。</p>
<p>Claude Code 目前要求 Node.js <strong>≥ 22.0.0</strong>，建议直接装最新的 LTS 版本。</p>
<h2>1.1 Windows 安装</h2>
<p>1）打开 Node.js 官网：<a href="https://nodejs.org">https://nodejs.org</a></p>
<p>2）下载左边的 <strong>LTS</strong> 版本（长期支持版），右边是最新版也可以用，但 LTS 更稳。你会得到一个 <code>.msi</code> 安装包，比如 <code>node-v22.x.x-x64.msi</code>。</p>
<p>3）双击运行安装包，一路点 &quot;Next&quot; 就行。<strong>注意这一步</strong>：在安装选项页面，确保 <strong>&quot;Add to PATH&quot;</strong> 是勾选状态（默认就是勾的），这样才能在任意位置使用 <code>node</code> 和 <code>npm</code> 命令。</p>
<p>4）安装完成后，验证一下。按 <code>Win + R</code>，输入 <code>cmd</code> 回车，打开命令提示符，输入：</p>
<pre><code class="language-bash">node --version
npm --version</code></pre>
<p>如果分别输出 <code>v22.x.x</code> 和 <code>10.x.x</code>，说明安装成功。</p>
<h2>1.2 Mac 安装</h2>
<p>Mac 推荐用 Homebrew，一行搞定。如果你还没有 Homebrew，先去 <a href="https://brew.sh">https://brew.sh</a> 复制那行安装命令跑一遍。</p>
<pre><code class="language-bash"># 安装 Node.js
brew install node

# 验证
node --version
npm --version</code></pre>
<blockquote>
<p>如果你习惯用 nvm 管理 Node 版本，也可以 <code>nvm install 22</code>，效果一样。对小白来说 Homebrew 就够。</p>
</blockquote>
<h2>1.3 Linux 安装</h2>
<p>以 Ubuntu/Debian 为例：</p>
<pre><code class="language-bash"># 添加 NodeSource 源（Node.js 22.x）
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -

# 安装
sudo apt-get install -y nodejs

# 验证
node --version
npm --version</code></pre>
<p>CentOS/RHEL 用对应的 yum 源；其他发行版请参考 NodeSource 官方文档。装完验证版本号，确保 ≥ 22。</p>
<h1>2 安装 Claude Code</h1>
<p>Node.js 就位后，打开终端（Windows 是 cmd 或 PowerShell，Mac/Linux 是 Terminal），执行一行命令：</p>
<h2>2.1 通过 npm 全局安装</h2>
<pre><code class="language-bash">npm install -g @anthropic-ai/claude-code</code></pre>
<p><code>-g</code> 表示全局安装，装完后在任意目录都能使用 <code>claude</code> 命令。</p>
<p>安装过程大概 1-2 分钟，取决于网络速度。如果下载很慢，可以先设置 npm 镜像：</p>
<pre><code class="language-bash"># 可选：设置淘宝镜像加速下载
npm config set registry https://registry.npmmirror.com

# 然后再安装
npm install -g @anthropic-ai/claude-code</code></pre>
<h2>2.2 验证安装</h2>
<pre><code class="language-bash">claude --version</code></pre>
<p>如果输出版本号（如 <code>2.1.217</code>），说明安装成功。</p>
<h1>3 接入第三方 API</h1>
<p>这是本文最核心的部分。Claude Code 默认连的是 Anthropic 官方 API（<code>https://api.anthropic.com</code>），需要海外信用卡和网络。国内用户可以通过 <strong>API 中转服务</strong>（也叫 API Proxy / API 网关）来使用，原理很简单：</p>
<blockquote>
<p>你的 Claude Code → 中转服务器 → Anthropic 官方 API → 中转服务器 → 你的 Claude Code</p>
</blockquote>
<p>你只需要一个中转服务的 API Key 和一个 Base URL，就能跟官方一样使用。</p>
<h2>3.1 获取中转 API 地址和 Key</h2>
<p>市面上有多个中转服务商，比如 APIHub、AIHubMix、LobeChat 网关、CloudFlare Worker 自建等，选一个注册获取：</p>
<ul>
<li><strong>Base URL</strong>：类似 <code>https://your-api-proxy.com</code> 的地址</li>
<li><strong>API Key</strong>：类似 <code>sk-xxxxxxxxxxxxxxxxxxxxxxxx</code> 的密钥</li>
<li><strong>模型名称</strong>：中转服务会告诉你可以用哪些模型，比如 <code>claude-sonnet-4-20250514</code></li>
</ul>
<blockquote>
<p><strong>注意</strong><br />
不要把 API Key 发给任何人，不要贴到公开的 GitHub 仓库里。Key 泄露会被盗刷。</p>
</blockquote>
<h2>3.2 配置 settings.json</h2>
<p>Claude Code 的配置统一放在 <code>settings.json</code> 里。这个文件有两级：</p>
<table>
<thead>
<tr>
<th>级别</th>
<th>路径</th>
<th>作用范围</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>用户级</strong>（推荐）</td>
<td><code>~/.claude/settings.json</code></td>
<td>对所有项目生效</td>
</tr>
<tr>
<td><strong>项目级</strong></td>
<td>项目根目录下 <code>.claude/settings.json</code></td>
<td>只对当前项目生效</td>
</tr>
</tbody>
</table>
<p>推荐先配置用户级，这样不管在哪个目录用 Claude Code，都能用同一个中转 API。</p>
<h3>步骤 1：找到或创建配置文件</h3>
<p><code>.claude</code> 文件夹在用户主目录下，不同系统的路径：</p>
<ul>
<li><strong>Windows</strong>：<code>C:\Users\你的用户名\.claude\</code></li>
<li><strong>Mac / Linux</strong>：<code>~/.claude/</code></li>
</ul>
<p>如果该目录不存在，自己创建：</p>
<pre><code class="language-bash"># Windows (PowerShell)
mkdir $env:USERPROFILE\.claude

# Mac / Linux
mkdir -p ~/.claude</code></pre>
<h3>步骤 2：编写 settings.json</h3>
<p>在 <code>.claude/</code> 目录下新建 <code>settings.json</code>，写入以下内容：</p>
<pre><code class="language-json">{
  "env": {
    "ANTHROPIC_BASE_URL": "https://your-api-proxy.com",
    "ANTHROPIC_API_KEY": "sk-your-api-key-here",
    "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"
  },
  "model": "claude-sonnet-4-20250514"
}</code></pre>
<p>逐项解释：</p>
<table>
<thead>
<tr>
<th>字段</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ANTHROPIC_BASE_URL</code></td>
<td>中转 API 的地址，替代官方 <code>https://api.anthropic.com</code>。<strong>注意不要在后面加 <code>/v1</code> 之类的路径</strong>，程序会自动拼接</td>
</tr>
<tr>
<td><code>ANTHROPIC_API_KEY</code></td>
<td>中转服务给你的 API Key</td>
</tr>
<tr>
<td><code>CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY</code></td>
<td>设为 <code>"1"</code>，让 Claude Code 自动从中转服务获取可用模型列表</td>
</tr>
<tr>
<td><code>model</code></td>
<td>默认使用的模型名称。可以写 <code>"claude-sonnet-4-20250514"</code> 或者其他中转支持的模型</td>
</tr>
</tbody>
</table>
<h3>步骤 3：创建文件的方式</h3>
<p>Windows 用户如果不熟悉命令行，用记事本就行：</p>
<ol>
<li>打开记事本，把上面的 JSON 复制进去，替换 <code>BASE_URL</code> 和 <code>API_KEY</code> 为你的真实值</li>
<li>另存为 → 路径填 <code>C:\Users\你的用户名\.claude\settings.json</code></li>
<li><strong>保存类型选&quot;所有文件&quot;</strong>，编码选 UTF-8</li>
</ol>
<p>Mac / Linux 用户直接用命令：</p>
<pre><code class="language-bash"># 创建并编辑
vim ~/.claude/settings.json</code></pre>
<h2>3.3 环境变量方式（备选）</h2>
<p>除了 <code>settings.json</code>，也可以直接用环境变量，效果一样：</p>
<pre><code class="language-bash"># Windows PowerShell
$env:ANTHROPIC_BASE_URL="https://your-api-proxy.com"
$env:ANTHROPIC_API_KEY="sk-your-api-key-here"
$env:CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY="1"

# Mac / Linux
export ANTHROPIC_BASE_URL="https://your-api-proxy.com"
export ANTHROPIC_API_KEY="sk-your-api-key-here"
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY="1"</code></pre>
<p>不过这种方式只在当前终端窗口有效，关掉就没了。想持久化的话，把 <code>export</code> 写到 <code>~/.bashrc</code> 或 <code>~/.zshrc</code> 里，或者在 settings.json 里配置（推荐后者，集中管理）。</p>
<h1>4 配置 CLAUDE.md</h1>
<p>CLAUDE.md 是给 Claude Code 看的&quot;说明书&quot;。你在这个文件里告诉 Claude：你做什么工作、这个项目是什么、有什么特殊规则——Claude 每次启动都会自动读它，并按照里面的约定来帮你干活。</p>
<p>分两级（跟 settings.json 一样）：</p>
<h2>4.1 项目级 CLAUDE.md</h2>
<p>放在你正在做的项目的<strong>根目录</strong>下。比如你有一个项目叫 <code>my-project/</code>，就在里面放一个 <code>CLAUDE.md</code>。Claude Code 在那个目录启动时会自动加载。</p>
<p>典型场景：</p>
<pre><code>my-project/
├── CLAUDE.md          ← 这个项目的专属指令
├── src/
├── package.json
└── ...</code></pre>
<p>CLAUDE.md 内容示例：</p>
<pre><code class="language-markdown"># 项目说明

这是一个个人博客项目，基于 Next.js 构建。

## 技术栈
- Next.js 14 + TypeScript
- Tailwind CSS
- Contentlayer 管理文章

## 规则
- 所有新组件放 `src/components/`
- 提交前先跑 `npm run lint`
- 不要直接改 `content/` 下的 .mdx，走 Contentlayer 生成</code></pre>
<h2>4.2 用户级 CLAUDE.md</h2>
<p>放在 <code>~/.claude/CLAUDE.md</code>（即 <code>C:\Users\你的用户名\.claude\CLAUDE.md</code>），对所有项目生效。适合放通用的个人偏好和工作习惯：</p>
<pre><code class="language-markdown"># 个人偏好

- 用中文回复
- 写代码先解释思路再动手
- 每次改完代码自动跑测试
- 不要主动新建文件，先问我</code></pre>
<h2>4.3 一个简单的入门模板</h2>
<p>如果你是第一次用，跟我一样只是拿 Claude Code 处理零散任务，用户级 CLAUDE.md 够用了：</p>
<pre><code class="language-markdown"># 通用指令

- 默认用中文交流
- 修改文件前先和我确认
- 代码注释用中文
- 遇到不确定的，列出选项让我选，不要直接做决定</code></pre>
<p>创建后用 <code>claude</code> 命令启动，它会自动读取 CLAUDE.md。你可以问它：&quot;你读到了什么规则？&quot;来验证是否加载成功。</p>
<h1>5 开始使用</h1>
<p>配置完成后，打开终端，进入你的项目目录，输入：</p>
<pre><code class="language-bash">claude</code></pre>
<p>首次启动可能提示权限确认，一路 Yes 就行。启动后你会看到类似这样的界面：</p>
<pre><code>  Claude Code v2.1.217
  Model: claude-sonnet-4-20250514
  API: https://your-api-proxy.com

&gt; </code></pre>
<p>到这里就成功了。试着输入第一个指令：</p>
<pre><code>帮我看看这个项目的结构</code></pre>
<p>按 <code>Ctrl + C</code> 或输入 <code>/exit</code> 退出。</p>
<h1>6 常见问题</h1>
<p><strong>Q: 启动后报 &quot;401 Unauthorized&quot;？</strong><br />
A: API Key 写错了，或者 Base URL 格式不对。检查 settings.json 里的 key 有没有多余空格，Base URL 末尾不要带斜杠。</p>
<p><strong>Q: 报 &quot;Model not found&quot;？</strong><br />
A: model 名称写错了，或者中转服务不支持这个模型。先确认中转给的支持列表，然后把 <code>"model"</code> 字段改成正确的。也可以删掉 model 字段，加上 <code>CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1</code> 让程序自动发现。</p>
<p><strong>Q: 速度很慢？</strong><br />
A: 中转服务走的海外线路，有天然延迟，正常现象。换一个延迟更低的中转商，或者自建 Worker 能改善。</p>
<p><strong>Q: npm install 报错 / 权限不够？</strong><br />
A: Mac/Linux 如果不用 sudo 就报权限错，可以这样修：</p>
<pre><code class="language-bash"># 方法1：修复 npm 全局目录权限（推荐）
mkdir -p ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' &gt;&gt; ~/.bashrc
source ~/.bashrc

# 方法2：直接 sudo（不推荐，但快）
sudo npm install -g @anthropic-ai/claude-code</code></pre>
<p><strong>Q: Windows 上 PowerShell 提示&quot;无法加载文件&quot;？</strong><br />
A: PowerShell 执行策略限制了脚本运行。管理员身份打开 PowerShell，执行：</p>
<pre><code class="language-powershell">Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser</code></pre>
<p>然后重新打开终端即可。</p>
<p><strong>Q: 怎么更新 Claude Code？</strong></p>
<pre><code class="language-bash">npm update -g @anthropic-ai/claude-code</code></pre>
<p><strong>Q: 怎么卸载？</strong></p>
<pre><code class="language-bash">npm uninstall -g @anthropic-ai/claude-code</code></pre>]]></description>
    <pubDate>Thu, 23 Jul 2026 01:20:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/claude_code_install</guid>
</item>
<item>
    <title>01 基础大模型调用</title>
    <link>https://blog.askerlab.com/01_ji_chu_da_mo_xing_diao_yong</link>
    <description><![CDATA[<h1>1 背景</h1>
<p>本节课只解决一个问题：<strong>怎么用最简单的代码调用一次大模型</strong>。<br />
先不要急着学 CoT、ReAct、工具调用、记忆、多 Agent。因为这些能力最后都会回到同一个基础动作：</p>
<pre><code class="language-text">把问题发给大模型
→ 大模型返回答案
→ 程序拿到答案继续处理</code></pre>
<p>如果这一步都不能稳定运行，后面写 Agent 时会很痛苦。</p>
<h2>1.1 本节课要做什么</h2>
<p>本节课只做四件事：</p>
<ol>
<li>说明现在常见的大模型调用方式</li>
<li>说明为什么本节先使用 OpenAI 兼容接口</li>
<li>写一个简单的 <code>llm.py</code></li>
<li>写一个简单的 <code>main.py</code>，运行后能看到模型输出<br />
本节课不追求封装得很完整，也不做太多抽象。代码要做到：<strong>高中生看得懂，复制下来能运行，后面课程能继续复用</strong>。</li>
</ol>
<h1>2 当前有几种大模型调用方式</h1>
<h2>2.1 第一种：官方原生接口</h2>
<p>每个大模型厂商一般都有自己的官方接口。<br />
例如：</p>
<table>
<thead>
<tr>
<th>厂商</th>
<th>常见接口方式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>OpenAI</td>
<td>OpenAI SDK、Responses API、Chat Completions API</td>
<td>OpenAI 自己的官方接口</td>
</tr>
<tr>
<td>Anthropic Claude</td>
<td>Anthropic SDK、Messages API</td>
<td>Claude 原生接口</td>
</tr>
<tr>
<td>Google Gemini</td>
<td>Google Gen AI SDK、Gemini API</td>
<td>Gemini 原生接口</td>
</tr>
</tbody>
</table>
<p>这种方式的好处是能力最完整，坏处是每家写法不完全一样。你刚开始学习时，如果一上来同时适配 OpenAI、Claude、Gemini，很容易被 SDK 差异打断思路。</p>
<h2>2.2 第二种：OpenAI 兼容接口</h2>
<p>很多模型厂商支持 OpenAI 兼容接口。<br />
简单说，就是它们让你继续使用 OpenAI SDK，只需要改三样东西：</p>
<pre><code class="language-text">API Key
Base URL
Model Name</code></pre>
<p>常见支持 OpenAI 兼容接口的平台包括：</p>
<table>
<thead>
<tr>
<th>平台</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>OpenAI</td>
<td>原始 OpenAI 接口</td>
</tr>
<tr>
<td>DeepSeek</td>
<td>支持 OpenAI 兼容格式</td>
</tr>
<tr>
<td>阿里云百炼 Qwen</td>
<td>支持 OpenAI 兼容模式</td>
</tr>
<tr>
<td>Moonshot Kimi</td>
<td>支持 OpenAI 兼容格式</td>
</tr>
<tr>
<td>智谱 GLM</td>
<td>支持 OpenAI 兼容格式</td>
</tr>
</tbody>
</table>
<p>这就是本节课选择 OpenAI 兼容接口的原因：<strong>代码最少，迁移最简单，适合入门</strong>。</p>
<h2>2.3 第三种：统一网关</h2>
<p>还有一种方式是使用 LiteLLM、One API、公司内部网关这类统一代理。<br />
它的作用是把多个模型包装成一个统一入口：</p>
<pre><code class="language-text">业务代码
→ 模型网关
→ OpenAI / DeepSeek / Qwen / Claude / Gemini</code></pre>
<p>这种方式适合团队使用，但不适合第一节课。因为它又多引入了一层系统，初学者很容易不知道问题出在代码、网关还是模型服务。</p>
<h2>2.4 本节课的推荐做法</h2>
<p>本节课推荐：</p>
<pre><code class="language-text">先用 OpenAI SDK
→ 连接一个 OpenAI 兼容模型
→ 跑通最小调用
→ 后面课程全部复用这个 LLM 客户端</code></pre>
<p>这样后面写 CoT 时，只需要关注 prompt 怎么写；写 ReAct 时，只需要关注模型怎么推理和行动；写工具调用时，再扩展工具参数即可。</p>
<h1>3 一次大模型调用需要哪些信息</h1>
<h2>3.1 最少需要三个配置</h2>
<p>一次大模型调用最少需要三个配置：</p>
<table>
<thead>
<tr>
<th>配置</th>
<th>含义</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>LLM_MODEL_ID</code></td>
<td>要调用哪个模型</td>
<td><code>deepseek-chat</code>、<code>qwen-plus</code>、<code>kimi-k2.5</code></td>
</tr>
<tr>
<td><code>LLM_API_KEY</code></td>
<td>你的 API 密钥</td>
<td><code>sk-xxxx</code></td>
</tr>
<tr>
<td><code>LLM_BASE_URL</code></td>
<td>模型服务地址</td>
<td><code>https://api.deepseek.com</code></td>
</tr>
</tbody>
</table>
<p>可以把它理解成点外卖：</p>
<pre><code class="language-text">LLM_MODEL_ID：你要点哪道菜
LLM_API_KEY：证明这是你的账号
LLM_BASE_URL：外卖平台地址</code></pre>
<h2>3.2 为什么配置放到 .env</h2>
<p>不要把 API Key 写死在 Python 代码里。<br />
不推荐：</p>
<pre><code class="language-python">api_key = "sk-xxxx"</code></pre>
<p>推荐：</p>
<pre><code class="language-python">api_key = os.getenv("LLM_API_KEY")</code></pre>
<p>原因很简单：</p>
<ul>
<li>API Key 是密码，不能随便暴露</li>
<li>换模型时不需要改代码</li>
<li>后面部署到服务器时也更方便<br />
所以本节课把 <code>.env</code> 当作配置文件使用，先不引入 <code>config.yaml</code>。对第一节课来说，<code>.env</code> 已经够用了。</li>
</ul>
<h1>4 项目结构</h1>
<h2>4.1 最终目录</h2>
<p>创建一个目录：</p>
<pre><code class="language-text">agent-course-01-llm-call/
├── .env
├── llm.py
├── main.py
└── requirements.txt</code></pre>
<p>每个文件的作用如下：</p>
<table>
<thead>
<tr>
<th>文件</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>.env</code></td>
<td>放模型配置和 API Key</td>
</tr>
<tr>
<td><code>llm.py</code></td>
<td>封装大模型调用</td>
</tr>
<tr>
<td><code>main.py</code></td>
<td>写一个最小运行示例</td>
</tr>
<tr>
<td><code>requirements.txt</code></td>
<td>放 Python 依赖</td>
</tr>
</tbody>
</table>
<h1>5 安装依赖</h1>
<h2>5.1 创建 requirements.txt</h2>
<p>创建 <code>requirements.txt</code>：</p>
<pre><code class="language-txt">openai
python-dotenv</code></pre>
<p>这里只安装两个包：</p>
<ul>
<li><code>openai</code>：用来调用 OpenAI 兼容接口</li>
<li><code>python-dotenv</code>：用来读取 <code>.env</code> 配置</li>
</ul>
<h2>5.2 安装依赖</h2>
<p>执行：</p>
<pre><code class="language-bash">pip install -r requirements.txt</code></pre>
<p>如果你的电脑需要使用 <code>pip3</code>，就执行：</p>
<pre><code class="language-bash">pip3 install -r requirements.txt</code></pre>
<h2>5.3 验证安装</h2>
<p>执行：</p>
<pre><code class="language-bash">python -c "from openai import OpenAI; from dotenv import load_dotenv; print('ok')"</code></pre>
<p>预期输出：</p>
<pre><code class="language-text">ok</code></pre>
<p>如果这里都不能运行，先不要继续写代码，先解决 Python 环境和依赖安装问题。</p>
<h1>6 配置 .env</h1>
<h2>6.1 创建 .env 文件</h2>
<p>创建 <code>.env</code>：</p>
<pre><code class="language-bash">LLM_MODEL_ID=deepseek-chat
LLM_API_KEY=你的API_KEY
LLM_BASE_URL=https://api.deepseek.com
LLM_TIMEOUT=60</code></pre>
<p>把 <code>你的API_KEY</code> 换成真实的 API Key。</p>
<h2>6.2 不同平台的配置示例</h2>
<p>如果你使用 DeepSeek：</p>
<pre><code class="language-bash">LLM_MODEL_ID=deepseek-chat
LLM_API_KEY=你的DeepSeek_API_KEY
LLM_BASE_URL=https://api.deepseek.com
LLM_TIMEOUT=60</code></pre>
<p>如果你使用阿里云百炼 Qwen：</p>
<pre><code class="language-bash">LLM_MODEL_ID=qwen-plus
LLM_API_KEY=你的DASHSCOPE_API_KEY
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_TIMEOUT=60</code></pre>
<p>如果你使用OpenAI：</p>
<pre><code>如果你使用 OpenAI：
```bash
LLM_MODEL_ID=gpt-5-mini
LLM_API_KEY=你的OPENAI_API_KEY
LLM_BASE_URL=https://api.openai.com/v1
LLM_TIMEOUT=60</code></pre>
<p>注意：模型名称可能会变化，最终以对应平台控制台和官方文档为准。如果报 <code>model not found</code>，优先检查 <code>LLM_MODEL_ID</code>。</p>
<h1>7 编写 llm.py</h1>
<h2>7.1 代码目标</h2>
<p><code>llm.py</code> 只做一件事：<strong>把 messages 发给大模型，然后返回文本结果</strong>。<br />
这里保留流式输出。因为流式输出体验更好，运行时能看到模型一个字一个字返回，不会一直卡着没反应。</p>
<h2>7.2 完整代码</h2>
<p>创建 <code>llm.py</code>：</p>
<pre><code class="language-python">import os
from typing import List, Dict, Optional
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
class HelloAgentsLLM:
    """
    一个最小可用的 LLM 客户端。
    只支持 OpenAI 兼容接口。
    """
    def __init__(
        self,
        model: Optional[str] = None,
        api_key: Optional[str] = None,
        base_url: Optional[str] = None,
        timeout: Optional[int] = None,
    ):
        self.model = model or os.getenv("LLM_MODEL_ID")
        self.api_key = api_key or os.getenv("LLM_API_KEY")
        self.base_url = base_url or os.getenv("LLM_BASE_URL")
        self.timeout = timeout or int(os.getenv("LLM_TIMEOUT", "60"))
        if not self.model:
            raise ValueError("缺少模型名称，请在 .env 中配置 LLM_MODEL_ID")
        if not self.api_key:
            raise ValueError("缺少 API Key，请在 .env 中配置 LLM_API_KEY")
        if not self.base_url:
            raise ValueError("缺少服务地址，请在 .env 中配置 LLM_BASE_URL")
        self.client = OpenAI(
            api_key=self.api_key,
            base_url=self.base_url,
            timeout=self.timeout,
        )
    def think(self, messages: List[Dict[str, str]], temperature: float = 0) -&gt; str:
        """
        调用大模型，并返回完整文本。
        messages 是对话列表，例如：
        [
            {"role": "system", "content": "你是一个有帮助的助手"},
            {"role": "user", "content": "你好"}
        ]
        """
        print(f"🧠 正在调用模型：{self.model}")
        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                temperature=temperature,
                stream=True,
            )
            print("✅ 模型返回：")
            result = []
            for chunk in response:
                if not chunk.choices:
                    continue
                content = chunk.choices[0].delta.content
                if not content:
                    continue
                print(content, end="", flush=True)
                result.append(content)
            print()
            return "".join(result)
        except Exception as e:
            print(f"❌ 调用大模型失败：{e}")
            return ""</code></pre>
<h2>7.3 这段代码怎么理解</h2>
<p>这段代码里最重要的只有三部分。<br />
第一，读取配置：</p>
<pre><code class="language-python">self.model = model or os.getenv("LLM_MODEL_ID")
self.api_key = api_key or os.getenv("LLM_API_KEY")
self.base_url = base_url or os.getenv("LLM_BASE_URL")</code></pre>
<p>意思是：如果代码里传了参数，就用代码里的；如果没有传，就从 <code>.env</code> 里读取。<br />
第二，创建客户端：</p>
<pre><code class="language-python">self.client = OpenAI(
    api_key=self.api_key,
    base_url=self.base_url,
    timeout=self.timeout,
)</code></pre>
<p>意思是：告诉 OpenAI SDK，我要访问哪个模型服务。<br />
第三，发送 messages：</p>
<pre><code class="language-python">response = self.client.chat.completions.create(
    model=self.model,
    messages=messages,
    temperature=temperature,
    stream=True,
)</code></pre>
<p>意思是：把对话内容发给模型，并使用流式返回。</p>
<h1>8 编写 main.py</h1>
<h2>8.1 代码目标</h2>
<p><code>main.py</code> 是入口文件，只负责演示怎么使用 <code>HelloAgentsLLM</code>。<br />
它不要复杂，也不要封装太多逻辑。第一节课最重要的是让读者看清楚调用顺序。</p>
<h2>8.2 完整代码</h2>
<p>创建 <code>main.py</code>：</p>
<pre><code class="language-python">from llm import HelloAgentsLLM
def main():
    llm = HelloAgentsLLM()
    messages = [
        {
            "role": "system",
            "content": "你是一个讲解清楚、表达简单的 AI 助手。",
        },
        {
            "role": "user",
            "content": "用高中生能听懂的话解释什么是 LLM Agent。",
        },
    ]
    print("--- 调用 LLM ---")
    response_text = llm.think(messages)
    if response_text:
        print("\n--- 完整模型响应 ---")
        print(response_text)
if __name__ == "__main__":
    main()</code></pre>
<h2>8.3 调用顺序</h2>
<p>这个文件的执行顺序很简单：</p>
<pre><code class="language-text">创建 HelloAgentsLLM
→ 准备 messages
→ 调用 llm.think(messages)
→ 打印模型返回内容</code></pre>
<p>这就是后面所有 Agent 课程的基础。</p>
<h1>9 运行验证</h1>
<h2>9.1 执行命令</h2>
<p>在项目目录下执行：</p>
<pre><code class="language-bash">python main.py</code></pre>
<p>预期输出类似：</p>
<pre><code class="language-text">--- 调用 LLM ---
🧠 正在调用模型：deepseek-chat
✅ 模型返回：
LLM Agent 可以理解成一个由大语言模型驱动的智能助手，它不只是回答问题，还能根据目标规划步骤、调用工具，并根据结果继续完成任务。
--- 完整模型响应 ---
LLM Agent 可以理解成一个由大语言模型驱动的智能助手，它不只是回答问题，还能根据目标规划步骤、调用工具，并根据结果继续完成任务。</code></pre>
<p>只要能看到模型返回内容，就说明本节课完成了。</p>
<h2>9.2 修改问题再运行</h2>
<p>你可以把 <code>main.py</code> 里的问题改成：</p>
<pre><code class="language-python">"写一个 Python 快速排序算法"</code></pre>
<p>再执行：</p>
<pre><code class="language-bash">python main.py</code></pre>
<p>如果模型能返回代码，说明你的调用链路是正常的。</p>
<h1>10 常见坑</h1>
<h2>10.1 .env 没有生效</h2>
<p>如果提示：</p>
<pre><code class="language-text">缺少 API Key，请在 .env 中配置 LLM_API_KEY</code></pre>
<p>先检查 <code>.env</code> 是否在当前目录。<br />
正确目录应该是：</p>
<pre><code class="language-text">agent-course-01-llm-call/
├── .env
├── llm.py
├── main.py
└── requirements.txt</code></pre>
<p>然后检查变量名是不是写错了。<br />
必须是：</p>
<pre><code class="language-bash">LLM_MODEL_ID=xxx
LLM_API_KEY=xxx
LLM_BASE_URL=xxx</code></pre>
<p>不是：</p>
<pre><code class="language-bash">MODEL_ID=xxx
API_KEY=xxx
BASE_URL=xxx</code></pre>
<h2>10.2 base_url 写成了完整接口地址</h2>
<p>这是新手最常见的问题。<br />
如果你使用 SDK，一般配置的是：</p>
<pre><code class="language-bash">LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1</code></pre>
<p>不要写成：</p>
<pre><code class="language-bash">LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions</code></pre>
<p>因为 <code>/chat/completions</code> 这段路径 SDK 会自动拼接。</p>
<h2>10.3 模型名称写错</h2>
<p>如果报错里出现：</p>
<pre><code class="language-text">model not found</code></pre>
<p>一般是模型名不对。<br />
解决办法：</p>
<ol>
<li>去模型平台控制台确认模型名</li>
<li>确认账号是否有这个模型权限</li>
<li>先使用官方示例里的模型名</li>
<li>不要照抄过期文章里的模型名</li>
</ol>
<h2>10.4 API Key 错误</h2>
<p>如果报错里出现：</p>
<pre><code class="language-text">401
unauthorized
invalid api key</code></pre>
<p>一般是 API Key 错了。<br />
先检查：</p>
<ul>
<li>API Key 有没有复制完整</li>
<li>API Key 前后有没有多余空格</li>
<li>当前平台和 API Key 是否匹配</li>
<li>DeepSeek 的 Key 不要拿去调用 Qwen</li>
<li>Qwen 的 Key 不要拿去调用 Kimi</li>
</ul>
<h2>10.5 temperature 设置</h2>
<p>本节课默认：</p>
<pre><code class="language-python">temperature=0</code></pre>
<p>可以简单理解成：让模型尽量稳定回答。<br />
推荐：</p>
<table>
<thead>
<tr>
<th>场景</th>
<th>temperature</th>
</tr>
</thead>
<tbody>
<tr>
<td>学习、测试、写代码</td>
<td><code>0</code></td>
</tr>
<tr>
<td>普通问答</td>
<td><code>0.2</code> 到 <code>0.5</code></td>
</tr>
<tr>
<td>创意写作</td>
<td><code>0.7</code> 左右</td>
</tr>
</tbody>
</table>
<p>刚开始学习 Agent，推荐先用 <code>0</code>。结果稳定，方便排查问题。</p>
<h1>11 本节课最终代码</h1>
<h2>11.1 requirements.txt</h2>
<pre><code class="language-txt">openai
python-dotenv</code></pre>
<h2>11.2 .env</h2>
<pre><code class="language-bash">LLM_MODEL_ID=deepseek-chat
LLM_API_KEY=你的API_KEY
LLM_BASE_URL=https://api.deepseek.com
LLM_TIMEOUT=60</code></pre>
<h2>11.3 llm.py</h2>
<pre><code class="language-python">import os
from typing import List, Dict, Optional
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
class HelloAgentsLLM:
    def __init__(
        self,
        model: Optional[str] = None,
        api_key: Optional[str] = None,
        base_url: Optional[str] = None,
        timeout: Optional[int] = None,
    ):
        self.model = model or os.getenv("LLM_MODEL_ID")
        self.api_key = api_key or os.getenv("LLM_API_KEY")
        self.base_url = base_url or os.getenv("LLM_BASE_URL")
        self.timeout = timeout or int(os.getenv("LLM_TIMEOUT", "60"))
        if not self.model:
            raise ValueError("缺少模型名称，请在 .env 中配置 LLM_MODEL_ID")
        if not self.api_key:
            raise ValueError("缺少 API Key，请在 .env 中配置 LLM_API_KEY")
        if not self.base_url:
            raise ValueError("缺少服务地址，请在 .env 中配置 LLM_BASE_URL")
        self.client = OpenAI(
            api_key=self.api_key,
            base_url=self.base_url,
            timeout=self.timeout,
        )
    def think(self, messages: List[Dict[str, str]], temperature: float = 0) -&gt; str:
        print(f"🧠 正在调用模型：{self.model}")
        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                temperature=temperature,
                stream=True,
            )
            print("✅ 模型返回：")
            result = []
            for chunk in response:
                if not chunk.choices:
                    continue
                content = chunk.choices[0].delta.content
                if not content:
                    continue
                print(content, end="", flush=True)
                result.append(content)
            print()
            return "".join(result)
        except Exception as e:
            print(f"❌ 调用大模型失败：{e}")
            return ""</code></pre>
<h2>11.4 main.py</h2>
<pre><code class="language-python">from llm import HelloAgentsLLM
def main():
    llm = HelloAgentsLLM()
    messages = [
        {
            "role": "system",
            "content": "你是一个讲解清楚、表达简单的 AI 助手。",
        },
        {
            "role": "user",
            "content": "用高中生能听懂的话解释什么是 LLM Agent。",
        },
    ]
    print("--- 调用 LLM ---")
    response_text = llm.think(messages)
    if response_text:
        print("\n--- 完整模型响应 ---")
        print(response_text)
if __name__ == "__main__":
    main()</code></pre>
<h1>12 总结</h1>
<h2>12.1 本节课学到了什么</h2>
<p>本节课只做了一个最小闭环：</p>
<pre><code class="language-text">.env 配置模型
→ llm.py 封装调用
→ main.py 发起请求
→ 控制台看到模型输出</code></pre>
<p>这个闭环很小，但非常重要。后面所有 Agent 能力都会建立在它上面。</p>
<h2>12.2 为什么这样写</h2>
<p>本节课没有做复杂封装，原因是：</p>
<ul>
<li>第一节课的目标是跑通，不是设计框架</li>
<li><code>.env</code> 已经能满足基础配置需求</li>
<li>OpenAI 兼容接口能覆盖很多常见模型</li>
<li><code>llm.think(messages)</code> 这个入口后面可以继续复用<br />
后面进入 CoT 时，可以这样调用：
<pre><code class="language-python">messages = [
{"role": "system", "content": "你是一个擅长一步一步推理的助手。"},
{"role": "user", "content": "请一步一步分析这个问题：..."}
]
llm.think(messages)</code></pre>
<p>也就是说，本节课不是孤立代码，而是后续 Agent 课程的底座。</p></li>
</ul>
<h1>13 参考文献</h1>
<h2>13.1 官方文档</h2>
<ul>
<li>OpenAI API Reference：Chat Completions、Streaming、OpenAI Python SDK</li>
<li>DeepSeek API Docs：OpenAI-compatible API format</li>
<li>阿里云百炼 Model Studio 文档：OpenAI 兼容接口调用千问模型</li>
<li>Moonshot Kimi API 开放平台文档：OpenAI 兼容 API</li>
<li>智谱 AI 开放文档：OpenAI API 兼容接入说明</li>
<li>Anthropic Claude API Docs：Claude Python SDK、Messages API</li>
<li>Google AI for Developers：Gemini API、Google Gen AI SDK</li>
</ul>
<h2>13.2 后续课程</h2>
<ul>
<li>02 CoT：显式步骤推理</li>
<li>03 ReAct：推理和行动交替</li>
<li>04 Tool Use：让模型调用工具</li>
<li>05 Memory：让 Agent 记住上下文</li>
<li>06 Reflection：让 Agent 自我修正</li>
</ul>]]></description>
    <pubDate>Sun, 24 May 2026 00:15:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/01_ji_chu_da_mo_xing_diao_yong</guid>
</item>
<item>
    <title>wireguard 局域网VPN方案</title>
    <link>https://blog.askerlab.com/wireguard_install</link>
    <description><![CDATA[<p>本文用一台云服务器作为 WireGuard 服务端，手机、电脑、CatWrt 作为客户端接入。这个方案最稳定，也最适合普通用户：云服务器有公网 IP，客户端不需要公网 IP，直接连服务器即可。</p>
<h2>一、网络规划</h2>
<p>先统一规划 IP，后面所有配置都按这个来。</p>
<table>
<thead>
<tr>
<th>设备</th>
<th>角色</th>
<th>WireGuard IP</th>
</tr>
</thead>
<tbody>
<tr>
<td>云服务器</td>
<td>服务端</td>
<td>10.8.0.1</td>
</tr>
<tr>
<td>手机</td>
<td>客户端</td>
<td>10.8.0.2</td>
</tr>
<tr>
<td>电脑</td>
<td>客户端</td>
<td>10.8.0.3</td>
</tr>
<tr>
<td>CatWrt</td>
<td>客户端</td>
<td>10.8.0.10</td>
</tr>
<tr>
<td>端口</td>
<td>UDP</td>
<td>51820</td>
</tr>
</tbody>
</table>
<p>需要记住一个原则：一台设备一套密钥，一个独立 IP，不要多个设备共用同一个配置。</p>
<h2>二、服务器安装 WireGuard</h2>
<h3>1. 安装软件</h3>
<p>Ubuntu / Debian 执行：</p>
<pre><code class="language-bash">sudo apt update
sudo apt install -y wireguard qrencode iptables</code></pre>
<p>CentOS / Rocky / AlmaLinux 执行：</p>
<pre><code class="language-bash">sudo dnf install -y wireguard-tools qrencode iptables</code></pre>
<h3>2. 开启 IP 转发</h3>
<p>编辑系统配置：</p>
<pre><code class="language-bash">sudo vim /etc/sysctl.conf</code></pre>
<p>加入或确认有这一行：</p>
<pre><code class="language-bash">net.ipv4.ip_forward=1</code></pre>
<p>让配置立即生效：</p>
<pre><code class="language-bash">sudo sysctl -p</code></pre>
<p>检查结果：</p>
<pre><code class="language-bash">cat /proc/sys/net/ipv4/ip_forward</code></pre>
<p>输出 <code>1</code> 就是成功。</p>
<h3>3. 查看服务器公网网卡</h3>
<p>执行：</p>
<pre><code class="language-bash">ip route | grep default</code></pre>
<p>你会看到类似：</p>
<pre><code class="language-text">default via 172.31.0.1 dev eth0</code></pre>
<p>这里的 <code>eth0</code> 就是公网网卡。也可能是 <code>ens3</code>、<code>ens5</code>、<code>enp1s0</code>，后面配置里的 <code>eth0</code> 要按实际结果替换。</p>
<h3>4. 生成服务端密钥</h3>
<pre><code class="language-bash">sudo mkdir -p /etc/wireguard/clients
cd /etc/wireguard
sudo umask 077
sudo wg genkey | sudo tee server_private.key | sudo wg pubkey | sudo tee server_public.key</code></pre>
<p>查看服务端私钥和公钥：</p>
<pre><code class="language-bash">sudo cat /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_public.key</code></pre>
<p>私钥不要泄露，公钥后面要填到客户端里。</p>
<h3>5. 创建服务端配置</h3>
<p>编辑配置文件：</p>
<pre><code class="language-bash">sudo vim /etc/wireguard/wg0.conf</code></pre>
<p>写入：</p>
<pre><code class="language-ini">[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = 填服务端PrivateKey
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE</code></pre>
<p>注意：如果你的公网网卡不是 <code>eth0</code>，把上面的 <code>eth0</code> 改成你的实际网卡名。</p>
<h3>6. 放行 UDP 端口</h3>
<p>如果服务器用了 UFW：</p>
<pre><code class="language-bash">sudo ufw allow 51820/udp
sudo ufw reload</code></pre>
<p>如果是云服务器，还要去云厂商安全组放行：</p>
<pre><code class="language-text">协议：UDP
端口：51820
来源：0.0.0.0/0</code></pre>
<p>这是最容易漏的一步。WireGuard 用的是 UDP，不是 TCP。</p>
<h3>7. 启动 WireGuard</h3>
<pre><code class="language-bash">sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0</code></pre>
<p>查看状态：</p>
<pre><code class="language-bash">sudo wg show</code></pre>
<p>看到 <code>interface: wg0</code> 就说明服务端启动成功。</p>
<h2>三、添加手机、电脑、CatWrt 客户端</h2>
<h3>1. 生成客户端密钥</h3>
<p>生成手机密钥：</p>
<pre><code class="language-bash">cd /etc/wireguard
sudo wg genkey | sudo tee clients/phone_private.key | sudo wg pubkey | sudo tee clients/phone_public.key</code></pre>
<p>生成电脑密钥：</p>
<pre><code class="language-bash">sudo wg genkey | sudo tee clients/pc_private.key | sudo wg pubkey | sudo tee clients/pc_public.key</code></pre>
<p>生成 CatWrt 密钥：</p>
<pre><code class="language-bash">sudo wg genkey | sudo tee clients/catwrt_private.key | sudo wg pubkey | sudo tee clients/catwrt_public.key</code></pre>
<h3>2. 把客户端加入服务端</h3>
<p>编辑服务端配置：</p>
<pre><code class="language-bash">sudo vim /etc/wireguard/wg0.conf</code></pre>
<p>在文件末尾追加：</p>
<pre><code class="language-ini">[Peer]
# phone
PublicKey = 填phone_public.key里的内容
AllowedIPs = 10.8.0.2/32
[Peer]
# pc
PublicKey = 填pc_public.key里的内容
AllowedIPs = 10.8.0.3/32
[Peer]
# catwrt
PublicKey = 填catwrt_public.key里的内容
AllowedIPs = 10.8.0.10/32</code></pre>
<p>服务端这里一定要写 <code>/32</code>，不要给每个客户端都写 <code>10.8.0.0/24</code>，否则多客户端容易冲突。</p>
<h3>3. 重启服务端</h3>
<pre><code class="language-bash">sudo systemctl restart wg-quick@wg0
sudo wg show</code></pre>
<h2>四、生成客户端配置文件</h2>
<h3>1. 手机配置 phone.conf</h3>
<p>查看需要填的密钥：</p>
<pre><code class="language-bash">sudo cat /etc/wireguard/clients/phone_private.key
sudo cat /etc/wireguard/server_public.key</code></pre>
<p>创建手机配置：</p>
<pre><code class="language-bash">sudo vim /etc/wireguard/clients/phone.conf</code></pre>
<p>写入：</p>
<pre><code class="language-ini">[Interface]
PrivateKey = 填phone_private.key里的内容
Address = 10.8.0.2/32
DNS = 1.1.1.1
MTU = 1420
[Peer]
PublicKey = 填server_public.key里的内容
Endpoint = 你的服务器公网IP或域名:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25</code></pre>
<p><code>AllowedIPs = 0.0.0.0/0</code> 表示手机所有流量都走 WireGuard。如果只想访问 WireGuard 内网，改成：</p>
<pre><code class="language-ini">AllowedIPs = 10.8.0.0/24</code></pre>
<p>生成二维码：</p>
<pre><code class="language-bash">sudo qrencode -t ansiutf8 &lt; /etc/wireguard/clients/phone.conf</code></pre>
<h3>2. 电脑配置 pc.conf</h3>
<p>查看电脑私钥：</p>
<pre><code class="language-bash">sudo cat /etc/wireguard/clients/pc_private.key</code></pre>
<p>创建电脑配置：</p>
<pre><code class="language-bash">sudo vim /etc/wireguard/clients/pc.conf</code></pre>
<p>写入：</p>
<pre><code class="language-ini">[Interface]
PrivateKey = 填pc_private.key里的内容
Address = 10.8.0.3/32
DNS = 1.1.1.1
MTU = 1420
[Peer]
PublicKey = 填server_public.key里的内容
Endpoint = 你的服务器公网IP或域名:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25</code></pre>
<h3>3. CatWrt 配置 catwrt.conf</h3>
<p>查看 CatWrt 私钥：</p>
<pre><code class="language-bash">sudo cat /etc/wireguard/clients/catwrt_private.key</code></pre>
<p>创建 CatWrt 配置：</p>
<pre><code class="language-bash">sudo vim /etc/wireguard/clients/catwrt.conf</code></pre>
<p>写入：</p>
<pre><code class="language-ini">[Interface]
PrivateKey = 填catwrt_private.key里的内容
Address = 10.8.0.10/32
DNS = 1.1.1.1
MTU = 1420
[Peer]
PublicKey = 填server_public.key里的内容
Endpoint = 你的服务器公网IP或域名:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25</code></pre>
<p>如果你第一次配置 CatWrt，建议先把 <code>AllowedIPs</code> 写成：</p>
<pre><code class="language-ini">AllowedIPs = 10.8.0.0/24</code></pre>
<p>确认能连通后，再改成：</p>
<pre><code class="language-ini">AllowedIPs = 0.0.0.0/0</code></pre>
<p>这样可以避免路由器一启用 WireGuard 就全家断网。</p>
<h2>五、手机设置 WireGuard</h2>
<h3>1. iPhone / iPad</h3>
<p>在 App Store 安装 <code>WireGuard</code>，打开 App，点击 <code>+</code>，选择扫码导入，扫描服务器生成的二维码，保存后打开开关。</p>
<h3>2. Android</h3>
<p>安装 <code>WireGuard</code> App，点击 <code>+</code>，选择扫码导入或导入配置文件，保存后打开开关。</p>
<h3>3. 手机测试</h3>
<p>手机打开 WireGuard 后，访问查 IP 网站。如果显示的是服务器公网 IP，说明手机流量已经走服务器。如果只是访问内网，可以测试：</p>
<pre><code class="language-bash">ping 10.8.0.1</code></pre>
<h2>六、电脑设置 WireGuard</h2>
<h3>1. Windows</h3>
<p>安装 WireGuard 官方客户端，点击 <code>Import tunnel(s) from file</code>，选择 <code>pc.conf</code>，然后点击 <code>Activate</code>。</p>
<h3>2. macOS</h3>
<p>在 App Store 安装 WireGuard，导入 <code>pc.conf</code>，然后启用。</p>
<h3>3. Linux</h3>
<p>安装：</p>
<pre><code class="language-bash">sudo apt update
sudo apt install -y wireguard</code></pre>
<p>复制配置：</p>
<pre><code class="language-bash">sudo cp pc.conf /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/wg0.conf</code></pre>
<p>启动：</p>
<pre><code class="language-bash">sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0</code></pre>
<p>查看：</p>
<pre><code class="language-bash">sudo wg show</code></pre>
<h2>七、CatWrt 设置 WireGuard</h2>
<p>CatWrt 可以按 OpenWrt 的方式配置。这里推荐把 CatWrt 当客户端，连接云服务器。</p>
<h3>1. 安装 WireGuard 组件</h3>
<p>SSH 登录 CatWrt，执行：</p>
<pre><code class="language-bash">opkg update
opkg install luci-proto-wireguard wireguard-tools kmod-wireguard qrencode</code></pre>
<p>安装后重启：</p>
<pre><code class="language-bash">reboot</code></pre>
<p>如果提示 <code>kmod-wireguard</code> 内核版本不匹配，不要硬装。通常是固件和软件源不匹配，需要换匹配的软件源或升级 CatWrt 固件。</p>
<h3>2. 新建 WireGuard 接口</h3>
<p>进入 CatWrt 后台：</p>
<pre><code class="language-text">网络 -&gt; 接口 -&gt; 添加新接口</code></pre>
<p>设置：</p>
<pre><code class="language-text">名称：wg_vps
协议：WireGuard VPN</code></pre>
<p>如果没有 <code>WireGuard VPN</code> 选项，说明插件没装好，或装完没有重启。</p>
<h3>3. 填写接口信息</h3>
<p>在 <code>wg_vps</code> 接口中填写：</p>
<pre><code class="language-text">私钥：catwrt_private.key 的内容
IP 地址：10.8.0.10/32
MTU：1420
监听端口：留空</code></pre>
<p>CatWrt 作为客户端时，监听端口可以留空。</p>
<h3>4. 添加服务端 Peer</h3>
<p>在 <code>对端 / Peers</code> 里添加：</p>
<pre><code class="language-text">公钥：server_public.key 的内容
端点主机：服务器公网IP或域名
端点端口：51820
允许的 IP：10.8.0.0/24 或 0.0.0.0/0
持久 KeepAlive：25</code></pre>
<p>如果只是测试，允许的 IP 先写：</p>
<pre><code class="language-text">10.8.0.0/24</code></pre>
<p>如果想让 CatWrt 下的设备都走 WireGuard，再改成：</p>
<pre><code class="language-text">0.0.0.0/0</code></pre>
<p>如果页面有 <code>Route Allowed IPs / 路由允许的 IP</code>，需要勾选。</p>
<h3>5. 设置防火墙区域</h3>
<p>如果只是让 CatWrt 自己访问 WireGuard，把 <code>wg_vps</code> 放到 <code>lan</code> 区域即可。<br />
如果想让 CatWrt 下面的设备通过 WireGuard 上网，建议把 <code>wg_vps</code> 放到 <code>wan</code> 区域，因为 <code>wan</code> 通常已经开启 NAT，并允许 <code>lan -&gt; wan</code> 转发。<br />
如果你新建了 <code>vpn</code> 区域，需要确认：</p>
<pre><code class="language-text">lan -&gt; vpn：允许转发
vpn：开启 Masquerading
vpn：允许输出</code></pre>
<p>普通用户建议先放到 <code>wan</code> 区域，少踩坑。</p>
<h3>6. CatWrt 测试</h3>
<p>SSH 到 CatWrt：</p>
<pre><code class="language-bash">wg show
ping 10.8.0.1</code></pre>
<p>如果看到 <code>latest handshake</code>，说明已经连上。如果 CatWrt 配的是全局流量，可以测试出口 IP：</p>
<pre><code class="language-bash">curl ifconfig.me</code></pre>
<p>显示服务器公网 IP，说明 CatWrt 已经通过 WireGuard 出口上网。</p>
<h2>八、常见坑</h2>
<h3>1. 没有握手</h3>
<p>优先检查：云服务器安全组是否放行 UDP 51820，服务器防火墙是否放行 UDP 51820，客户端 Endpoint 是否写错，服务端是否添加了对应客户端 Peer，公钥是否填反。</p>
<h3>2. 有握手但不能上网</h3>
<p>检查服务器是否开启转发：</p>
<pre><code class="language-bash">cat /proc/sys/net/ipv4/ip_forward</code></pre>
<p>必须输出：</p>
<pre><code class="language-text">1</code></pre>
<p>再检查 NAT 网卡名是否正确：</p>
<pre><code class="language-bash">ip route | grep default</code></pre>
<p>如果实际网卡是 <code>ens3</code>，配置里却写 <code>eth0</code>，就会连上但不能上网。</p>
<h3>3. 多个客户端互相顶掉</h3>
<p>原因通常是多个设备用了同一个配置。每台设备都要有独立 <code>PrivateKey</code> 和独立 <code>Address</code>。</p>
<h3>4. CatWrt 一启用就断网</h3>
<p>先把 CatWrt 的 <code>AllowedIPs</code> 从：</p>
<pre><code class="language-ini">0.0.0.0/0</code></pre>
<p>改成：</p>
<pre><code class="language-ini">10.8.0.0/24</code></pre>
<p>确认基础连接正常后，再考虑全局代理。旁路由不会自动接管全家流量，如果 CatWrt 是旁路由，还需要让设备网关指向 CatWrt，或者使用策略路由。</p>
<h3>5. 能连但网页打不开</h3>
<p>优先检查 DNS。客户端可以先写：</p>
<pre><code class="language-ini">DNS = 1.1.1.1</code></pre>
<p>如果某些网络下不稳定，保留：</p>
<pre><code class="language-ini">PersistentKeepalive = 25</code></pre>
<p>必要时把 MTU 改小：</p>
<pre><code class="language-ini">MTU = 1280</code></pre>
<h2>九、新增客户端接入时怎么修改</h2>
<p>后面如果要新增手机、电脑、平板、路由器，不需要重装 WireGuard，只需要做三件事：生成新客户端密钥、服务端增加一个 Peer、生成新客户端配置。</p>
<h3>1. 规划一个新 IP</h3>
<p>先给新设备分配一个没有用过的 WireGuard IP。例如前面已经用了：</p>
<pre><code class="language-text">手机：10.8.0.2
电脑：10.8.0.3
CatWrt：10.8.0.10</code></pre>
<p>新增一台平板，可以用：</p>
<pre><code class="language-text">平板：10.8.0.4</code></pre>
<p>不要和已有设备重复。</p>
<h3>2. 生成新客户端密钥</h3>
<p>假设新客户端叫 <code>pad</code>：</p>
<pre><code class="language-bash">cd /etc/wireguard
sudo bash -c 'umask 077; wg genkey | tee /etc/wireguard/pad_private.key | wg pubkey &gt; /etc/wireguard/pad_public.key'</code></pre>
<p>查看新客户端公钥：</p>
<pre><code class="language-bash">sudo cat /etc/wireguard/pad_public.key</code></pre>
<h3>3. 修改服务端配置</h3>
<p>编辑服务端配置：</p>
<pre><code class="language-bash">sudo vim /etc/wireguard/wg0.conf</code></pre>
<p>在文件末尾追加：</p>
<pre><code class="language-ini">[Peer]
# pad
PublicKey = 填pad_public.key里的内容
AllowedIPs = 10.8.0.4/32</code></pre>
<p>注意：服务端这里只需要新增这个客户端的 <code>[Peer]</code>，不要修改已有手机、电脑、CatWrt 的配置。</p>
<h3>4. 重启 WireGuard</h3>
<pre><code class="language-bash">sudo systemctl restart wg-quick@wg0</code></pre>
<p>查看状态：</p>
<pre><code class="language-bash">sudo wg show</code></pre>
<p>重启时已有客户端会短暂断开，几秒后会自动恢复。普通用户用这种方式最简单。</p>
<h3>5. 生成新客户端配置</h3>
<p>查看服务端公钥和新客户端私钥：</p>
<pre><code class="language-bash">sudo cat /etc/wireguard/server_public.key
sudo cat /etc/wireguard/clients/pad_private.key</code></pre>
<p>创建新客户端配置：</p>
<pre><code class="language-bash">sudo vim /etc/wireguard/clients/pad.conf</code></pre>
<p>写入：</p>
<pre><code class="language-ini">[Interface]
PrivateKey = 填pad_private.key里的内容
Address = 10.8.0.4/32
DNS = 1.1.1.1
MTU = 1420
[Peer]
PublicKey = 填server_public.key里的内容
Endpoint = 你的服务器公网IP或域名:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25</code></pre>
<p>如果这个客户端只想访问 WireGuard 内网，不想让所有流量都走服务器，把：</p>
<pre><code class="language-ini">AllowedIPs = 0.0.0.0/0</code></pre>
<p>改成：</p>
<pre><code class="language-ini">AllowedIPs = 10.8.0.0/24</code></pre>
<h3>6. 导入新客户端</h3>
<p>手机或平板可以生成二维码：</p>
<pre><code class="language-bash">sudo qrencode -t ansiutf8 &lt; /etc/wireguard/clients/pad.conf</code></pre>
<p>然后用 WireGuard App 扫码导入。电脑客户端则直接导入 <code>pad.conf</code> 文件。</p>
<h3>7. 新增客户端时最容易出错的地方</h3>
<p>新增客户端时，只记住这几条：</p>
<pre><code class="language-text">1. 新设备必须用新的 PrivateKey 和 PublicKey
2. 新设备必须用新的 Address，不能和旧设备重复
3. 服务端必须新增一个对应的 [Peer]
4. 服务端 Peer 里的 AllowedIPs 要写这个客户端的 /32
5. 原有客户端配置不用改</code></pre>
<p>例如新增 <code>pad</code>，服务端只新增：</p>
<pre><code class="language-ini">[Peer]
# pad
PublicKey = pad的PublicKey
AllowedIPs = 10.8.0.4/32</code></pre>
<p>客户端只使用：</p>
<pre><code class="language-ini">Address = 10.8.0.4/32</code></pre>
<p>不要复制旧手机的配置直接用，否则两个设备会互相顶掉。</p>]]></description>
    <pubDate>Sat, 23 May 2026 16:57:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/wireguard_install</guid>
</item>
<item>
    <title>tmux安装与claude监控安装</title>
    <link>https://blog.askerlab.com/tmux_cluade_tmux</link>
    <description><![CDATA[<p>用 tmux + claude-tmux + claude-tui 管理多个 Claude Code 终端任务</p>
<pre><code class="language-text">tmux        负责多终端会话持久化
claude-tmux 负责查看多个 Claude Code 会话状态
claude-tui  负责查看上下文、成本、工具调用、session 统计</code></pre>
<h2>一、环境说明</h2>
<p>本文以 Ubuntu / Debian / WSL / SSH 终端环境为主。<br />
先确认 Claude Code 已经能用：</p>
<pre><code class="language-bash">claude --version</code></pre>
<p>如果这里都不能运行，先把 Claude Code 装好，再继续后面的步骤。</p>
<h2>二、安装 tmux</h2>
<h3>Ubuntu / Debian</h3>
<pre><code class="language-bash">sudo apt update
sudo apt install -y tmux curl git build-essential</code></pre>
<h3>macOS</h3>
<pre><code class="language-bash">brew install tmux</code></pre>
<p>验证：</p>
<pre><code class="language-bash">tmux -V</code></pre>
<h2>三、安装 Rust / Cargo</h2>
<p><code>claude-tmux</code> 推荐通过 Cargo 安装，所以需要 Rust 工具链。<br />
先检查：</p>
<pre><code class="language-bash">cargo --version</code></pre>
<p>如果没有 Cargo，安装 Rust，如果没有魔法可能安装失败，可以部分用国内安装：</p>
<pre><code class="language-bash">curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
env RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup \
    RUSTUP_UPDATE_ROOT=https://mirrors.tuna.tsinghua.edu.cn/rustup/rustup \
    sh -s -- -y --profile minimal

source "$HOME/.cargo/env"</code></pre>
<p>验证：</p>
<pre><code class="language-bash">cargo --version</code></pre>
<h2>四、安装 claude-tmux</h2>
<p>执行：</p>
<pre><code class="language-bash">cargo install claude-tmux</code></pre>
<p>确认二进制文件存在：</p>
<pre><code class="language-bash">which claude-tmux
ls -lh ~/.cargo/bin/claude-tmux</code></pre>
<p>正常路径一般是：</p>
<pre><code class="language-text">/home/你的用户名/.cargo/bin/claude-tmux</code></pre>
<h2>五、配置 tmux 快捷键打开 claude-tmux</h2>
<p>编辑 tmux 配置：</p>
<pre><code class="language-bash">vim ~/.tmux.conf</code></pre>
<p>加入这一行：</p>
<pre><code class="language-bash">bind-key C-c display-popup -E -w 80 -h 30 "~/.cargo/bin/claude-tmux"</code></pre>
<p>重载配置：</p>
<pre><code class="language-bash">tmux source-file ~/.tmux.conf</code></pre>
<p>进入 tmux：</p>
<pre><code class="language-bash">tmux</code></pre>
<p>然后按：</p>
<pre><code class="language-text">Ctrl-b  Ctrl-c</code></pre>
<p>就会弹出 <code>claude-tmux</code> 面板。<br />
<code>claude-tmux</code> 常见状态：</p>
<pre><code class="language-text">● Working           Claude 正在工作
○ Idle              Claude 空闲，等你输入
◐ Waiting for input 等待权限确认，例如 [y/n]
? Unknown           不是 Claude 会话，或者状态无法判断</code></pre>
<h2>六、安装 claude-tui</h2>
<p><code>claude-tui</code> 的坑比较多，核心要求是：</p>
<pre><code class="language-text">Python 3.13+</code></pre>
<p>先检查当前 Python：</p>
<pre><code class="language-bash">python3 --version</code></pre>
<p>如果输出是 <code>Python 3.13.x</code>，可以直接安装：</p>
<pre><code class="language-bash">curl -sSL https://raw.githubusercontent.com/slima4/claude-tui/main/install.sh | bash</code></pre>
<p>安装后执行：</p>
<pre><code class="language-bash">claudetui setup
claudetui mode full</code></pre>
<p>验证：</p>
<pre><code class="language-bash">which claudetui
claudetui --help</code></pre>
<h2>七、如果 Python 版本不是 3.13+</h2>
<p>很多 Ubuntu 机器默认是 Python 3.10 或 3.12。不要直接替换系统的 <code>/usr/bin/python3</code>，否则可能影响 apt 和系统脚本。<br />
推荐做法：只在当前用户环境里使用 Python 3.13。<br />
先检查：</p>
<pre><code class="language-bash">python3.13 --version
which python3.13</code></pre>
<p>如果能看到 Python 3.13，执行：</p>
<pre><code class="language-bash">mkdir -p ~/.local/bin
ln -sf "$(command -v python3.13)" ~/.local/bin/python3
grep -q 'export PATH="$HOME/.local/bin:$PATH"' ~/.bashrc || echo 'export PATH="$HOME/.local/bin:$PATH"' &gt;&gt; ~/.bashrc
source ~/.bashrc
hash -r</code></pre>
<p>验证：</p>
<pre><code class="language-bash">which python3
python3 --version</code></pre>
<p>你应该看到类似：</p>
<pre><code class="language-text">/home/你的用户名/.local/bin/python3
Python 3.13.x</code></pre>
<p>然后重新安装 <code>claude-tui</code>：</p>
<pre><code class="language-bash">curl -sSL https://raw.githubusercontent.com/slima4/claude-tui/main/install.sh | bash
claudetui setup
claudetui mode full</code></pre>
<h2>八、常见坑</h2>
<h3>1：代理环境变量导致 curl 失败</h3>
<p>如果你看到类似错误：</p>
<pre><code class="language-text">curl: (97) Can't complete SOCKS5 connection</code></pre>
<p>或者明明没有主动加代理，但 curl 自动走了 socks/http 代理，先检查环境变量：</p>
<pre><code class="language-bash">env | grep -i proxy</code></pre>
<p>也可以逐个 echo：</p>
<pre><code class="language-bash">echo "http_proxy=$http_proxy"
echo "https_proxy=$https_proxy"
echo "all_proxy=$all_proxy"
echo "HTTP_PROXY=$HTTP_PROXY"
echo "HTTPS_PROXY=$HTTPS_PROXY"
echo "ALL_PROXY=$ALL_PROXY"</code></pre>
<p>临时清掉当前终端代理：</p>
<pre><code class="language-bash">unset http_proxy
unset https_proxy
unset all_proxy
unset HTTP_PROXY
unset HTTPS_PROXY
unset ALL_PROXY</code></pre>
<p>再次确认：</p>
<pre><code class="language-bash">env | grep -i proxy</code></pre>
<p>如果没有输出，说明当前 shell 的代理已经清掉。<br />
重新测试：</p>
<pre><code class="language-bash">curl -I --max-time 10 https://astral.sh
curl -I --max-time 10 https://raw.githubusercontent.com</code></pre>
<p>如果你想查代理从哪里自动注入：</p>
<pre><code class="language-bash">grep -nEi 'proxy|1080|8080|7890|socks' ~/.bashrc ~/.profile ~/.bash_profile ~/.zshrc 2&gt;/dev/null</code></pre>
<hr />
<h3>2：apt 代理配置导致 502 Bad Gateway</h3>
<p>如果你给 apt 配过代理，比如：</p>
<pre><code class="language-text">Acquire::http::Proxy "http://127.0.0.1:8080/";
Acquire::https::Proxy "http://127.0.0.1:8080/";</code></pre>
<p>然后出现：</p>
<pre><code class="language-text">502 Bad Gateway [IP: 127.0.0.1 8080]
仓库 InRelease 的签名不再生效</code></pre>
<p>这通常不是 Ubuntu 源坏了，而是 apt 拿到的是代理返回的 502 页面，不是真正的 InRelease 文件。<br />
先删掉 apt 代理：</p>
<pre><code class="language-bash">sudo rm -f /etc/apt/apt.conf.d/99proxy
sudo apt clean</code></pre>
<p>确认没有残留：</p>
<pre><code class="language-bash">ls /etc/apt/apt.conf.d/*proxy* 2&gt;/dev/null
cat /etc/apt/apt.conf.d/*proxy* 2&gt;/dev/null</code></pre>
<p>然后重新测试：</p>
<pre><code class="language-bash">sudo apt update</code></pre>
<p>如果你的网络必须走代理，先确认代理端口真的可用。<br />
测试 HTTP 代理：</p>
<pre><code class="language-bash">curl -v --max-time 10 -x http://127.0.0.1:8080 https://www.cloudflare.com/cdn-cgi/trace</code></pre>
<p>测试 SOCKS5 代理：</p>
<pre><code class="language-bash">curl -v --max-time 10 --socks5-hostname 127.0.0.1:1080 https://www.cloudflare.com/cdn-cgi/trace</code></pre>
<p>如果 8080 返回 502，说明这个 HTTP 代理出口不可用，不要给 apt 配 8080。<br />
如果 1080 可用，可以尝试 apt 使用 socks5h：</p>
<pre><code class="language-bash">sudo tee /etc/apt/apt.conf.d/99proxy &lt;&lt;'EOF'
Acquire::ForceIPv4 "true";
Acquire::http::Proxy "socks5h://127.0.0.1:1080/";
Acquire::https::Proxy "socks5h://127.0.0.1:1080/";
EOF</code></pre>
<p>然后：</p>
<pre><code class="language-bash">sudo apt clean
sudo apt update</code></pre>
<p>不用代理时，删除它：</p>
<pre><code class="language-bash">sudo rm -f /etc/apt/apt.conf.d/99proxy
sudo apt clean</code></pre>
<h3>3：deadsnakes PPA 连接超时</h3>
<p>如果你用 deadsnakes PPA 安装 Python 3.13，可能遇到：</p>
<pre><code class="language-text">连接超时
无法连接 ppa.launchpadcontent.net
IPv6 网络不可达</code></pre>
<p>这时有三个选择：</p>
<h4>选择 1：优先用 uv，绕开 apt PPA</h4>
<pre><code class="language-bash">curl -LsSf https://astral.sh/uv/install.sh | sh
source "$HOME/.local/bin/env" 2&gt;/dev/null || export PATH="$HOME/.local/bin:$PATH"
uv python install 3.13</code></pre>
<h4>选择 2：修好代理后再 apt install</h4>
<p>确认代理可用后再执行：</p>
<pre><code class="language-bash">sudo apt update
sudo apt install -y python3.13 python3.13-venv libpython3.13-stdlib</code></pre>
<h4>选择 3：不用 claude-tui，先只用 tmux + claude-tmux</h4>
<p>如果网络暂时修不好，<code>claude-tmux</code> 已经足够看多个 Claude 是否 working、idle、waiting。</p>
<h2>九、推荐的 tmux 工作流</h2>
<p>创建多个 Claude 任务 session：</p>
<pre><code class="language-bash">tmux new -s claude-api
tmux new -s claude-web
tmux new -s claude-test
tmux new -s claude-monitor</code></pre>
<p>在前三个 session 里启动 Claude：</p>
<pre><code class="language-bash">claude</code></pre>
<p>在 monitor session 里启动 claude-tui：</p>
<pre><code class="language-bash">claudetui monitor</code></pre>
<p>最终结构类似：</p>
<pre><code class="language-text">tmux
├── claude-api      # Claude 任务 1
├── claude-web      # Claude 任务 2
├── claude-test     # Claude 任务 3
└── claude-monitor  # claudetui monitor</code></pre>
<p>日常命令：</p>
<pre><code class="language-bash">tmux ls
tmux attach -t claude-api
tmux attach -t claude-web
tmux attach -t claude-test</code></pre>
<p>在任意 tmux session 里打开 <code>claude-tmux</code>：</p>
<pre><code class="language-text">Ctrl-b  Ctrl-c</code></pre>
<p>打开 <code>claude-tui</code> 实时监控：</p>
<pre><code class="language-bash">claudetui monitor</code></pre>
<p>查看历史统计：</p>
<pre><code class="language-bash">claudetui stats</code></pre>
<p>查看 session 列表：</p>
<pre><code class="language-bash">claudetui sessions list</code></pre>
<h2>十、最终配置</h2>
<p>我的推荐组合是：</p>
<pre><code>vim ~/.tmux.conf</code></pre>
<p>配置：</p>
<pre><code># Ctrl+a c      新建 window
# Ctrl+a d      detach
# Ctrl+a |      左右分屏
# Ctrl+a -      上下分屏
# Alt+h/j/k/l   直接切 pane
# 鼠标点击       选择 pane
# 鼠标拖边框     调整 pane 大小
# 把 prefix 从 Ctrl+b 改成 Ctrl+a

unbind C-b
set -g prefix C-a
bind C-a send-prefix

# 开启鼠标
set -g mouse on
setw -g mode-keys vi
# 鼠标左键拖选：自动进入 copy-mode，并限定在当前 pane
bind-key -T root MouseDrag1Pane if-shell -F "#{pane_in_mode}" "send-keys -M" "copy-mode -M"

# 鼠标松开：复制并退出 copy-mode
bind-key -T copy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel

bind-key -T copy-mode-vi v send -X begin-selection
bind-key -T copy-mode-vi y send -X copy-selection-and-cancel
bind-key -T copy-mode-vi Enter send -X copy-selection-and-cancel

set -g set-clipboard on

# 按 r 重新加载配置
bind r source-file ~/.tmux.conf \; display-message "tmux.conf reloaded"

# 更容易记的分屏
bind h split-window -h
bind v split-window -v

# Alt + hjkl 直接切换 pane，不需要先按 prefix
bind -n M-h select-pane -L
bind -n M-j select-pane -D
bind -n M-k select-pane -U
bind -n M-l select-pane -R

# Alt + 方向键也可以切换 pane
bind -n M-Left select-pane -L
bind -n M-Down select-pane -D
bind -n M-Up select-pane -U
bind -n M-Right select-pane -R

# Alt + 数字切 window
bind -n M-1 select-window -t 1
bind -n M-2 select-window -t 2
bind -n M-3 select-window -t 3
bind -n M-4 select-window -t 4
bind -n M-5 select-window -t 5

# 绑定claude-tmux
bind-key C-c display-popup -E -w 80 -h 30 "~/.cargo/bin/claude-tmux"
</code></pre>]]></description>
    <pubDate>Wed, 13 May 2026 20:16:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/tmux_cluade_tmux</guid>
</item>
<item>
    <title>【1】openclaw 安装部署</title>
    <link>https://blog.askerlab.com/install_openclaw</link>
    <description><![CDATA[<h1>安装和部署</h1>
<h1>常用命令</h1>
<pre><code>openclaw gateway stop
openclaw gateway start</code></pre>
<p>ssh代理远程访问</p>
<pre><code>ssh -N -L 18789:127.0.0.1:18789 claw@192.168.100.104</code></pre>
<p>登录：<br />
查看.openclaw/openclaw.json中token秘钥，然后登录：</p>
<pre><code>http://localhost:18789/chat?token=XXXX</code></pre>]]></description>
    <pubDate>Thu, 12 Mar 2026 00:30:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/install_openclaw</guid>
</item>
<item>
    <title>自制追剧助手：阿里云盘的便捷使用技巧.md</title>
    <link>https://blog.askerlab.com/aliautosave_use</link>
    <description><![CDATA[<h1>1. 背景</h1>
<p>最近正好在使用figma的make功能，正巧代码工具又层出不穷，除了我常用的cursor， codex、claude code、gemini-cli等Agent代码工具迎来了重大升级，正巧我使用阿里云盘在追剧，痛点是当有新剧增加的时候，别人是分享到网盘链接里的，每次我都需要找到对应的网盘，然后保存到我的阿里云盘里。如果是已更新完的剧还好，如果是正在更新的剧，我还需要记住对应的链接，没事点开看看有没有更新，然后转存到我的网盘里。所以正好想开发一个小工具，能够根据链接进行转存，并且定时查看分享链接里的内容有没有更新，如果更新以后则转存到我的目标云盘里。<br />
因此做了一个阿里云盘自动转存的功能，目前支持的功能如下：</p>
<ul>
<li><strong>支持阿里云盘登录、查看存储状态</strong></li>
<li><strong>支持阿里云盘通过分享链接转存</strong></li>
<li><strong>支持转存为特定的名字</strong></li>
<li><strong>支持定时转存设置</strong></li>
<li><strong>支持对订阅内容进行编辑、暂停、删除</strong></li>
<li><strong>支持查看转存日志</strong><br />
<code>本项目完全没有主动编写任何代码，全程使用和大模型对话方式进行实现，本次只是说明如何使用该软件，后续我会将如何用figma+codex自动化编写代码进行分享。</code></li>
</ul>
<h1>2. 软件安装与使用</h1>
<h2>2.1 安装</h2>
<p>目前已经上传到docker hub，建议直接使用docker进行安装，也可以访问我的github，按照github进行安装</p>
<pre><code>https://github.com/apostle9891/aliautosave</code></pre>
<p>使用docker进行安装</p>
<pre><code class="language-docker"># 拉取aliautosave
docker pull apostle9891/aliautosave:latest

# 运行docker，其中/path要改成你的path
docker run -d --name aliautosave -p 8144:8144 -v /path/aliautosave_data:/app/data apostle9891/aliautosave:latest</code></pre>
<p>如果是网页docker，可以直接搜索：<code>aliautosave</code><br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/53f41764691593.png" alt="" /><br />
点击下载，并进行安装，安装过程中端口映射为<code>8144</code>，存储位置替换<code>/app/data</code><br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/a8561764691593.png" alt="" /></p>
<h2>2.2 软件使用</h2>
<p>当安装完成后，可以打开对应的<code>IP:8144</code>,比如我的网页是<code>192.168.100.101:8144</code><br />
打开以后，点击扫码登录阿里云盘<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/88d81764691593.png" alt="" /><br />
打开阿里云盘，登录阿里云盘<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/5ba21764691593.png" alt="" /><br />
登录完毕后，可以看到对应的云盘信息<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/7a371764691593.png" alt="" /><br />
点击新增订阅，填入需要新增的分享链接<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/d9691764691594.png" alt="" /><br />
可以看到拉取到对应的云盘里的电视剧信息，支持多种自动命名规则，会自动提取里面的名字、集数、季数、扩展信息等，然后转为标准的姓名，目前支持：</p>
<ul>
<li><code>{title}.S{season:02}E{episode:02}{ext}</code></li>
<li><code>{title}.E{episode:02}{ext}</code></li>
<li><code>{name}{ext}</code></li>
<li><code>{title}.{index:02}{ext}</code><br />
提取出来以后，我们可以自定义命名title，比如我们这次的电视剧叫做<strong>大生意</strong>，那我们改为<strong>大生意人</strong>。<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/61b51764691594.png" alt="" /><br />
可以看到整体预览名字改成了大生意人<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/83921764691594.png" alt="" /><br />
选择对应的目标文件夹<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/cb841764691594.png" alt="" /><br />
支持新建文件夹<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/f9941764691594.png" alt="" /><br />
选择定时的策略，默认选择立即订阅并开始复制，会对内容进行复制<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/b44c1764691595.png" alt="" /><br />
点击后即可立即进行复制<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/e2241764691595.png" alt="" /><br />
支持暂停、编辑、删除、查看转存日志<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/add31764691595.png" alt="" /><br />
暂停<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/2ca51764691595.png" alt="" /><br />
日志<br />
<img src="https://blog.askerlab.com/content/uploadfile/202512/6aae1764691595.png" alt="" /></li>
</ul>
<h1>结尾</h1>
<p>大模型能力越来越强了，如果说在2023年我第一次开通vscode的copilot还是属于辅助编程，去年我买了cursor属于自定义编程，现在从6月份开始就真正可以叫做Agent编程了，可以全程通过对话进行编程，以后只需要一个ideal，就可以实现自己的想法。</p>]]></description>
    <pubDate>Tue, 02 Dec 2025 23:25:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/aliautosave_use</guid>
</item>
<item>
    <title>提示词维度测试仪</title>
    <link>https://blog.askerlab.com/prompt_test-2</link>
    <description><![CDATA[<h1>提示词维度测试仪</h1>
<h2>背景</h2>
<p>当我们使用大模型对ASR（语音识别）文本进行总结时，如何判断总结质量的好坏？这里提供一个简单的测试框架。</p>
<h2>核心测试维度</h2>
<h3>1. 完整性 ✅</h3>
<blockquote>
<p>原文的关键信息是否都被覆盖？</p>
</blockquote>
<table>
<thead>
<tr>
<th>检查项</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>核心观点</td>
<td>主要论点是否保留</td>
</tr>
<tr>
<td>关键数据</td>
<td>重要数字、时间是否准确</td>
</tr>
<tr>
<td>人物/事件</td>
<td>关键角色是否提及</td>
</tr>
</tbody>
</table>
<h3>2. 准确性 🎯</h3>
<blockquote>
<p>总结内容是否与原文一致？</p>
</blockquote>
<table>
<thead>
<tr>
<th>检查项</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>事实正确</td>
<td>没有张冠李戴</td>
</tr>
<tr>
<td>无幻觉</td>
<td>没有凭空编造内容</td>
</tr>
<tr>
<td>语义保真</td>
<td>没有曲解原意</td>
</tr>
</tbody>
</table>
<h3>3. 简洁性 📝</h3>
<blockquote>
<p>是否做到了有效压缩？</p>
</blockquote>
<table>
<thead>
<tr>
<th>检查项</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>去除冗余</td>
<td>口语化内容已精简</td>
</tr>
<tr>
<td>结构清晰</td>
<td>层次分明，易于阅读</td>
</tr>
<tr>
<td>长度合理</td>
<td>压缩比适当（通常10%-30%）</td>
</tr>
</tbody>
</table>
<h3>4. 可读性 👀</h3>
<blockquote>
<p>总结文本是否流畅易懂？</p>
</blockquote>
<table>
<thead>
<tr>
<th>检查项</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>语句通顺</td>
<td>没有病句、断句</td>
</tr>
<tr>
<td>逻辑连贯</td>
<td>上下文衔接自然</td>
</tr>
<tr>
<td>专业术语</td>
<td>保留必要术语，通俗化表达</td>
</tr>
</tbody>
</table>
<h2>快速打分表</h2>
<table>
<thead>
<tr>
<th>维度</th>
<th>权重</th>
<th>得分(1-5)</th>
<th>加权分</th>
</tr>
</thead>
<tbody>
<tr>
<td>完整性</td>
<td>30%</td>
<td>_</td>
<td>_</td>
</tr>
<tr>
<td>准确性</td>
<td>40%</td>
<td>_</td>
<td>_</td>
</tr>
<tr>
<td>简洁性</td>
<td>15%</td>
<td>_</td>
<td>_</td>
</tr>
<tr>
<td>可读性</td>
<td>15%</td>
<td>_</td>
<td>_</td>
</tr>
<tr>
<td><strong>总分</strong></td>
<td>100%</td>
<td>-</td>
<td><strong>_</strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p>💡 <strong>评分参考</strong>：4分以上为优秀，3-4分为合格，3分以下需要优化提示词</p>
</blockquote>
<h2>常见问题与优化方向</h2>
<table>
<thead>
<tr>
<th>问题</th>
<th>可能原因</th>
<th>优化建议</th>
</tr>
</thead>
<tbody>
<tr>
<td>遗漏关键点</td>
<td>提示词太笼统</td>
<td>明确要求&quot;保留所有核心观点&quot;</td>
</tr>
<tr>
<td>出现幻觉</td>
<td>模型自由发挥</td>
<td>添加&quot;不要编造原文没有的内容&quot;</td>
</tr>
<tr>
<td>过于冗长</td>
<td>未限制长度</td>
<td>指定字数或压缩比例</td>
</tr>
<tr>
<td>逻辑混乱</td>
<td>缺少结构引导</td>
<td>要求&quot;按时间/主题分点总结&quot;</td>
</tr>
</tbody>
</table>]]></description>
    <pubDate>Thu, 27 Nov 2025 09:59:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/prompt_test-2</guid>
</item></channel>
</rss>