<?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>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>[!warning] 注意<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>Wed, 22 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>
<item>
    <title>提示词机理档案</title>
    <link>https://blog.askerlab.com/prompt_archive-2</link>
    <description><![CDATA[<h1>模型内部是「方向」而不是「句子」</h1>
<p>比如当我们约束“永远不要高度浓缩！”。</p>
<ul>
<li>它不是直接理解「永远不要高度浓缩！」这 9 个汉字。</li>
<li>它理解的是：在训练语料中，「不要高度浓缩」「详细展开讲」「拆开来说说细节」这些表达，经常伴随什么样的输出模式。</li>
<li>所以每一句话，对它来说等价于在行为空间里施加一个「方向」：往“展开、详细、慢速讲”的区域移动。<br />
所以那些能稳定触发你想要的行为方向的短语</li>
<li>详细展开 / 不要省略 → 「不要简略」「详细展开」「逐点解释」</li>
<li>忠实原文 / 不瞎编 → 「不新增事实」「不要编造细节」「如实保留」</li>
<li>去掉元语言 → 「不要解释你的思考过程」「最终只输出文章，不提到指令」</li>
<li>结构清晰 → 「按以下结构…」「先…，然后…，最后…」<br />
这些词对模型来说，就是统计上非常稳定的“开关”。</li>
</ul>]]></description>
    <pubDate>Thu, 27 Nov 2025 09:59:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/prompt_archive-2</guid>
</item>
<item>
    <title>提示词维度测试仪</title>
    <link>https://blog.askerlab.com/prompt_test</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</guid>
</item>
<item>
    <title>提示词机理档案</title>
    <link>https://blog.askerlab.com/prompt_archive</link>
    <description><![CDATA[<h1>模型内部是「方向」而不是「句子」</h1>
<p>比如当我们约束“永远不要高度浓缩！”。</p>
<ul>
<li>它不是直接理解「永远不要高度浓缩！」这 9 个汉字。</li>
<li>它理解的是：在训练语料中，「不要高度浓缩」「详细展开讲」「拆开来说说细节」这些表达，经常伴随什么样的输出模式。</li>
<li>所以每一句话，对它来说等价于在行为空间里施加一个「方向」：往“展开、详细、慢速讲”的区域移动。<br />
所以那些能稳定触发你想要的行为方向的短语</li>
<li>详细展开 / 不要省略 → 「不要简略」「详细展开」「逐点解释」</li>
<li>忠实原文 / 不瞎编 → 「不新增事实」「不要编造细节」「如实保留」</li>
<li>去掉元语言 → 「不要解释你的思考过程」「最终只输出文章，不提到指令」</li>
<li>结构清晰 → 「按以下结构…」「先…，然后…，最后…」<br />
这些词对模型来说，就是统计上非常稳定的“开关”。</li>
</ul>]]></description>
    <pubDate>Thu, 27 Nov 2025 09:59:00 +0800</pubDate>
    <dc:creator>apostle9891</dc:creator>
    <guid>https://blog.askerlab.com/prompt_archive</guid>
</item></channel>
</rss>