<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="/rss.xsl" ?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>夜雨的博客</title><description>YEYUbaka的个人博客，分享计算机人工智能技术学习、开发经验和生活感悟。</description><link>https://blog.yeyubaka.top</link><item><title>最新版 Claude Code 接入 DeepSeek 报错 API Error 400？降级 2.1.153 一步解决</title><link>https://blog.yeyubaka.top/article/20260529</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260529/</guid><description>最新版 Claude Code 接入 DeepSeek 模型时报错 API Error 400，原因是 DeepSeek 的 /anthropic 端点不支持 messages 数组中的 system 角色。降级到 2.1.153 版本即可解决，本文记录完整排查和修复过程。</description><pubDate>Fri, 29 May 2026 04:00:00 GMT</pubDate><content:encoded>::vhMusic{id=&quot;3361870496&quot;}

:::note{type=&quot;warning&quot;}
**如果你正在用最新版 Claude Code 对接 DeepSeek 的 Anthropic 兼容接口**，突然发现报 `API Error: 400` 死活连不上——不用折腾 API key 和网络，大概率是 Claude Code 新版本改了请求格式，跟 DeepSeek 不兼容了。

这篇文章帮你搞清楚报错的真正原因，并用最简单的降级操作恢复正常。
:::

## 问题现象

最近把 Claude Code 更新到最新版后，配置 DeepSeek 模型时报错，连最基本的对话都发不出去。完整报错信息如下：

```
API Error: 400 Failed to deserialize the JSON body into the target type:
messages[1].role: unknown variant `system`, expected `user` or `assistant`
at line 1 column 18790
```

核心信息就一句：**DeepSeek 不认识 `system` 这个 role，它只认 `user` 和 `assistant`**。

## 原因分析

### 问题出在 Anthropic API 兼容层的差异

Claude Code 发起 API 请求时，会把 system prompt（系统提示词）夹在 `messages` 数组里，给每条 message 标记 `role: &quot;system&quot;`。这在 Anthropic 官方 API 里是完全合法的——Anthropic 同时支持两种方式传递 system prompt：

- **顶层参数**：`system` 作为请求体的顶层字段（传统方式）
- **messages 内嵌**：`role: &quot;system&quot;` 放在 `messages` 数组里（新版方式）

但 DeepSeek 的 `/anthropic` 兼容端点是按传统方式实现的——**它只支持顶层 `system` 参数，不支持 `messages` 数组里出现 `system` 角色**。当 Claude Code 新版把 system prompt 塞进 messages 数组时，DeepSeek 服务端反序列化 JSON 直接失败：

```
unknown variant `system`, expected `user` or `assistant`
```

说白了就是：DeepSeek 的 messages 数组预期里只有 `user` 和 `assistant` 两种角色，冒出一个 `system`，它不知道该拿它怎么办。

### 哪个版本开始出问题的？

Claude Code 在某个版本之后调整了 system prompt 的发送方式，从顶层参数改成了 messages 内嵌。具体从哪个小版本开始的我没逐个测，但可以确定：

- **2.1.153**：还在用传统的顶层 `system` 参数 → 兼容 DeepSeek
- **2.1.154+**（含当前最新版）：改用 messages 内嵌 `role: &quot;system&quot;` → 不兼容 DeepSeek

所以解决方案很直接——**降级到 2.1.153**，这是最后一个兼容 DeepSeek 的版本。

## 解决方案

### 方案一：CLI 降级（PowerShell）

如果你用的是命令行版 Claude Code，一条命令搞定：

```powershell
npm install -g @anthropic-ai/claude-code@2.1.153 --force
```

`--force` 是为了覆盖已安装的最新版，避免 npm 因为版本&quot;降级&quot;而拒绝安装。

安装完成后验证版本：

```bash
claude -v
```

输出 `2.1.153` 就说明降级成功了。

![PowerShell 降级流程](/images/posts/claude-code-deepseek-400-fix/powershell-downgrade.png)

*▲ PowerShell 中执行 npm install 降级 Claude Code 到 2.1.153 的完整流程*

### 方案二：VS Code 插件降级

如果你用的是 VS Code 里的 Claude Code 插件，操作也很简单：

**第一步：关掉自动更新**

打开 VS Code 扩展面板（`Ctrl+Shift+X`），搜索 &quot;Claude Code for VS Code&quot;，点击插件齿轮 ⚙️ → **关闭自动更新**。否则你一降级它又自动升回去了。

**第二步：安装指定版本**

同样在插件齿轮菜单里，选择 **&quot;安装另一个版本...&quot;（Install Another Version...）**，在弹出的版本列表里选 **2.1.153**。

![VS Code 插件降级操作](/images/posts/claude-code-deepseek-400-fix/vscode-plugin-downgrade.png)

*▲ 在 VS Code 插件商店中关闭自动更新，并通过&quot;安装另一个版本&quot;选择 2.1.153*

等安装完成，重新加载 VS Code 窗口，API Error 400 就不会再出现了。

## 关于版本 2.1.153

为什么选 2.1.153 而不是别的版本？这里有三个考量：

1. **API 格式**：2.1.153 是最后一个用传统顶层 `system` 参数发请求的版本。从 2.1.154 开始，system prompt 的发送方式变了，messages 数组里出现了 `role: &quot;system&quot;`，直接触发 DeepSeek 的 400 错误。

2. **功能完整**：2.1.153 功能上跟当前最新版差别不大，sub-agent、skills、MCP、hook 等核心能力都有，日常使用完全够。

3. **稳定性**：之前那波 Windows 兼容性问题（claude.exe 变 1KB）我在 2.1.112 上踩过坑，2.1.153 是在那之后的版本，Windows 下运行稳定，不需要额外折腾。

总结成一个简单的判断：

:::note{type=&quot;info&quot;}
- 接入 **Anthropic 官方 API** → 随便升最新版，不受影响
- 接入 **DeepSeek / 第三方 Anthropic 兼容接口** → 锁定 2.1.153，别手贱更新
:::

## 总结

这个问题说到底是个**API 兼容性错位**：

- Anthropic 在自己生态里改了 system prompt 的传递方式（顶层 → messages 内嵌）
- DeepSeek 的兼容层只实现了传统的顶层 `system` 参数
- Claude Code 新版跟着 Anthropic 走，第三方适配没跟上

社区里其实有不少人遇到了同样的问题，CSDN 和博客园上都有讨论。解决方式都一样：**降级到 2.1.153，关闭自动更新**。

两分钟的操作，值得一记。

---

**参考资料**：

- [ClaudeCode接入DeepSeek报错400 - CSDN](https://blog.csdn.net/bc202205/article/details/161505390)
- [Claudecode降级2.1.153解决API Error: 400 - 博客园](https://www.cnblogs.com/xzaxs/p/20212017#!comments)</content:encoded></item><item><title>Claude Code 2.1.36+ 的 billing header 在破坏你的 prompt cache？一行命令修复</title><link>https://blog.yeyubaka.top/article/20260525</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260525/</guid><description>升级 Claude Code 2.1.36 后，第三方 API 代理的 prompt caching 突然失效？原因是新版在 system prompt 中插入了每次变化的 x-anthropic-billing-header。本文拆解根因、源码追踪、给出修复方案，并整理了各大代理的适配现状。</description><pubDate>Mon, 25 May 2026 04:00:00 GMT</pubDate><content:encoded>:::note{type=&quot;warning&quot;}
**如果你正在用第三方 API 代理玩 Claude Code**，并且最近发现 token 消耗暴涨、推理变慢、缓存命中率突然归零——那么你大概率被这个东西坑了。

这篇文章会帮你搞清楚：发生了什么、为什么、怎么修。文末还整理了 DeepSeek、智谱、vLLM 等主流代理的适配现状，帮你判断自己是否需要动手。
:::

## 一、问题现象

事情要从 Claude Code 的 **2.1.36 版本**说起。

2026 年 5 月，B站 UP 主**张司机在路上**发了一个视频（BV1m2LG6WEdH，当前播放量 7.6 万），标题直截了当——「如何修复 Claude Code 给第三方大模型用户挖的坑」。

视频里他用 `cc-tap`（一个 Claude Code 抓包工具）截取了一组对比数据：

同一个 session 里，给 Claude Code 连续发三轮简单的消息（Hello → Fine → Thank you）。正常情况下，第二个和第三个请求应该命中 prompt cache，大幅度减少 token 消耗。

但实际抓包结果是——**三轮对话的 system prompt 第一块，每次都不一样：**

```
第一轮: x-anthropic-billing-header: cc_version=...; cc_entrypoint=...; cch=97bd6
第二轮: x-anthropic-billing-header: cc_version=...; cc_entrypoint=...; cch=24c2d
第三轮: x-anthropic-billing-header: cc_version=...; cc_entrypoint=...; cch=ead88
```

注意看 `cch` 这个字段——5 位 hex 字符，每次都在变。

而 prompt cache 的原理是前缀哈希：从 system prompt 开头算起，任何一字节变了，整个缓存 key 就对不上了。所以第一轮刚建好的缓存，第二轮的 `cch` 一换，cache 直接 miss。第三轮再换，再 miss。

**结果就是：用第三方代理的用户，每轮对话都等于第一次对话——全量 token 消耗，没有任何缓存命中。**

---

## 二、问题出在哪？

我们先复习一下 Prompt Cache 的工作方式（张司机的前几期视频有详细讲解）：

1. 服务端按 **Tools → System → Messages** 的顺序拼接前缀
2. 请求里每个带 `cache_control` 标记的 block 都定义了一个 **breakpoint**（缓存检查点）
3. 每个 breakpoint 的缓存 key = 从开头到该 block 末尾所有内容的哈希值

Claude Code 的请求通常有三个 breakpoint：
- 第一个：System 第二个 block 末尾
- 第二个：System 第三个 block 末尾
- 第三个：User message 上

**而关键问题是：`x-anthropic-billing-header` 被塞在了 System 的第一个 block 里——排在所有 breakpoint 的最前面。**

所以这条 header 一变，后面所有 breakpoint 的哈希全部跟着变。三个缓存检查点全部 miss。

:::note{type=&quot;info&quot;}
**为什么 Anthropic 官方服务不受影响？**

因为 Anthropic 自己的服务端知道这段内容是自己塞进去的，算缓存 key 的时候会**跳过这个 block**。但第三方大模型转发服务不知道这个细节，老老实实把整段 system prompt 拿来算哈希——所以每次都算出一个新 key，缓存永远对不上。
:::

这个问题其实从 2026 年 2 月就开始被社区报告了。GitHub Issues 里有大量相关讨论，但 Anthropic 一直没有正面回应。原因也很简单：**他们自己的服务不受影响。**

---

## 三、源码追踪：cch 到底怎么生成的？

张司机在视频里直接扒了 Claude Code 的源码，追踪了整个链路。

### JS 层：`getAttributionHeaders`

在 `src/constant/system.ts` 中可以找到这个函数——它负责构造 billing header 字符串：

```typescript
// 伪代码还原
function getAttributionHeaders() {
  if (!isAttributionHeadersEnabled()) return &quot;&quot;;

  let cch = &quot;00000&quot;;  // 静态占位符
  if (featureFlag) {
    // cch 在 JS 层始终是 &quot;00000&quot;
  }

  return `x-anthropic-billing-header: cc_version=${version}; cc_entrypoint=${entrypoint}; cch=${cch}`;
}
```

注意看——`cch` 在 JS 层是一个**静态占位符 `00000`**。真正的值在哪计算的呢？

### Zig 原生层：`Attestation.zig`

源码中的注释大方地承认了真相：

&gt; &quot;真正的 cch 是由 NativeHttpGen 在请求发出的最后一刻计算的，会覆盖占位符。&quot;

关键点：Claude Code 是用 **Bun**（一个用 Zig 写的 JS 运行时）编译打包成独立二进制的。Anthropic 不仅用了 Bun，还 fork 了一份自己的版本 `bun-anthropic`，在其中加入了一个文件叫 `Attestation.zig`。

它做的事情非常精巧：

1. JS 层生成 header，其中 `cch=00000`（5 个零）
2. 因为 `00000` 和真实值长度相同（都是 5 位 hex），Content-Length 不变
3. Zig 原生层在请求发出的最后瞬间，把 `00000` 直接原地替换成真实的 cch 值
4. Buffer 不需要重新分配，整个过程对 JS 层完全透明

**所以从外面看，cch 就像凭空变出来的一样——每次都不一样，你在 JS 里抓不到它的生成逻辑。**

### 那 cch 到底是干什么的？

一句话：**防白嫖。**

Claude Code 的登录走的是 OAuth，拿到 Token 后绑定你的 Pro 或 Max 订阅。订阅价格比直接 API 按量付费便宜很多。于是有人想：「我把 Token 从二进制里抠出来，自己写代码调用 API，不就能用订阅价跑任意请求了？」

那服务器怎么识别一个请求是真的来自 Claude Code 客户端、还是某个山寨程序伪造的？

答案就是 cch——它由 Zig 原生层计算，是一段只存在于 Bun 运行时内部的客户端签名。山寨程序根本拿不到真值，一发请求就露馅了。

不过嘛……这套防御机制也不是铁板一块。GitHub 上已经有了开源项目（比如 Sub2API）把整套 cch 算法完整复刻了。藏在 Zig 层的逻辑，在开源社区面前早就透明了。

---

## 四、怎么修复？

回到代码里，追一下 `isAttributionHeadersEnabled` 这个函数：

```typescript
function isAttributionHeadersEnabled() {
  // 第一行就检查环境变量
  if (!process.env.CLAUDE_CODE_ATTRIBUTION_HEADER) return false;
  // ...
}
```

所以解决方案就是一句话：

**设置环境变量 `CLAUDE_CODE_ATTRIBUTION_HEADER=0`，让整个 billing header 直接返回空串。**

### 具体操作

编辑 `~/.claude/settings.json`，在 `env` 段中加一行：

```json
{
  &quot;env&quot;: {
    &quot;CLAUDE_CODE_ATTRIBUTION_HEADER&quot;: &quot;0&quot;
  }
}
```

如果你用的是命令行启动，也可以在终端里直接设：

```bash
# Linux / macOS
export CLAUDE_CODE_ATTRIBUTION_HEADER=0

# Windows PowerShell
$env:CLAUDE_CODE_ATTRIBUTION_HEADER=0
```

设完之后**重启 Claude Code** 即刻生效。

### 怎么验证？

用 `cc-tap` 或任何抓包工具看请求内容。设置前，system prompt 的第一个 block 是：

```
block 1: x-anthropic-billing-header: cc_version=2.1.143.f09; cc_entrypoint=cli; cch=0f646;
block 2: You are Claude Code, Anthropic&apos;s official CLI for Claude.
block 3: ...
```

设置后，billing header 整个消失：

```
block 1: You are Claude Code, Anthropic&apos;s official CLI for Claude.
block 2: ...
```

System block 数量从 3 个变成 2 个。之后 `cch` 不再变化，prompt cache 恢复正常。

---

## 五、各大代理适配现状

不是所有第三方代理都需要你自己动手。以下是基于社区实测整理的各家适配情况：

### ✅ 无需手动修复

| 平台 | 状态 | 来源 |
|------|------|------|
| **DeepSeek API** | 已自行处理，缓存命中率 ~99% | 社区实测：加不加环境变量，缓存命中率几乎没区别 |
| **智谱 GLM-5.1** | 服务端已过滤 billing header | 社区实测：加不加环境变量，服务端返回的缓存读取数量相同 |
| **vLLM (v&gt;0.17.1)** | PR #36829 已修复（2026-03-12 合并） | 在 `serving.py` 中检测 billing header block 并直接 `continue` 跳过 |

vLLM 的修复方式非常直接——在收到 Anthropic 格式请求时，检查 system prompt 中的每个 block：

```python
# vllm/entrypoints/anthropic/serving.py
for block in anthropic_request.system:
    if block.type == &quot;text&quot; and block.text:
        if block.text.startswith(&quot;x-anthropic-billing-header&quot;):
            continue  # 直接跳过整个 billing header block
        system_prompt += block.text
```

这样真正的 system prompt 内容（从 `&quot;You are Claude Code...&quot;` 开始）完全不受影响，billing header 被原地丢弃。

### ⚠️ 建议手动设置

如果你用的是**自建代理、小众转发服务、LiteLLM、Ollama 本地部署**，或者不确定你的代理有没有做处理——**保险起见，直接加环境变量。**

Anthropic 官方文档也说得很清楚：

&gt; &quot;Claude Code 还会在系统提示前面添加一个简短的归属块，其中包含客户端版本和从对话派生的指纹。Anthropic API 在处理前会删除此块，因此不会影响第一方提示缓存。**如果您的网关实现了自己的提示缓存（以完整请求体为键），请设置 CLAUDE_CODE_ATTRIBUTION_HEADER=0 以省略它。**&quot;

来源：[Claude Code 官方文档 — LLM Gateway](https://code.claude.com/docs/zh-CN/llm-gateway#llm-gateway)

---

## 六、社区评论精选

张司机视频下的评论区也有不少有价值的信息，摘几条：

&gt; **评论1：** &quot;我用的是 DeepSeek 的 Anthropic-compatible API，链路是 `Claude Code → 本地抓包转发代理 → DeepSeek`。未设置环境变量时，抓包确认 `cch` 确实在变。设置 `CLAUDE_CODE_ATTRIBUTION_HEADER=0` 后 billing header 消失。我也问了 DeepSeek 模型能否看到这个 header，它回答没看到。所以 DeepSeek 可能已经在适配层过滤了。&quot;

&gt; **评论2：** &quot;测试了智谱的 Coding Plan GLM-5.1，加不加环境变量，服务端返回的缓存读取数量一样，说明智谱也对这个头做过处理了。&quot;

&gt; **评论3：** &quot;早就修复了，现在才发视频。vLLM 在 PR #36829（3 月 12 日合并，v&gt;0.17.1）里修了——检测到 billing header block 直接 skip。&quot;

&gt; **评论4：** &quot;这个 cch 是 CC 客户端的签名，用于保护他的订阅服务，是上次源码泄露后被发现的。&quot;

&gt; **评论5：** &quot;题外话：如果你好奇 cch 的完整生成算法，GitHub 上已经有开源项目复刻了整个逻辑。Anthropic 藏在 Zig 层的东西，社区早就扒干净了。&quot;

---

## 七、总结

整个故事的脉络其实很清晰：

1. **Claude Code 2.1.36+** 在每个 API 请求的 system prompt 最前面插入了一行 `x-anthropic-billing-header`
2. 其中的 `cch` 字段是 5 位 hex，每次请求变化，由 Zig 原生层在请求发出前瞬间生成
3. 它的原始目的：防止第三方用订阅 Token 白嫖 API（客户端签名验证）
4. **副作用**：第三方代理在计算 prompt cache 哈希时包含了这段变化的内容 → 缓存 key 每次不同 → 缓存全部 miss
5. Anthropic 官方服务不受影响（它们会跳过这个 block 算缓存），所以一直没有积极处理
6. **修复**：加一行环境变量 `CLAUDE_CODE_ATTRIBUTION_HEADER=0`，重启 Claude Code

加一个环境变量，重启，你的 prompt cache 就回来了。

如果你在用第三方 API 代理，不妨顺手设上。哪怕你的代理声称已经适配了，多一层保险总是好的。

---

## 参考资源

- [B站视频：如何修复Claude Code给第三方大模型用户挖的坑 — 张司机在路上](https://www.bilibili.com/video/BV1m2LG6WEdH/)
- [Claude Code 官方文档 — LLM Gateway](https://code.claude.com/docs/zh-CN/llm-gateway#llm-gateway)
- [Claude Code 官方文档 — 环境变量参考](https://code.claude.com/docs/zh-CN/settings)
- [vLLM PR #36829: fix Anthropic billing header breaking prompt cache](https://github.com/vllm-project/vllm/pull/36829)
- [Claude Code cch 生成机制相关分析](https://aiorang.com/article/P1mOhMs.html)</content:encoded></item><item><title>CLAUDE.md 维护指南：从「写了等于没写」到「真正让 Claude 听话」</title><link>https://blog.yeyubaka.top/article/20260524</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260524/</guid><description>CLAUDE.md 越写越长，Claude 反而越来越不听话？本文基于小林coding 的深度解析，加入我半年来的实战经验，讲清楚 CLAUDE.md 的正确写法、分层策略和维护方法。</description><pubDate>Sun, 24 May 2026 04:00:00 GMT</pubDate><content:encoded>:::note{type=&quot;import&quot;}
本文核心内容整理自**小林coding** 公众号文章《面试官皱眉：&quot;Claude Code 你用了半年，CLAUDE.md 平时怎么维护？&quot;》，并结合我（YEYUbaka）半年来的 CLAUDE.md 实战踩坑经验做了补充和批注。文中标注 **「YEYUbaka 注」** 的段落为个人补充内容。
:::

## 前言

前阵子，有个朋友在群里发牢骚。

他说给 Claude Code 写了一份 1000 多行的 CLAUDE.md，把团队的代码规范、目录结构、命名约定、测试要求一股脑全塞进去了，自我感觉特别细致。

结果呢？Claude 该忘的还是忘，该违规的还是违规。

是啊，CLAUDE.md 这玩意，很多人用 Claude Code 这么久了，也是想到啥写啥，越写越长。

还真没认真想过：写得多，到底是好事还是坏事？

我把 Anthropic 官方文档整个翻了一遍，这一翻不要紧，我发现自己之前的 CLAUDE.md 一半内容压根是负资产。

今天就把这套经验整个分享出来，按这个顺序展开：

- CLAUDE.md 到底是个什么东西？
- 为什么写多了反而废？
- 什么样的规则才真正生效？
- 怎么把 CLAUDE.md 分层组织？
- 怎么用 `/init` 和 `/memory` 维护？
- CLAUDE.md 到底该怎么写？

读完这篇，你应该能立刻去改自己的 CLAUDE.md，从「写了等于没写」变成「真正能让 Claude 听话」。

---

## 01｜CLAUDE.md 到底是个什么东西？

很多人一上来就开始问「CLAUDE.md 应该写些啥」、「行数多少合适」、「能不能 import 别的文件」。

但我觉得这些问题之前，得先回答一个更基本的问题：CLAUDE.md 到底是干啥的？为什么 Claude Code 要专门搞这么个文件？

你想象一个场景。

你刚入职一家公司，主管丢给你一份文档，标题叫「团队约定」。里面写了：我们用 yarn 不用 npm，API 在 `src/api/` 下，生产数据库千万别动，提 PR 之前要跑 `yarn lint`。

这份文档看着不起眼，但效果惊人。新人不用一遍遍问「咱们这边怎么做 X」，老人也不用一遍遍重复回答。一份文档，省下整个团队的沟通成本。

CLAUDE.md 就是给 Claude 的这份「团队约定」。

说穿了，它本质上就是一个普通的 markdown 文件，文件名固定叫 `CLAUDE.md`，放在你项目的根目录下。

你能用记事本打开，能用 VSCode 编辑，跟你平时写的 README 一样，里面就是写写规则、放放说明，谁都能上手。

但它特别的地方在于：每次你打开 Claude Code 跟它聊天，Claude 都会自动把这个文件读一遍，作为整个对话的「ground truth」（基准事实，可以理解为「默认成立的前提」）。你后面提的需求、它做的判断，全都是在这份「团队约定」的基础上推进的。

换句话说：在你输入第一句提问之前，在写任何代码之前，在任何事情发生之前，Claude 都会先读这个文件，并把它当作整段会话的默认前提。它不是「可选的提示」，而是「默认的前提」。

说概念可能有点虚，咱们来做个最简单的实验你就懂了。

你在项目根目录新建一个 CLAUDE.md，里面就写一行规则：

```
解释任何技术概念时，必须以「打个比方」开头。
```

保存。然后打开 Claude Code 问它一个完全无关的问题：

&gt; 你：什么是 agent？

&gt; Claude：打个比方，agent 就像你雇的一个能自己干活的助理。你跟它说「帮我把上周的数据整理成报表」，它会自己拆任务、找文件、读数据、生成表格，中间用什么工具、分几步走全都它自己决定，最后把成品交给你，不用你一步步指挥……

你看，你压根没在提问里提这条规则，Claude 自己就把它套上了。

更妙的是，下次再开新 session 问别的，比如「什么是 WebSocket」，它还是会以「打个比方」开头。规则一旦写进 CLAUDE.md，相当于给 Claude 装了个稳定的「长期记忆」，每个新对话都默认带着。

讲到这儿，你可能会冒出一个新疑问：那为啥要专门搞一个 CLAUDE.md？

我直接把规则写在 README 里不行吗？反正它俩都是 markdown 文件。

不行。两个文件长得像，但定位完全不一样。

Anthropic 官方文档里有句话点醒了我：「README 是写给人看的，CLAUDE.md 是写给 agent 看的，两个读者群体不一样，密度也不一样。」

啥意思？README 是给开发者翻的，写项目介绍、快速上手、贡献指南，长一点没事，散一点没事，反正人会跳读。

而且 Claude 默认不会主动去读 README，你不告诉它去看，它就当不存在。

CLAUDE.md 才是那个被自动加载、每次都吃 token 的「默认配置」。

（顺带说一句，token 简单理解就是模型读写时的「字符单元」，大致 1 个中文字 ≈ 1 个 token；每次请求消耗的 token 既算钱也占用上下文窗口，所以「省 token」全文会反复出现。）

那它具体是怎么被加载的？这里顺便贴一小段 Claude Code 源码让你看看背后的机制：

```typescript
const dirs: string[] = []
const originalCwd = getOriginalCwd()
let currentDir = originalCwd

while (currentDir !== parse(currentDir).root) {
  dirs.push(currentDir)
  currentDir = dirname(currentDir)
}
```

这段代码出自 `src/utils/claudemd.ts`。逻辑很朴素：从你当前所在的目录一路往上爬到文件系统根目录，每爬一层就把目录名记下来。爬完之后再反向遍历，从根目录往下读每一层的 CLAUDE.md 和 `.claude/CLAUDE.md`，全部合并喂给模型。

所以一个项目可能同时有好几份 CLAUDE.md 在生效，这一点我们第四节会展开。

:::note{type=&quot;success&quot;}
**YEYUbaka 注：** 我最早也不知道 CLAUDE.md 会被每次会话完整加载。有一次我在项目根 CLAUDE.md 里写了一大段约 2000 字的项目背景介绍，从业务逻辑写到部署架构，觉得越详细越好。结果呢？每次跟 Claude 聊正事，前几轮它都在「回忆」那些背景段落，真正干活的有效对话空间被严重挤压。后来我把那 2000 字删到只剩一句「项目架构详见 `docs/architecture.md`，需要时请 read」，对话质量立刻提升了。**记住：CLAUDE.md 是指路牌，不是百科全书。**
:::

到这里你应该能感觉到 CLAUDE.md 的特殊之处了：它不是文档，是配置。是你给 Claude 配的「这个项目的预设」。

理解了这一层，接下来就要扒一个让所有人意外的事实：写得越多，效果反而越差。

---

## 02｜写多了反而废？

我刚开始用 Claude Code 的时候，是这么想的：CLAUDE.md 嘛，多写点总没坏处，规则越细，Claude 越知道我要啥。

后来看到一组数据，我直接把自己 400 多行的 CLAUDE.md 删了一半。

这个数据来自一个叫 SFEIR Institute 的技术博客。他们做了一组实测：把所有规则塞在一个 CLAUDE 里，**控制在 200 行以内的时候，规则遵守率大概 92%**。但写到 400 行往上，遵守率就肉眼可见地往下掉。

更有意思的是，如果你把 200 行拆成 5 个 30 行的模块化文件，丢到 `.claude/rules/` 目录里，**遵守率反而能涨到 96%**。

写得多反而不听，写少了拆开反而听了。这跟我朴素的直觉完全是反的。

为啥会这样？两个原因。

**第一个原因，token 经济。** CLAUDE.md 每次启动都会被完整加载进上下文窗口。你写 400 行，每次请求就消耗几千 token，挤压你的对话、Claude 的思考、工具调用结果的位置。

打个比方，会议桌上摆 50 张便签，重点一目了然。换成 400 张，整张桌子都被淹没，谁也找不着重点。

**第二个原因，注意力稀释。** 模型的注意力不是无限的，规则一多，每条规则在模型脑子里的权重就被摊薄了。社区里不少重度用户都聊过这个体感：CLAUDE.md 超过 300 行之后，「记不住」就变成常态。

讲到这儿你可能想，那只要控制在 200 行就行了？也不全是。光控制行数还不够，得知道哪些东西根本就不该写进 CLAUDE.md。

肯定有不少人的 CLAUDE.md 里塞着大量负资产。最典型的三类反例：

**第一类，复述型。** 把整个项目架构文档复制粘贴进 CLAUDE.md，一写写 100 行。问题是项目架构会变，今天 React，半年后可能就 Vue 了，CLAUDE.md 里的 100 行还停留在 React 时代。正确做法是一行话指过去：「项目架构详见 docs/architecture.md」，Claude 真要看自己会去 read。

**第二类，愿望型。** 「我们希望测试覆盖率达到 90%」、「我们的目标是 0 bug」。这种话听着政治正确，但 Claude 没法判断「希望」和「实际」的差距，可能为了「满足愿望」给你乱补一堆没意义的测试。CLAUDE.md 里只写当下实际执行的规则，「PR 提交前必须跑 npm test」是规则，「我们希望大家多写测试」是 PUA。

**第三类，术语表型。** 把团队术语表往 CLAUDE.md 里搬。「Repo 指 repository、PR 指 pull request……」Claude 是个 LLM，这些通用术语它都懂。你真正需要解释的是团队特有的黑话（比如「我们说『小灰』指的是预发布环境」），但也建议放 `docs/glossary.md` 里。

把这三类垃圾清掉，你的 CLAUDE.md 可能直接从 400 行瘦到 80 行。Claude 的表现，下一次开 session 就能感觉到。

:::note{type=&quot;warning&quot;}
**YEYUbaka 注：** 我曾经在 CLAUDE.md 里写过一句「代码要优雅整洁」。结果 Claude 开始给每个简单的 CRUD 函数都加上装饰器模式包装，一个本来 30 行能搞定的 Service 被扩展成三层抽象——Interface → BaseImplementation → ConcreteClass，同事们 code review 的时候直接看傻了。**模糊的愿望型规则 = 给 Claude 发了一张随意发挥的许可证。** 后来我把这条改成「新增 Service 类保持在 80 行以内，超过请先拆方法」，问题立刻消失了。一句话的差别，效果天差地别。
:::

---

## 03｜什么样的规则才真正「有效」？

清完垃圾，问题就变成：剩下那些该写的规则，到底怎么写才有用？

我先抛个问题给你猜：同样讲缩进，下面哪种写法 Claude 听得更好？

- A：「所有 TypeScript 文件用 2 个空格缩进」
- B：「代码要按规范格式化」

如果你猜 A，恭喜，答对了。但你能说清楚为什么吗？

关键差异在一个词：**可验证**。

A 是具体的，Claude 写完代码自己就能数：是不是 2 个空格？是不是 TypeScript 文件？这两个问题都有明确答案，它能自检。

B 是模糊的，什么叫「按规范格式化」？这个判断需要外部标准，Claude 只能猜你的偏好，猜得对就对，猜得错就错。

关于「啥样的规则才有效」，可以浓缩成一句话四个原则：

**短、具体、告诉为什么、持续更新。**

这四点挨个聊聊。

**短**，上一节聊过了，呼应 200 行的黄金线。

**具体**，就是上面 A 和 B 的差异。再举几个例子你感受一下：

| ❌ 模糊规则 | ✅ 具体规则 |
|:-----------|:----------|
| 代码要规范格式化 | 所有 TypeScript 文件用 2 个空格缩进 |
| 写清晰的错误处理 | API 错误统一用 `throw new AppError(code, msg)` |
| 注意性能 | 列表超过 50 条必须加分页，用 `usePagination` hook |
| 做好测试 | 每个 service 方法至少一个 happy-path 测试 + 一个 error-case 测试 |

「具体」其实就是把抽象意图翻译成可执行命令、可定位路径、可验证规则。Claude 不是你团队里磨合三年的老同事，它没法靠默契理解你的意思。

**告诉为什么**，这条乍一看像废话。规则就是规则，Claude 照做就行，还要告诉它为啥？

要的。而且这是四条里最关键的一条。

比如你写「不要在测试里写入生产数据库」，Claude 知道不能写生产库就完了。

但你加一句「因为去年有次测试不小心把 users 表清空了，出过事故」，Claude 不光知道这条规则，还知道规则的边界。

啥意思？以后你跑预发布环境（staging）测试，问它能不能写预发布数据库，它会基于「规则的本质是防生产事故」做出正确判断，而不是机械地说「规则说了不能写数据库」。

告诉「为什么」不是废话，是给 Claude 留判断空间。

**持续更新**，就是把 CLAUDE.md 当活文档维护。

Claude 在哪儿犯错了两次以上，你就加一条防御规则。但同样重要的是：老规则要删。

claudeguide.io 上有句话特别戳：「错误的规则比没有规则更糟。」

想想也是，规则在那儿摆着，Claude 就会试图遵守，但规则本身已经过时了，结果就是你在花 token 买一份混乱。

:::note{type=&quot;info&quot;}
**YEYUbaka 注——我踩过的坑 vs 改进后的规则：**

| 踩过的坑 | 改进后的规则 |
|:---------|:-----------|
| 部署流程写了 30 行详细步骤，但每次升级 blog-astro-test 的依赖后步骤就变了，Claude 还按旧规则走 | 「部署用 `.\deploy-blog.bat`，具体步骤脚本内已封装，不要手工 scp」——一行搞定，永不脱节 |
| 「用 Less 写样式」→ Claude 给每个组件都新建 `.less` 文件，但实际上项目里组件样式写在 `.astro` 的 `` 标签里 | 「组件样式写在 `.astro` 文件的 `` 中，不要新建独立 `.less` 文件」 |
| 「所有路径包含 /blog/ 前缀」→ Claude 把代码里的内部引用也加上了 /blog/ | 「静态资源路径（图片、字体、favicon）加 /blog/ 前缀；import/组件引用不加」 |
:::

讲到这儿，想多说一句：CLAUDE.md 其实没有标准模板，每个项目都该有自己的样子。

Claude Code 当初的设计理念就是「让用户随便用、随便改、随便魔改」，根本没有所谓「正确」的用法。

所以别迷信任何人的「最佳实践」，包括我这篇。把原则吃透，按你项目的实际情况裁剪。

---

## 04｜CLAUDE.md 不只是一个文件

讲到这里，你可能默认 CLAUDE.md 就是项目根目录下那一个文件。

但实际上，CLAUDE.md 是分层的。

一个项目可能有好几份 CLAUDE.md 同时在生效。

回想一下第一节那段源码做的事：从你的工作目录一路往上爬，每一层都尝试读 CLAUDE.md 和 `.claude/CLAUDE.md`，全部合并喂给模型。

所以一份完整的 CLAUDE.md 生态长这样：

- **项目根的 CLAUDE.md**：写整个项目的通用约定（技术栈、目录、命令、硬约束），每次启动都加载，是大头。
- **子目录的 CLAUDE.md**：比如前端 `frontend/CLAUDE.md` 写组件约定。这层按需加载，Claude 工作到该目录才生效，不污染整个项目上下文。
- **`~/.claude/CLAUDE.md` 全局**：跨项目的个人偏好（比如「永远用中文回复」、「我喜欢 4 空格缩进」），相当于给所有 Claude 打了同一份补丁。

文字可能还有点抽象，咱们拿一个典型的前后端分离项目举例，目录结构大概长这样：

```
~/.claude/
└── CLAUDE.md          # 全局：用中文回复我、commit message 写中文

my-project/
├── CLAUDE.md          # 项目根：技术栈、目录结构、命令、硬约束
├── frontend/
│   ├── CLAUDE.md      # 前端模块：组件用函数式、状态管理用 Zustand
│   └── src/
└── backend/
    ├── CLAUDE.md      # 后端模块：API 用 RESTful 风格、错误统一抛 AppError
    └── src/
```

启动 Claude Code 的时候，根目录的 CLAUDE.md 和 `~/.claude/CLAUDE.md` 会自动合并加载。等你让 Claude 改 `frontend/` 里的代码，它才会顺手把 `frontend/CLAUDE.md` 也读进来。改后端代码时，前端那份规则压根不会进上下文，节省 token。

理解了这三层，你会发现玩法一下打开了。项目通用规则放项目根、模块特有规则放子目录、个人偏好放全局，各管各的，互不污染。

但还有一层更进阶的玩法：`.claude/rules/` 目录。

这是 Claude Code 提供的「模块化 CLAUDE.md」机制。你不在 CLAUDE.md 里堆所有规则，而是在 `.claude/rules/` 目录下每个主题一个文件。

举个例子，你的 `.claude/rules/` 目录可能长这样：

```
.claude/
└── rules/
    ├── testing.md       # 测试规则
    ├── api-design.md    # 接口设计规则
    ├── security.md      # 安全规则
    └── ui-components.md # UI 组件约定
```

每个文件聚焦一个主题，控制在 30 行以内，结构清爽好维护。

最妙的是，每个 rules 文件可以加一段 YAML frontmatter（写在文件最顶部、用 `---` 包起来的一段元信息），标注「这规则只在改某类文件的时候加载」。比如 `testing.md` 长这样：

```yaml
---
paths: [&quot;**/*.test.ts&quot;, &quot;**/*.spec.ts&quot;]
---
# 测试规则
- 用 describe / it，不用 test()
- mock 外部依赖必须用 vi.mock
- 每个测试只写一个断言
- 别用 expect.anything()，断言要精确
```

frontmatter 里的 `paths` 告诉 Claude：「这条规则只在改测试文件时才加载」，业务代码改起来你压根看不到这份规则。

同理，`api-design.md` 顶部可以写 `paths: [&quot;src/api/**/*.ts&quot;]`，Claude 只在改接口代码时才加载：

```yaml
---
paths: [&quot;src/api/**/*.ts&quot;]
---
# 接口设计规则
- 所有接口走 RESTful 命名（GET / POST / PUT / DELETE）
- 返回值统一用 { data, error } 格式
- 错误码用 4 位数字（如 1001、1002），别用字符串
```

这就叫 **path-scoped rules**（路径作用域规则）。Claude 只在工作到匹配路径的文件时才把这份规则加载进上下文。改业务代码的时候根本看不到测试规则，改接口的时候也不会看到 UI 组件约定，省下来的 token 全留给真正有用的对话。

打个比方，公司有总公司手册、各部门有部门手册、每个岗位有岗位手册。你不会让每个新人都把所有手册随身带着，对应业务的时候才翻对应的手册。

这种模块化拆分的好处就是上一节那个 96% 数据：少加载、按需加载，效果反而比一坨更好。

:::note{type=&quot;success&quot;}
**YEYUbaka 注——我的 blog-astro-test 项目的真实 .claude/rules/ 结构：**

```
.claude/
├── settings.json
├── CLAUDE.md               # &quot;永远用中文回复&quot;
└── rules/
    ├── deployment.md        # paths: [&quot;deploy-blog.*&quot;]
    │                        # 部署只走 deploy-blog.bat，不要手工 scp
    ├── blog-paths.md        # paths: [&quot;src/**/*.astro&quot;, &quot;public/**/*&quot;]
    │                        # 静态资源路径必须含 /blog/ 前缀
    └── frontmatter.md       # paths: [&quot;src/content/blog/*.md&quot;]
                             # 文章 frontmatter 格式：id 必须 8 位数字、date 用 ISO 8601
```

这样当我改博客文章（Markdown）的时候，deployment.md 的规则压根不会加载，省下的 token 全部留给文章内容的讨论。反之部署的时候，写作相关的规则也不会出来捣乱。
:::

我之前看到有海外开发者的方案把这套生态推到了极致：

&gt; CLAUDE.md 起步；长了拆 `rules/`；高频工作流写到 `commands/`；可复用能力封装成 `skills/`。

CLAUDE.md 只是入口，后面还有 commands（自定义命令）和 skills（可复用能力包）两套机制。

讲到这儿，可能有读者要问了：

&gt; 我用的不是 Claude Code，而是 OpenAI 的 Codex，前面这一通是不是跟我没关系？

也不是。

Codex 那边也有一份自己的「团队约定」，只不过文件名不叫 CLAUDE.md，叫 **AGENTS.md**。

它的作用、写法、加载机制跟 CLAUDE.md 几乎一模一样。你前面学的所有原则，200 行黄金线、具体可验证、告诉 why、持续更新，一条都不用扔，照搬到 AGENTS.md 里就行。

那要是你的项目同时用 Claude Code 和 Codex 呢？两份文件维护成两套，规则一改要改两遍，妥妥的负担。

这里有个特别巧的做法：把所有规则写在 AGENTS.md 里，CLAUDE.md 里只留一行：

```
@AGENTS.md
```

CLAUDE.md 里的 `@文件名` 是个引用指令。Claude Code 启动加载 CLAUDE.md 时，看到 `@AGENTS.md`，会顺着这条引用把 AGENTS.md 的内容也读进来；Codex 那边本来就直接读 AGENTS.md，规则自然也拿到。

一份文件、两个工具、零重复维护。

讲完跨工具这茬，最后提一个容易被忽略的坑：规则之间会打架。

官方文档原话是：「如果两条规则互相矛盾，Claude 可能会随便挑一条。」模型又不是律师，没法判断哪条优先级更高。所以分层之后，得定期 review，把过时的、冲突的规则清掉。我自己的习惯是每 1 到 2 周扫一次。

---

## 05｜`/init` 起步、`/memory` 维护

讲完 CLAUDE.md 怎么写、怎么分层，最后一个绕不开的话题是：怎么把它跑起来。

CLAUDE.md 并不是自动创建的，而是需要我们自己手动创建的。

如果你项目里压根还没 CLAUDE.md，第一步是什么？答案是 `/init`。

在 Claude Code 里输入 `/init`，Claude 会自动扫一遍你的代码库，把分析出来的技术栈、目录结构、常用命令起个草稿。Anthropic 官方文档里有句话：「五分钟时间，永久受益。」

我实测过，`/init` 起的草稿质量出乎意料地好。当然不完美，你得 review 一遍删掉不准的、补上漏掉的，但起点已经比从空文件开始高出几个台阶。

项目跑起来之后，规则怎么补充？最经典的工作流是这样：Claude 在哪儿犯错了，就加一条防御规则。

但你不需要手动打开 CLAUDE.md 编辑，Claude Code 提供了 `/memory` 命令。

session 中途想加规则，直接输入 `/memory` 会弹出 CLAUDE.md 让你直接改。或者你跟 Claude 说一句「记一下这条规则」，它会自动追加到合适的 CLAUDE.md 文件里去。

claudeguide.io 给了一个特别实用的规则触发标准：**Claude 错两次以上，就加一条新规则。** 一次可能是偶发，两次说明规则有缺。再不写就会一直被它坑。

:::note{type=&quot;info&quot;}
**YEYUbaka 注——/memory 实战：** 上周我在写一篇关于 MCP Server 开发的博文（就是本站的《学会这一招，任何API都能变成Claude Code的工具》）。中途 Claude 连续两次把文章 ID 写成了 `20260515a`（带了字母后缀），而我们的规范要求纯数字 8 位。我当场跟 Claude 说了句「记一下：文章 id 必须是 8 位纯数字，格式 YYYYMMDD」，它自动把这条加进了 `.claude/rules/frontmatter.md`。之后整篇文章再没犯过这个错。**两错一规则，这个标准真的实用。**
:::

还有一个常被忽略的命令配合：**Plan Mode**。

复杂任务的时候，按 Shift+Tab 两次切到 Plan Mode，Claude 不直接动手写代码，而是先出一份计划给你看，确认了再执行。

为啥这玩意儿要跟 CLAUDE.md 配合讲？因为 Plan Mode 出计划的时候，会把你 CLAUDE.md 里的规则全考虑进去。一份好的 CLAUDE.md 直接决定了计划的质量，计划出得好不好，决定了最终代码写得对不对。

Claude Code 官方一直在推 Plan Mode 的用法，社区里也基本形成了共识：动手写代码之前先切 Plan Mode，尤其是改动跨多个文件的时候。

我自己实测下来，对 3 个文件以上的改动，Plan Mode 配合 CLAUDE.md 这套组合质量提升肉眼可见。

---

## 06｜可以参考的模板

讲了这么多原则和反例，最后给你一份可以参考的模板：80 行以内、6 段式结构、每段都加了点注释。

这套结构参考了 claude-codex.fr 技术博客里的 6 段式建议，然后稍微做了精简：

```markdown
# CLAUDE.md

## 1. Project Overview
（2-3 行讲清这是个啥项目，技术栈 + 定位）
- 这是一个面向 B 端的订单管理系统
- 技术栈：TypeScript + Next.js 14 + PostgreSQL
- 部署：Vercel + Supabase

## 2. Commands
（最常用的几个命令，Claude 会直接执行）
- 安装依赖：`pnpm install`
- 启动开发：`pnpm dev`
- 跑测试：`pnpm test`
- 类型检查：`pnpm typecheck`
- Lint：`pnpm lint`

## 3. Architecture
（三句话讲完架构，不要展开）
- 前端页面在 app/（App Router）
- API 路由在 app/api/
- 数据库 schema 在 prisma/schema.prisma
- 详细架构见 docs/architecture.md

## 4. Conventions
（团队真实在用的约定）
- 组件文件用 PascalCase（UserCard.tsx）
- 工具函数用 kebab-case（format-date.ts）
- API 返回统一用 { data, error } 格式
- 错误处理用 Result type，不要 throw

## 5. Hard Constraints
（这部分要严，Claude 越界一次就要补）
- 不要写入 production 数据库（去年事故）
- 不要修改 prisma/migrations/ 下已经合入的 migration
- 不要把 .env 文件加入 git
- 所有 API 路由必须过 requireAuth() middleware

## 6. Gotchas
（每个新人都踩过的坑）
- 跑 dev 之前要先 pnpm db:push 同步 schema
- macOS 上 Prisma 偶发崩溃，重启 dev server 就好
- Vercel 部署日志在 dashboard 里看，不在终端
```

这份模板里有几个细节值得留意。

第一，总行数 50 行左右，远低于 200 黄金线，给后续加规则留了空间。

第二，Architecture 段故意写得短，只指住址不复述详情，避开第二节讲的「复述型」陷阱。

第三，Hard Constraints 写了 why（「去年事故」），呼应第三节的「告诉为什么」原则。

第四，Gotchas 部分价值最高，因为这些坑都是踩出来的经验，Claude 没法从代码里推断。

:::note{type=&quot;success&quot;}
**YEYUbaka 注：** 我的 blog-astro-test 项目 CLAUDE.md 就是用这套模板改的，目前保持在 70 行左右。最爽的一点是 Gotchas 段——我记录了三个新人在这个项目上必踩的坑（图片路径必须带 /blog/ 前缀、数学公式少了一个 `$$` 会导致全篇渲染崩溃、部署不要绕开 `deploy-blog.bat`），之后每次 Claude 在相关区域工作时都会主动避开。**这几行 Gotchas 可能值几百行其他规则。**
:::

你照这份模板改，别从头复制。先抄结构，再填你项目的内容。复制完整内容只会让你换了个壳，规则还是不准。

---

## 收尾：3 句话精华

文章讲了一堆，咱们最后做个总结。

如果你只能从这篇文章带走三句话，那就是这三句：

**第一，CLAUDE.md 是给 Agent 的入职手册，不是给人的 README。** 写之前先问自己：这句话是给人看的，还是给 Claude 看的？给人看的留给 README。

**第二，200 行是黄金线，每行都吃 token，多写不如不写。** 复述型、愿望型、术语表型这三类内容直接删，瘦下来 Claude 反而更听话。

**第三，具体可验证、告诉 why、持续更新，三条铁律压过一切技巧。** 哪条规则都别忘了这三个核心。

如果你面试被问到对 CLAUDE.md 的理解，可以这么答：

&gt; 「CLAUDE.md 每次启动都会被完整加载进上下文，规则一多反而稀释模型注意力。社区实测数据是 200 行 92% 遵守率，400 行掉到 70%。我的做法是项目根 CLAUDE.md 控制在 80 行以内，按模块拆到 `.claude/rules/` 下用 path-scoped 加载，配合 `/init` 起步和 `/memory` 维护，规则遵守率明显上来了。」

---

## 参考资料

- [Anthropic 官方文档：CLAUDE.md 使用指南](https://docs.anthropic.com/en/docs/claude-code/claude-md)
- [Anthropic Help Center：Give Claude context with CLAUDE.md and better prompts](https://support.claude.com/en/articles/14553240)
- [claudeguide.io：How to Write Effective CLAUDE.md Files (With 12 Real Examples)](https://claudeguide.io/claude-md-effective-patterns)
- [claude-codex.fr：Mastering CLAUDE.md（6 段式结构推荐来源）](https://claude-codex.fr/en/prompting/claude-md/)
- [SFEIR Institute：The CLAUDE.md Memory System Deep Dive（200 行 92%、模块化 96% 实测数据来源）](https://institute.sfeir.com/en/claude-code/claude-code-memory-system-claude-md/deep-dive/)
- 原文来源：小林coding 公众号《面试官皱眉：&quot;Claude Code 你用了半年，CLAUDE.md 平时怎么维护？&quot;》</content:encoded></item><item><title>学会这一招，任何API都能变成Claude Code的工具——MCP Server开发实战</title><link>https://blog.yeyubaka.top/article/20260515</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260515/</guid><description>看了那么多MCP和Skill的介绍文章，你有没有想过自己写一个？这篇文章用完全免费的天气API作为案例，带你从零开发一个MCP服务器，并把它接入Claude Code。读完你会发现：原来开发一个AI工具，真的没你想的那么难。</description><pubDate>Fri, 15 May 2026 04:00:00 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
很多同学看完 MCP 和 Skill 的概念文之后都有一个共同的疑问：**&quot;道理我都懂，但到底怎么自己写一个？&quot;** 这篇文章就是用最接地气的方式回答这个问题。我选了一个完全免费、无需 API Key 的天气查询服务作为目标，带你一步步完成：读懂 API → 写 MCP Server → 接入 Claude Code → 让 AI 自动查天气。每一步都有代码、有解释、有踩坑记录，包你能跑通。
:::

## 一、先搞清楚我们要做什么

&gt; 与其在概念里打转，不如直接看效果。

### 1.1 最终效果

写完这套 MCP Server 之后，你在 Claude Code 里就能这样用：

```
你：北京现在天气怎么样？
Claude Code：[调用天气工具]
→ 北京当前温度 22.3°C，体感 22.5°C，湿度 59%，大部晴朗

你：那上海呢？未来三天会下雨吗？
Claude Code：[调用预报工具]
→ 上海未来三天都是晴天，27-28°C，降雨概率很低
```

AI 不再只能&quot;聊天气&quot;，而是真的&quot;查天气&quot;。

### 1.2 为什么选&quot;天气 API&quot;作为教学案例

这就像学做菜，第一道菜肯定是番茄炒蛋，而不是佛跳墙。

| 选择标准 | 天气 API（Open-Meteo） | 其他 API（GitHub / 数据库等） |
| :--- | :--- | :--- |
| 是否需要注册 | 不需要 | 大多数需要账号 |
| 是否需要 API Key | 不需要 | 基本都需要 |
| 理解难度 | 一看就懂（温度、湿度、风速） | 需要懂领域知识 |
| 即时反馈 | 换个城市就有不同结果 | 写操作有风险 |
| 服务稳定性 | 开源项目，全球 CDN | 依赖商业服务 |

我们的目标是学会&quot;怎么写 MCP Server&quot;这个通用技能，而不是&quot;怎么对接某个特定 API&quot;。选一个零门槛的服务，你就能把全部注意力放在核心流程上。

### 1.3 你需要准备什么

- Node.js 环境（v18 或以上）
- Claude Code 已安装
- 一个文本编辑器
- 大概 15 分钟时间

### 1.4 整体流程

一张图说清楚整件事的链路：

```
Open-Meteo API（天气数据源）
        ↓
  MCP Server（把 API &quot;翻译&quot;成 AI 能调用的工具）
        ↓
  配置接入 Claude Code
        ↓
  AI 对话中自动查天气
```

&gt; 如果说 API 是外国厨师的菜谱，那 MCP Server 就是中文翻译 + 厨房操作台。AI 通过它就能&quot;做&quot;出这道菜。

---

## 二、读懂 API：MCP Server 的前置功课

&gt; 写 MCP Server 就像盖房子，而读 API 文档就是打地基。地基歪了，后面全白搭。

### 2.1 认识 Open-Meteo

[Open-Meteo](https://open-meteo.com/) 是一个完全免费、开源的世界天气预报 API。它的特点用一句话就能说完：**不用注册、不用 API Key、HTTPS 直接访问、全球覆盖**。

我们用到两个接口：
- **Geocoding API**：把&quot;北京&quot;翻译成经纬度（`39.9, 116.4`）
- **Forecast API**：根据经纬度返回天气数据

### 2.2 核心参数速览

天气预报接口的关键参数：

| 参数 | 含义 | 示例 |
| :--- | :--- | :--- |
| `latitude` | 纬度 | `39.9042`（北京） |
| `longitude` | 经度 | `116.4074`（北京） |
| `current` | 要获取的当前天气字段 | `temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code` |
| `daily` | 每日预报字段 | `temperature_2m_max,temperature_2m_min,precipitation_probability_max` |
| `timezone` | 时区 | `Asia/Shanghai`（推荐 `auto` 自动识别） |
| `forecast_days` | 预报天数 | `3`（默认 7） |

这些参数够你应对大部分场景了。入门先别贪多，把核心参数搞透再说。

### 2.3 浏览器控制台调试：最直观的 API 学习法

按 F12 打开开发者工具，切到 Console 标签，直接粘贴运行：

```javascript
// 直接复制到浏览器控制台就能看到结果
fetch(&apos;https://api.open-meteo.com/v1/forecast?latitude=39.9042&amp;longitude=116.4074&amp;current=temperature_2m&amp;timezone=auto&apos;)
  .then(r =&gt; r.json())
  .then(d =&gt; console.log(&apos;北京当前温度:&apos;, d.current.temperature_2m + &apos;°C&apos;))
```

这个东西我愿称之为&quot;工地上的皮尺&quot;——随时量，随时看，所见即所得，比 Postman 还快。

### 2.4 我们要实现的三个核心功能

不贪多，文章聚焦三个最实用的功能：

1. **城市名转坐标**（Geocoding）：用户说&quot;北京&quot;，自动转成经纬度
2. **查当前天气**：温度、湿度、风速、天气状况
3. **查未来几天预报**：每日最高/最低温度、降雨概率

这三个功能覆盖了&quot;即时查询&quot;和&quot;出行规划&quot;两大场景，学完就能用。

---

## 三、动手写 MCP Server：把 API &quot;翻译&quot;成 AI 听得懂的工具

&gt; 如果说 API 是厨师的菜谱，那 MCP Server 就是翻译 + 厨房操作台。AI 通过它就能&quot;做&quot;出天气查询这道菜。

### 3.1 项目初始化

```bash
mkdir weather-mcp-server
cd weather-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
```

然后创建 `tsconfig.json`：

```json
{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;ES2022&quot;,
    &quot;module&quot;: &quot;Node16&quot;,
    &quot;moduleResolution&quot;: &quot;Node16&quot;,
    &quot;outDir&quot;: &quot;./dist&quot;,
    &quot;rootDir&quot;: &quot;./src&quot;,
    &quot;strict&quot;: true
  },
  &quot;include&quot;: [&quot;src&quot;]
}
```

在 `package.json` 里加一句 `&quot;type&quot;: &quot;module&quot;`，这样才能用 ES Module 的 import。

### 3.2 MCP Server 的骨架

一个 MCP Server 由三个核心组件构成：

| 组件 | 作用 | 类比 |
| :--- | :--- | :--- |
| **McpServer 实例** | 负责通信协议 | 餐厅服务员 |
| **Tool 定义** | 告诉 AI &quot;我能做什么&quot; | 菜单 |
| **Tool Handler** | 实际执行 API 调用 | 后厨 |

我们先搭骨架，再逐个填肉。

### 3.3 核心代码：完整实现

打开 `src/index.ts`，下面是完整代码：

```typescript
import { McpServer } from &quot;@modelcontextprotocol/sdk/server/mcp.js&quot;;
import { StdioServerTransport } from &quot;@modelcontextprotocol/sdk/server/stdio.js&quot;;
import { z } from &quot;zod&quot;;

// ── 基础配置 ──
const GEOCODING_BASE = &quot;https://geocoding-api.open-meteo.com/v1&quot;;
const FORECAST_BASE = &quot;https://api.open-meteo.com&quot;;

// ── 城市名 → 坐标 ──
async function geocode(city: string) {
  const url = `${GEOCODING_BASE}/search?name=${encodeURIComponent(city)}&amp;count=3&amp;language=zh&amp;format=json`;
  const res = await fetch(url);
  if (!res.ok) return null;
  const data = await res.json() as { results?: Array };
  return data.results?.[0] ?? null;
}

// ── 创建 Server ──
const server = new McpServer(
  { name: &quot;weather-server&quot;, version: &quot;1.0.0&quot; },
  {
    capabilities: {},
    instructions: &quot;使用此服务器查询天气信息。先调用 geocode 将城市名转为坐标，再调用天气工具获取天气数据。&quot;
  }
);

// ── Tool 1：城市名转坐标 ──
server.registerTool(
  &quot;geocode&quot;,
  {
    description: &quot;将城市名称转换为经纬度坐标&quot;,
    inputSchema: z.object({
      city: z.string().describe(&quot;城市名称，如&apos;北京&apos;、&apos;上海&apos;&quot;)
    })
  },
  async ({ city }) =&gt; {
    const result = await geocode(city);
    if (!result) return { content: [{ type: &quot;text&quot;, text: `未找到城市：${city}` }] };
    const region = result.admin1 ? `${result.country} ${result.admin1}` : result.country;
    return { content: [{ type: &quot;text&quot;, text: `${result.name}：纬度 ${result.latitude}，经度 ${result.longitude}（${region}）` }] };
  }
);

// ── Tool 2：查询当前天气 ──
server.registerTool(
  &quot;get_current_weather&quot;,
  {
    description: &quot;查询指定城市当前的天气情况&quot;,
    inputSchema: z.object({
      city: z.string().describe(&quot;城市名称，如&apos;北京&apos;、&apos;上海&apos;&quot;)
    })
  },
  async ({ city }) =&gt; {
    const geo = await geocode(city);
    if (!geo) return { content: [{ type: &quot;text&quot;, text: `未找到城市：${city}` }] };

    const url = `${FORECAST_BASE}/v1/forecast?latitude=${geo.latitude}&amp;longitude=${geo.longitude}&amp;current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m&amp;timezone=auto`;
    const res = await fetch(url);
    const data = await res.json() as { current?: Record };

    const c = data.current;
    if (!c) return { content: [{ type: &quot;text&quot;, text: `未能获取 ${city} 的天气数据` }] };

    const weatherMap: Record = {
      0: &quot;☀️ 晴天&quot;, 1: &quot;🌤 大部晴朗&quot;, 2: &quot;⛅ 多云&quot;, 3: &quot;☁️ 阴天&quot;,
      45: &quot;🌫 有雾&quot;, 51: &quot;🌧 小毛毛雨&quot;, 61: &quot;🌧 小雨&quot;, 63: &quot;🌧 中雨&quot;,
      65: &quot;🌧 大雨&quot;, 71: &quot;❄️ 小雪&quot;, 73: &quot;❄️ 中雪&quot;, 75: &quot;❄️ 大雪&quot;,
      80: &quot;🌧 阵雨&quot;, 95: &quot;⛈️ 雷暴&quot;, 96: &quot;⛈️ 冰雹雷暴&quot;, 99: &quot;⛈️ 大冰雹雷暴&quot;
    };

    return {
      content: [{
        type: &quot;text&quot;,
        text: [
          `${geo.name} 当前天气：`,
          `  🌡 温度：${c.temperature_2m}°C（体感 ${c.apparent_temperature}°C）`,
          `  💧 湿度：${c.relative_humidity_2m}%`,
          `  🌬 风速：${c.wind_speed_10m} km/h`,
          `  ☁ 天气：${weatherMap[c.weather_code] || `代码 ${c.weather_code}`}`
        ].join(&quot;\n&quot;)
      }]
    };
  }
);

// ── Tool 3：天气预报 ──
server.registerTool(
  &quot;get_weather_forecast&quot;,
  {
    description: &quot;查询指定城市未来几天的天气预报&quot;,
    inputSchema: z.object({
      city: z.string().describe(&quot;城市名称&quot;),
      days: z.number().default(3).describe(&quot;预报天数，1-7&quot;)
    })
  },
  async ({ city, days }) =&gt; {
    const geo = await geocode(city);
    if (!geo) return { content: [{ type: &quot;text&quot;, text: `未找到城市：${city}` }] };

    const d = Math.min(Math.max(days ?? 3, 1), 7);
    const url = `${FORECAST_BASE}/v1/forecast?latitude=${geo.latitude}&amp;longitude=${geo.longitude}&amp;daily=temperature_2m_max,temperature_2m_min,precipitation_probability_max,weather_code&amp;timezone=auto&amp;forecast_days=${d}`;
    const res = await fetch(url);
    const data = await res.json() as { daily?: { time: string[]; temperature_2m_max: number[]; temperature_2m_min: number[]; precipitation_probability_max: number[]; weather_code: number[] } };

    if (!data.daily) return { content: [{ type: &quot;text&quot;, text: `未能获取 ${city} 的预报数据` }] };

    const weatherSimple = (code: number): string =&gt; {
      if (code  50 ? &quot;🌧&quot; : &quot;☀&quot;;
      lines.push(`  ${data.daily.time[i]} ${icon} ${weatherSimple(data.daily.weather_code[i])} | 高温 ${data.daily.temperature_2m_max[i]}°C / 低温 ${data.daily.temperature_2m_min[i]}°C | 降雨概率 ${data.daily.precipitation_probability_max[i]}%`);
    }

    return { content: [{ type: &quot;text&quot;, text: lines.join(&quot;\n&quot;) }] };
  }
);

// ── 启动 ──
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error(&quot;Weather MCP Server running on stdio&quot;);
}

main().catch((err) =&gt; {
  console.error(&quot;启动失败:&quot;, err);
  process.exit(1);
});
```

代码总共不到 120 行，但结构很清晰：一个 geocode 辅助函数 + 三个 registerTool 调用 + 一个 main 启动入口。

### 3.4 几个关键细节

**关于 import 路径**：注意导入路径必须带 `.js` 后缀，不然 Node.js ESM 会报 `ERR_MODULE_NOT_FOUND`。这是 MCP SDK 的 wildcard export 机制决定的，是很容易踩的坑。

**关于 `FORECAST_BASE`**：基础 URL 是 `https://api.open-meteo.com`，不是 `https://api.open-meteo.com/v1`。我当初写成了后者，结果拼出来的 URL 变成了 `api.open-meteo.com/v1/v1/forecast`，查了半天才发现是重复了路径。

**关于天气代码**：Open-Meteo 用的是 [WMO 天气代码](https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM)，我挑了几个最常用的做了映射。你想更精确的话可以去看官方文档补全。

### 3.5 构建和测试

```bash
# 构建
npx tsc

# 快速验证：手动发送一个初始化请求
printf &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;id&quot;:1,&quot;method&quot;:&quot;initialize&quot;,&quot;params&quot;:{&quot;protocolVersion&quot;:&quot;2024-11-05&quot;,&quot;capabilities&quot;:{},&quot;clientInfo&quot;:{&quot;name&quot;:&quot;test&quot;,&quot;version&quot;:&quot;1.0&quot;}}}\n{&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;notifications/initialized&quot;}\n{&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;id&quot;:2,&quot;method&quot;:&quot;tools/list&quot;,&quot;params&quot;:{}}\n&apos; | node dist/index.js
```

如果看到返回了 3 个 Tool 的定义，说明你的 MCP Server 准备好了。

### 3.6 接入 Claude Code

在项目根目录创建 `.mcp.json`：

```json
{
  &quot;mcpServers&quot;: {
    &quot;weather&quot;: {
      &quot;command&quot;: &quot;node&quot;,
      &quot;args&quot;: [&quot;E:/AI_projects/weather-mcp-server/dist/index.js&quot;]
    }
  }
}
```

路径用你的实际项目路径。重启 Claude Code 后，天气工具就会自动加载。

---

## 四、踩坑与排查

&gt; 代码写完不报错，只是过了第一关；真正能稳定跑起来的，都是踩过坑的。

### 4.1 常踩的坑

| 现象 | 原因 | 解决 |
| :--- | :--- | :--- |
| `ERR_MODULE_NOT_FOUND` | import 路径缺 `.js` 后缀 | 加上 `.js`：`from &quot;.../mcp.js&quot;` |
| API 返回 `&quot;Not Found&quot;` | 基础 URL 重复了 `/v1` | 检查 `FORECAST_BASE` 是否已包含版本路径 |
| 中文城市名查不到 | Geocoding API 未加 `language=zh` | URL 里加 `&amp;language=zh` |
| Claude Code 不显示工具 | `.mcp.json` 语法错误 | `jq . .mcp.json` 检查 JSON 是否合法 |
| 工具加载了但调用失败 | MCP Server 进程没启动 | 检查 `dist/index.js` 路径是否正确 |

### 4.2 排查心法

遇到问题时，最有效的调试方法是：

1. **先在浏览器控制台用 fetch 单独测 API**——看是服务器端问题还是你代码的问题
2. **用 `console.error` 打日志**——记住 MCP 用 stdio 通信，日志必须打到 stderr，不然会干扰协议
3. **手动 pipe 测试请求给 server**——绕过 Claude Code，直接看 server 是否正常响应

&gt; 排查的顺序永远是：先确认 API 正常 → 再确认 server 正常 → 最后排查 Claude Code 配置。

---

## 五、从 MCP 到 Skill：让 AI 更&quot;懂&quot;你的工具

&gt; MCP Server 让 AI 有了&quot;手&quot;（能调用 API），但 Skill 让 AI 有了&quot;脑子&quot;（知道什么时候该用、怎么组合使用）。这就好比你会切菜不等于你会做菜——Skill 就是菜谱。

### 5.1 什么时候需要写一个 Skill

| 场景 | 只用 MCP Server | MCP + Skill |
| :--- | :--- | :--- |
| 问&quot;北京天气&quot; | ✅ AI 知道调用工具 | ✅ 一样 |
| 问&quot;我这周末出差，帮我看看天气&quot; | ❌ AI 不知道该用哪个工具 | ✅ Skill 引导多工具组合 |
| 问&quot;对比 5 个城市，推荐最适合明天出行的&quot; | ❌ AI 可能只查一个 | ✅ Skill 自动多轮查询 + 对比 |

简单来说：**MCP Server 解决了&quot;有没有工具&quot;的问题，Skill 解决了&quot;怎么用工具&quot;的问题**。

### 5.2 写一个简单的天气 Skill

在你的项目 `.claude/skills/` 目录下创建 `weather-reporter.md`：

```markdown
## 触发条件
当用户询问天气、出行建议、气温相关信息时激活。

## 工作流程
1. 先用 geocode 将城市名转为坐标
2. 如果是&quot;当前/现在就&quot;，调用 get_current_weather
3. 如果是&quot;明天/周末/下周&quot;，调用 get_weather_forecast
4. 如果涉及多个城市对比，逐个查询后汇总
5. 用自然语言回答，突出关键信息

## 输出规范
- 默认用摄氏度
- 如果降雨概率 &gt; 50%，提醒带伞
- 如果温度 &gt; 30°C，提醒防暑
- 如果温度  就像买车不用先学造车，很多 MCP Server 别人已经写好了，直接用就行。

### 6.1 推荐已有的 MCP Server

| 项目 | 功能 | 适用场景 |
| :--- | :--- | :--- |
| [puppeteer MCP Server](https://github.com/modelcontextprotocol/servers) | 浏览器自动化 | 网页抓取、截图、表单填写 |
| [filesystem MCP Server](https://github.com/modelcontextprotocol/servers) | 文件系统操作 | 读写本地文件 |
| [brave-search MCP Server](https://github.com/modelcontextprotocol/servers) | 网络搜索 | 实时信息检索 |
| [sequential-thinking MCP Server](https://github.com/modelcontextprotocol/servers) | 强化推理 | 复杂逻辑分析 |

### 6.2 怎么判断&quot;该自己写&quot;还是&quot;用现成的&quot;

- **用现成的**：API 是主流服务（GitHub、Slack、数据库等）→ 直接搜 &quot;MCP Server + 名字&quot;
- **自己写**：内部系统 API、小众服务、需要定制业务逻辑 → 参考本文的套路手写

多数情况下，一个 100 行左右的 MCP Server 就够用了，核心代码量其实很少。

---

## 七、总结

&gt; 回顾一下，我们今天做了什么？

1. 读懂了一个免费天气 API（Open-Meteo）
2. 用不到 120 行 TypeScript 写了一个 MCP Server（三个 Tool）
3. 把它接入了 Claude Code
4. 让 AI 能自动查天气、做预报
5. 还顺带写了个 Skill 让它更聪明

**最关键的一点：这套方法论适用于任何 API。**

新闻 API、股票 API、物流 API、翻译 API……学会今天的内容，你就拥有了&quot;让 AI 连接万物的能力&quot;。每次遇到一个新的 API，你只需要三步：
- 读懂它的参数和返回格式
- 用 `registerTool` 定义工具
- 在 Handler 里调用 fetch

其他都是照搬模板。

&gt; AI 时代的核心竞争力，不是你会用多少 AI 工具，而是你能否把身边的任何一个 API，都变成 AI 能调用的能力。

---

**参考资料**：

- [Open-Meteo 官方文档](https://open-meteo.com/en/docs)
- [MCP 官方文档](https://modelcontextprotocol.io)
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [Claude Code MCP 集成文档](https://docs.anthropic.com/en/docs/claude-code/mcp)
- [WMO 天气代码对照表](https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM)</content:encoded></item><item><title>Claude Code 新版不兼容 Windows？两种方法快速修复</title><link>https://blog.yeyubaka.top/article/20260419</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260419/</guid><description>今天打开 Claude Code 突然报错「指定的可执行文件不是此操作系统平台的有效应用程序」，发现 claude.exe 变成了 1 KB 的残缺文件。原来是新版 Claude Code 不再兼容 Windows，本文记录两种最快解决办法。</description><pubDate>Sun, 19 Apr 2026 12:00:00 GMT</pubDate><content:encoded>:::note{type=&quot;warning&quot;}
2026-04-19 今日踩坑：Claude Code 自动更新后在 Windows 上直接崩溃，claude.exe 变成 1 KB 的残废文件，无法启动。如果你也遇到了同样问题，按本文操作可以快速恢复。
:::

## 问题现象

今天（4月19日）照常启动 Claude Code，结果直接弹出报错：

&gt; 程序&quot;claude.exe&quot;无法运行: **指定的可执行文件不是此操作系统平台的有效应用程序。**

昨天还好好的，今天突然就废了？第一反应是去看看 exe 文件有没有被杀毒软件误删，打开一看——

`C:\Users\你的用户名\AppData\Roaming\npm\node_modules\@anthropic-ai\claude-code\bin\`

发现 `claude.exe` 还在，但大小只有 **1 KB**，正常的应该有几十 MB。

这显然是自动更新拉回来的文件有问题。

## 原因分析

查了一下，Anthropic 的新版 Claude Code 已经**不再提供 Windows 原生 .exe 可执行文件**，改为以其他方式分发，导致 Windows 上的 npm 包里的 claude.exe 变成了一个无效的占位文件（1 KB）。

参考资料：
- https://blog.csdn.net/qq_54470008/article/details/160286310
- https://ask.csdn.net/questions/9512597

## 解决方案

### 方法一：用 winget 重新安装（推荐）

这是最简单彻底的方法，直接走 Anthropic 官方的 Windows 安装包。

**第一步：卸载 npm 版本**

```bash
npm uninstall -g @anthropic-ai/claude-code
```

**第二步：用 winget 安装官方版**

```bash
winget install Anthropic.ClaudeCode
```

:::note{type=&quot;info&quot;}
winget 安装可能需要魔法（访问 GitHub Releases），请确保网络畅通。
:::

安装完成后直接在终端输入 `claude` 验证是否正常启动。

---

### 方法二：降级到兼容版本（临时方案）

如果暂时不想换安装方式，可以把 npm 版本降级到最后一个兼容 Windows 的版本。

**第一步：卸载当前版本**

```bash
npm uninstall -g @anthropic-ai/claude-code
```

**第二步：安装指定版本**

```bash
npm install -g @anthropic-ai/claude-code@2.1.112
```

**第三步：验证**

```bash
claude --version
```

能正常输出版本号就说明恢复正常了。

---

## 安装完别忘了：禁用自动更新

不管用哪种方法，装好之后**一定要禁用自动更新**，不然下次自动更新又给你整回去。

找到你的 `.claude` 目录下的 `settings.json`（通常在 `C:\Users\你的用户名\.claude\settings.json`），添加下面这一行：

```json
{
  &quot;DISABLE_AUTOUPDATER&quot;: &quot;1&quot;
}
```

如果文件里已经有其他配置，加在对应位置就行，注意 JSON 格式不要写错。

:::note{type=&quot;success&quot;}
设置完这个之后，Claude Code 就不会再自动更新到不兼容的版本了，可以安心使用。
:::

## 总结

| 方法 | 适用场景 | 优点 |
|------|----------|------|
| winget 重装（方法一） | 推荐所有人 | 官方维护，长期稳定 |
| npm 降级（方法二） | 临时救急 | 快速恢复，无需切换安装方式 |

两种方法都能解决问题，个人更推荐方法一，走官方 winget 包以后更新维护都更稳。

最后提醒：**装完记得设 `DISABLE_AUTOUPDATER`，别又被自动更新坑了。**</content:encoded></item><item><title>Claude Code 新版本 Windows 兼容性问题：claude.exe 变 1KB 的解决方法</title><link>https://blog.yeyubaka.top/article/20260419</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260419/</guid><description>今天早上启动 Claude Code 突然报错，发现 claude.exe 只有 1KB，是自动更新器把文件搞坏了。这篇文章记录两种修复方法：winget 重装（推荐）和降级到 2.1.112。</description><pubDate>Sun, 19 Apr 2026 04:00:00 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
今天（4 月 19 日）早上准备用 Claude Code，结果启动直接报错，昨天还好好的。检查了一下本地的 `claude.exe`，发现文件只有 1KB——自动更新器把它搞坏了。这篇文章记录排查过程和两种解决方法，遇到同样问题的可以直接跳到解决方案。
:::

## 问题现象

启动 Claude Code 时报错：

```
程序&quot;claude.exe&quot;无法运行: 指定的可执行文件不是此操作系统平台的有效应用程序。
```

去检查本地的 `claude.exe`，发现文件大小只有 **1KB**，正常情况下应该有几十 MB。

原因是 Claude Code 的自动更新器把新版本写入失败，留下了一个损坏的可执行文件。新版本在 Windows 上存在兼容性问题，更新后直接无法运行。

## 解决方法一：卸载后用 winget 重装（推荐）

winget 版本经过打包验证，稳定性比 npm 直接安装更好，优先推荐这个方式。

**第一步：卸载现有版本**

```bash
npm uninstall -g @anthropic-ai/claude-code
```

**第二步：用 winget 重新安装**

```bash
winget install Anthropic.ClaudeCode
```

:::note{type=&quot;warning&quot;}
winget 安装需要访问 GitHub Releases，国内网络可能需要代理。如果下载失败，可以尝试方法二。
:::

安装完成后运行 `claude --version` 验证是否正常。

## 解决方法二：降级到 2.1.112

如果 winget 安装有困难，可以回退到已知稳定的 2.1.112 版本。

```bash
# 第一步：卸载现有版本
npm uninstall -g @anthropic-ai/claude-code

# 第二步：安装指定版本
npm install -g @anthropic-ai/claude-code@2.1.112

# 第三步：验证
claude --version
```

输出显示 `2.1.112` 即代表安装成功。

## 装完之后：禁用自动更新

不管用哪种方式修复，装好之后都建议把自动更新关掉，防止再次被更新器搞坏。

找到你的 `.claude/settings.json`（通常在 `C:\Users\你的用户名\.claude\settings.json`），添加一行：

```json
{
  &quot;DISABLE_AUTOUPDATER&quot;: &quot;1&quot;
}
```

如果文件里已经有其他配置，加在对应位置就行，注意 JSON 格式不要写错。

后续如果要手动升级，确认新版本稳定后再删掉这行配置。

---

**参考资料**：

- [claude code打开报错解决办法](https://blog.csdn.net/qq_54470008/article/details/160286310)
- [Claude Code Windows 启动问题讨论](https://ask.csdn.net/questions/9512597)
- [Claude Code 自动更新失败处理](https://www.cnblogs.com/chenzuoli/articles/19887036)</content:encoded></item><item><title>Hermes Agent 是什么？一篇讲清它和 OpenClaw 的区别、三层记忆与上手方式</title><link>https://blog.yeyubaka.top/article/20260412</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260412/</guid><description>如果你最近一直在看 OpenClaw，那大概率也刷到过 Hermes Agent。这篇文章不讲营销话术，只用项目资料和官方文档，把它的定位、自动长 Skill、三层记忆、工具机制和快速上手方式一次讲清。</description><pubDate>Sun, 12 Apr 2026 04:00:00 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
如果最近 AI Agent 圈你只记住了一个关键词，那多半是 OpenClaw；但如果你再往下看一层，很快就会碰到 Hermes Agent。它吸引人的地方不是“又多了一个终端壳子”，而是它把学习、记忆和工具组织能力一起塞进了系统里。本文按项目资料和官方文档重新整理，重点讲清三个问题：它到底是什么、它和 OpenClaw 差在哪、普通用户该怎么上手。
:::

## Hermes Agent 是什么

先说结论：**Hermes Agent 不是单纯的聊天终端，而是一套“会随着使用逐渐变熟”的 Agent 系统。**

这个项目来自 Nous Research。官方在 README 里给它的定位很直接，核心关键词就是 `self-improving`。翻成更好理解的话，它想做的不是一次性助手，而是一个会在长期使用里不断积累经验的 Agent。

它有几个比较明显的特征：

- 不绑模型厂商，你可以接 OpenRouter、OpenAI、Anthropic、GitHub Copilot，或者任意兼容 OpenAI API 的自定义端点
- 不绑运行位置，既能在本地 CLI 里对话，也能挂到 Telegram、Discord、Slack、WhatsApp、Signal、Email 这类入口
- 不只是“会调工具”，而是会把做过的事沉淀成可复用的 Skills 和记忆
- 能跑在本地，也能跑在 VPS、Docker、SSH、Modal 这类环境里，不一定非得跟着你的笔记本走

![Hermes 欢迎界面](/images/posts/hermes-agent/hermes-welcome.jpg)

*▲ 启动后的 Hermes 更像一个长期在线的 Agent 工作台，而不只是一次性对话框*

---

## 它和 OpenClaw 到底差在哪

很多人会把 Hermes 当成 OpenClaw 的“平替”，这话不能说全错，但也不够准确。

它们解决的是相近问题，路线却不一样。

OpenClaw 更像一个强调控制力和可审计性的框架。你可以很明确地决定它有哪些 Skill、怎么工作、能碰哪些能力。Hermes 则更像是在这个基础上，再往前加了一层“自我积累”：它不只执行任务，还会在执行后把方法留下来。

你可以先记这张表：

| 对比项 | OpenClaw | Hermes Agent |
| :--- | :--- | :--- |
| 主要感觉 | 你来调教 Agent | Agent 会边用边长经验 |
| Skill 来源 | 手动写、手动装、手动维护 | 系统自动沉淀，也能继续装社区 Skill |
| 长期记忆 | 更依赖你主动维护 | 内建持久记忆和会话检索 |
| 默认运行姿态 | 更像按需启动 | 更适合长期在线或后台运行 |
| 适合谁 | 喜欢强控制、强透明 | 想要 Agent 越用越顺手 |

所以我更愿意把 Hermes 理解成 OpenClaw 旁边的另一条路线，而不是谁简单替代谁。

---

## 最核心的能力：它会自己长 Skill

Hermes 最值钱的地方，不是工具数量，而是它把“做完一件事之后要不要沉淀方法”这件事产品化了。

官方文档里把这一层叫做 **procedural memory**，也就是“程序化经验”或者“做事方法的记忆”。它不是只把信息记下来，而是把一套能复用的流程记下来。

从使用效果上看，它的闭环可以理解成四步：

1. 先把任务跑完
2. 在过程中识别哪些步骤是稳定、可复用的
3. 自动创建或更新 Skill
4. 下次遇到类似问题时优先调用这个 Skill

官方文档还写得更细：Hermes 往往会在这些时机生成 Skill：

- 完成了一个复杂任务，尤其是工具调用比较多的时候
- 中间踩了坑，但最后找到了可行路径
- 用户纠正了它的做法
- 它发现了一条值得固定下来的非平凡工作流

![Hermes Skills / Memory 相关工作流示意](/images/posts/hermes-agent/hermes-skill-system.jpg)

*▲ 它更像是在把一次任务里的“做法”整理成下次还能继续用的能力，而不是只留下结果*

这也是为什么很多人会觉得：OpenClaw 更像“你亲手养出来的龙虾”，Hermes 更像“会自己长本事的龙虾”。

---

## 三层记忆为什么重要

官方文档把 Hermes 的持久记忆拆成 `MEMORY.md` 和 `USER.md` 两部分，前者偏环境、项目和经验，后者偏你的偏好、沟通风格和习惯。另外它还支持 `session_search`，会把历史会话存进 SQLite，并用 FTS5 做全文检索。

如果从实际体验去理解，我觉得可以把它看成三层：

| 记忆层 | 主要放什么 | 作用 |
| :--- | :--- | :--- |
| 当前会话上下文 | 这轮任务正在做什么 | 保证它不会在当前对话里掉线 |
| 持久记忆 | 你的偏好、项目事实、长期约定 | 让它跨会话还能记住“你是谁、这是什么项目” |
| Skill 记忆 | 从任务里沉淀出来的方法 | 让它下次不必从零再试一遍 |

这里要注意一点：这个“三层”是我根据官方文档和实际工作方式做的理解性拆分，不是 Hermes 文档里的固定术语，但它很适合拿来理解 Hermes 的优势。

再直白一点说：

- 普通聊天机器人常见的问题是“这轮会，下一轮忘”
- Hermes 试图解决的是“这轮会，下一轮还记得，而且还会更熟”

![Hermes 模块拆解记录](/images/posts/hermes-agent/hermes-research-summary.jpg)

*▲ 把项目结构拆开后会发现，Skills、Memory、Gateway、Toolsets 本来就是 Hermes 设计里的核心部件*

---

## 为什么工具多不是重点，按需启用才是重点

很多人第一次看 Hermes，会先被“40+ 内置工具”吸引住。

但真正更重要的，其实是它的 **toolsets** 机制。

Hermes 不是简单把所有工具全开给 Agent，而是把工具按场景分组，按平台、按任务需要启用。官方文档里能看到它把 Web、Terminal、File、Browser、Vision、Image Gen、TTS、Skills、Memory 这些能力都拆成了可控的工具集。

这样做有三个直接好处：

- 权限更收敛，不会让本来只该查资料的 Agent 顺手拿到不必要的执行权限
- 上下文更干净，不必每轮都把无关工具塞进提示词
- 响应更稳，工具越克制，跑偏和误调用的概率越低

所以 Hermes 的重点从来不是“工具越多越厉害”，而是“该给什么工具时给什么工具”。

---

## 为什么很多人把它看成 OpenClaw 的另一条路线

把前面的点串起来，就比较容易理解 Hermes 为啥会被频繁拿来和 OpenClaw 放在一起讲。

两者的共同目标，都是让 AI 从“会聊天”走向“能做事”。区别在于：

- OpenClaw 更强调把规则和能力清楚交到你手里
- Hermes 更强调系统自己累积经验、自己复用经验

所以如果你特别看重这些点：

- 我想完全知道 Agent 为什么这么做
- 我愿意手动维护 Skill 和行为规范
- 我希望控制粒度尽可能细

那 OpenClaw 这条路会更顺手。

如果你更在意这些点：

- 我不想每次都从零教一遍
- 我希望它做得越多越像我的长期助手
- 我想把它放到后台长期跑

那 Hermes 的吸引力就会更大。

---

## 为什么安装时很多教程会先让你填 OpenRouter API Key

这个问题很常见，根源其实只有一句话：**Hermes 自己不是模型，它是 Agent 的系统层。**

它负责的是：

- 记忆
- 工具
- Skills
- 网关入口
- 学习循环

真正输出 Token、做推理、给回答的，还是你背后接的模型服务。所以官方 Quickstart 里第一步安装完之后，很快就会进入 provider 和 model 的选择。

很多教程喜欢先拿 OpenRouter 起步，原因也不复杂：

- 一个 Key 能接很多模型，试错成本低
- 切换模型方便，适合比较不同效果
- 对新手来说，起步路径比较短

但这不等于 Hermes 只能用 OpenRouter。按照官方文档，OpenAI、Anthropic、GitHub Copilot、Kimi、DeepSeek、Qwen，以及任意兼容 OpenAI API 的自定义端点都可以接。

还有一个很容易忽略的点：**Hermes 要求模型至少有 64K 上下文窗口**。这个限制是官方 Quickstart 明写的，因为多步工具调用、记忆和长上下文任务都需要足够大的上下文空间。

:::note{type=&quot;warning&quot;}
截至 2026 年 4 月 12 日我查官方 Quickstart 时，Hermes 对 Windows 的推荐路径是：先安装 WSL2，再在 WSL2 里运行 Linux 安装命令。也就是说，如果你看到旧教程里还在直接演示原生 Windows 一键安装，优先以官方文档为准。
:::

---

## 5 分钟快速上手

如果你只是想先把它跑起来，步骤其实不复杂。

### 1. 安装

```bash
# Linux / macOS / WSL2 / Android (Termux)
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash

# 安装后重新加载 shell
source ~/.bashrc
```

### 2. 选 provider、模型和工具

```bash
hermes model
hermes tools
hermes setup
```

`hermes model` 用来选模型和服务商，`hermes tools` 用来决定启用哪些工具集，`hermes setup` 则适合重新走一遍完整配置。

![Hermes Quick setup](/images/posts/hermes-agent/hermes-quick-setup.jpg)

*▲ 如果只是先体验，走 Quick setup 就够了，后续再慢慢细配也不迟*

### 3. 开始对话

```bash
hermes
```

跑起来之后，你会看到欢迎界面、当前模型、可用工具和 Skills。

如果你准备把它接到消息平台，可以继续配置消息网关。官方 README 里常见的几个命令如下：

| 命令 | 用途 |
| :--- | :--- |
| `hermes` | 启动交互式 CLI |
| `hermes model` | 选择 provider 和模型 |
| `hermes tools` | 配置可用工具集 |
| `hermes setup` | 重新跑完整配置向导 |
| `hermes gateway` | 启动消息网关相关能力 |
| `hermes claw migrate` | 从 OpenClaw 迁移 |
| `hermes update` | 更新到最新版本 |
| `hermes doctor` | 做环境诊断 |

![Telegram 中与 Hermes 对话](/images/posts/hermes-agent/hermes-telegram-chat.jpg)

*▲ 真正接到消息入口之后，它的感觉就更像一个常驻在线的数字助理了*

---

## 它适合哪些场景

Hermes 不一定适合所有人，但有几类事情确实和它很搭。

### 1. 长期知识助手

如果你经常在同一个项目、同一套工作流里反复切换任务，那它的长期记忆和 Skills 会越来越有价值。你不需要每次都重新讲一遍背景。

### 2. 7×24 后台任务

官方有内置定时调度和消息网关，这让它很适合做日报汇总、通知跟踪、定时巡检、夜间任务这类持续型工作。

### 3. 持续内容创作

如果你经常写技术文章、做资料整理、维护自己的表达风格，那 Hermes 比“一次性问答型工具”更容易越用越顺。

### 4. 多平台 Bot

CLI、Telegram、Discord、Slack、WhatsApp、Signal、Email 这些入口打通后，它就不只是本地终端助手，而是一个真正可以长期在线的 Agent。

---

## 总结

Hermes Agent 最值得注意的，不是“它又接了多少平台”，也不是“它又加了多少工具”，而是它把学习和积累这件事做进了系统本身。

如果你更喜欢强控制、强透明、亲手把 Agent 一点点调出来，那 OpenClaw 依然是很强的一条路线。

但如果你已经开始觉得：

- 手动维护 Skill 很花时间
- 每次重新教 Agent 很累
- 你想要的是一个越用越像长期同事的系统

那 Hermes 的确值得认真试一下。

你可以把它和 OpenClaw 的区别，先记成一句最短的话：

**OpenClaw 更像“你来教它怎么做”，Hermes 更像“它会把做过的事慢慢学会”。**

---

**参考资料**:

- [Hermes Agent GitHub 仓库](https://github.com/NousResearch/hermes-agent)
- [Hermes Agent 官方 Quickstart](https://hermes-agent.nousresearch.com/docs/getting-started/quickstart/)
- [Hermes Agent Skills System 文档](https://hermes-agent.nousresearch.com/docs/user-guide/features/skills)
- [Hermes Agent Persistent Memory 文档](https://hermes-agent.nousresearch.com/docs/user-guide/features/memory/)
- [参考原文：很多人突然不玩小龙虾而用 Hermes Agent 了](https://mp.weixin.qq.com/s/fB4dLHu3BPHE4zWpuqoL4Q)</content:encoded></item><item><title>并发和并行的区别是什么？一篇讲清概念、CPU调度和 Java 里的用法</title><link>https://blog.yeyubaka.top/article/20260411</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260411/</guid><description>面试里经常被问的“并发和并行有什么区别”，很多人会背定义，但一开口还是容易说乱。这篇文章用生活例子、CPU 视角和 Java 代码把这道题讲清楚，让你不仅会背，还能真正讲明白。</description><pubDate>Sat, 11 Apr 2026 04:00:00 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
并发和并行这道题看着基础，实际非常容易答得又空又乱。背书式回答只能说明你看过概念，真正能把它讲顺、讲透、还能落到 Java 代码里，面试官才会觉得你是理解了。本文不绕术语，先讲人话，再讲 CPU 调度，最后落到 Java 和面试答法。
:::

## 为什么这道题总在面试里出现

这道题高频，不是因为它难，而是因为它特别适合区分“知道”和“懂了”。

面试官真正想看的，通常不是一句死定义，而是下面这三件事：

- 你能不能用大白话把两个概念讲清楚
- 你知不知道它背后对应的是 CPU 调度和硬件能力
- 你能不能顺手把它和 Java 多线程、线程池、并行计算联系起来

也就是说，这题表面在问术语，实际上在看你有没有形成完整的理解链路。

&gt; 能把概念讲明白的人，不一定写过特别复杂的并发程序；但连概念都讲不顺的人，大概率写并发代码时也容易混。

---

## 并发和并行，一句话怎么区分

先记最短版本：

**并发，是多个任务在一段时间内交替推进。并行，是多个任务在同一时刻真正一起执行。**

如果你想说得更像面试回答一点，也可以这样讲：

- **并发（Concurrency）**：多个任务在逻辑上同时进行，核心是“调度”和“切换”
- **并行（Parallelism）**：多个任务在物理上同时执行，核心是“同时跑”和“多核”

下面这个表最适合背下来之后自己再展开：

| 对比项 | 并发 | 并行 |
| :--- | :--- | :--- |
| 核心理解 | 同时处理多件事 | 同时做多件事 |
| 是否要求多核 | 不一定 | 通常需要 |
| 执行方式 | 交替推进 | 同时执行 |
| 关键能力 | 调度能力 | 执行能力 |
| 常见场景 | IO 密集任务、请求处理 | 大规模计算、数据并行处理 |

很多人卡住，就是因为把“看起来同时”误当成了“真的同时”。这两个词最容易混的地方，也恰恰在这里。

---

## 用生活例子把两个概念讲直白

如果只讲定义，脑子里很容易是空的。所以面试里最稳的方式，通常是先举一个生活例子。

### 并发：一个人来回切着做

想象你一个人在厨房里同时准备两道菜：

- 切一会儿土豆
- 回头去翻炒鸡蛋
- 然后再回来切肉
- 再去调酱汁

从外面看，好像四件事都在推进。但本质上，你始终只有一个人，只是在不同任务之间快速切换。

这就是**并发**。

关键点不在于“真的同时干”，而在于“多个任务都没有被彻底搁置，而是在交替推进”。

### 并行：多个人同时干

再换一个画面，厨房里现在有两位厨师：

- 厨师 A 负责西红柿炒蛋
- 厨师 B 负责宫保鸡丁

两个人各干各的，动作互不影响，任务也是真正同时进行。

这就是**并行**。

所以可以把它们理解成：

- 并发像是“一个人高频切任务”
- 并行像是“多个人同时干活”

这个类比非常朴素，但特别好用。因为它一下就把“切换”和“同时”区分开了。

---

## 从 CPU 角度看：时间片轮转 vs 多核同时执行

如果想把这题从“会说”提升到“说得专业”，下一步就是把视角切到 CPU。

### 并发的底层：时间片轮转

在单核 CPU 上，一个时刻通常只能真正执行一个线程。

那为什么我们会感觉多个任务都在跑？

因为操作系统会把 CPU 时间切成很多很短的时间片：

- 线程 A 先执行一小会儿
- 时间片到了，切到线程 B
- 再切到线程 C
- 然后可能又切回线程 A

由于切换速度很快，人就会觉得它们“像是在一起跑”。

这就是并发最核心的底层基础：**任务切换**。

### 上下文切换为什么有成本

线程切换不是白来的。每切一次，系统都要做几件事：

- 保存当前线程的运行状态
- 恢复下一个线程的状态
- 更新程序计数器、寄存器等执行现场

这套动作就叫**上下文切换**。

所以线程不是越多越好。线程太多，CPU 可能不是在干活，而是在忙着“切来切去”。

:::note{type=&quot;warning&quot;}
并发能提高系统的任务组织能力，但不等于一定提升性能。如果线程数量远超机器承载能力，频繁上下文切换反而会把性能拖下去。
:::

### 并行的底层：多核同时执行

并行就直接得多。

如果你的机器有多个 CPU 核心，那么多个线程就有机会被分配到不同核心上：

- 核心 1 跑线程 A
- 核心 2 跑线程 B
- 核心 3 跑线程 C

这时候不是“轮流来”，而是真正意义上的“大家一起跑”。

所以并行的前提往往是：**硬件上有足够的执行单元**。

---

## 并发和并行不是对立关系

这也是非常容易答错的一点。

很多人一听“并发”和“并行”，会下意识把它们理解成二选一。其实不是。

更准确的说法应该是：

- **并发是一种程序结构上的组织方式**
- **并行是一种运行时的执行方式**

换句话说，一个程序可以先被设计成并发的，再根据机器条件决定是不是以并行方式跑起来。

比如同一套程序：

- 在单核机器上，它可能只能并发，靠切换推进多个任务
- 在多核机器上，它不仅并发，还可能并行执行

所以这两者的关系，更像是：

**并发描述的是“怎么安排任务”，并行描述的是“任务有没有真的同时执行”。**

这句如果你能顺出来，整道题基本就已经稳了。

---

## 放到 Java 里该怎么理解

讲到这里，如果你还不能落到 Java，面试官通常还会继续追问。

### 1. Java 线程池更多体现的是并发能力

线程池最常见的作用，是让多个任务能被统一管理和调度。它天然就带有并发属性。

```java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrencyDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        executor.submit(() -&gt; handleRequest(&quot;订单查询&quot;));
        executor.submit(() -&gt; handleRequest(&quot;库存检查&quot;));
        executor.submit(() -&gt; handleRequest(&quot;物流追踪&quot;));

        executor.shutdown();
    }

    private static void handleRequest(String taskName) {
        System.out.println(Thread.currentThread().getName() + &quot; 处理任务: &quot; + taskName);
    }
}
```

这段代码的重点不是“3 个任务一定同时执行”，而是：

- 多个任务被同时提交
- 线程池负责调度它们
- 它们可以交替推进，也可能在多核机器上部分并行

所以你可以说：**Java 的线程池先解决的是并发组织问题，至于是否并行，要看底层线程调度和 CPU 核数。**

### 2. `parallelStream()`更强调并行处理

当你明确要把一批数据拆开，让多个核心一起算时，`parallelStream()` 就是一个很常见的入口。

```java
import java.util.List;

public class ParallelStreamDemo {
    public static void main(String[] args) {
        List numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);

        numbers.parallelStream()
                .map(ParallelStreamDemo::heavyCompute)
                .forEach(result -&gt;
                        System.out.println(Thread.currentThread().getName() + &quot; -&gt; &quot; + result)
                );
    }

    private static int heavyCompute(int value) {
        return value * value;
    }
}
```

这时候更偏向的是“让多核一起干活”，所以它更接近**并行计算**。

但也别把它神化。`parallelStream()`适合的是可拆分、彼此独立、计算量比较明确的任务。如果任务很轻、共享状态很多，或者顺序要求很强，它未必划算。

### 3. `ForkJoinPool`是 Java 里典型的并行计算工具

`ForkJoinPool` 的思路很适合解释并行：

- 把大任务拆成小任务
- 小任务分散到多个工作线程
- 多个核心一起处理
- 最后把结果再汇总

```java
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class ForkJoinDemo {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        int sum = pool.invoke(new SumTask(1, 100));
        System.out.println(&quot;sum = &quot; + sum);
    }

    static class SumTask extends RecursiveTask {
        private final int start;
        private final int end;

        SumTask(int start, int end) {
            this.start = start;
            this.end = end;
        }

        @Override
        protected Integer compute() {
            if (end - start  并发和并行的区别，核心在于一个是“交替推进”，一个是“同时执行”。  
&gt; 并发强调的是多个任务在一段时间内都在前进，哪怕底层只有一个 CPU 核心，也可以通过时间片轮转实现。  
&gt; 并行强调的是真正意义上的同时运行，通常依赖多核 CPU。  
&gt; 放到 Java 里，线程池更多体现的是并发调度能力，而像 `parallelStream()` 和 `ForkJoinPool` 这类工具，更偏向并行计算。

如果面试官继续追问，你再补下面两点：

1. 并发的底层关键是上下文切换
2. 并发和并行不是对立关系，而是“设计方式”和“执行方式”的区别

这套答法的优点是层次很清楚：

- 第一层：先下定义
- 第二层：解释底层
- 第三层：落到 Java

只要不乱，这题一般就不会翻车。

---

## 常见误区

### 误区 1：并发就是同时执行

不是。

并发可以只是“轮着来”，只是轮得很快，所以看起来像同时。

### 误区 2：线程越多，并发性能越高

也不是。

线程多到一定程度，CPU 会把大量时间浪费在上下文切换上，吞吐量反而可能下降。

### 误区 3：单核 CPU 就没有并发

单核 CPU 依然可以并发。

它做不到真正的并行，但完全可以通过任务切换让多个线程都获得推进机会。

### 误区 4：`parallelStream()`一定更快

不一定。

如果数据量小、任务本身很轻、拆分和合并的开销不小，`parallelStream()`反而可能更慢。

### 误区 5：并发和并行只能选一个

也不对。

很多程序在设计上是并发的，在多核机器上执行时又能体现出并行效果。它们经常是一起出现的。

---

## 总结

把这道题真正讲清楚，其实就抓住四句话：

- 并发是多个任务交替推进
- 并行是多个任务同时执行
- 并发靠调度，并行靠多核
- 并发是程序结构，并行是执行方式

如果只是背“逻辑同时”和“物理同时”，你最多算答到了表面；如果你能进一步讲到时间片轮转、上下文切换、Java 线程池和 `ForkJoinPool`，那这题基本就从“会背”变成“会讲”了。

面试里很多基础题都不是难在知识点本身，而是难在你能不能把它讲得清楚、讲得顺、讲得落地。并发和并行，就是一个很典型的例子。

---

**参考资料**:

- [阿里面试官：并发和并行的区别是什么？这都说不清楚吗？](https://mp.weixin.qq.com/s/qXg28gMMeh18QKAo9d645w)
- [Oracle Docs: Fork/Join](https://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html)
- [Oracle Docs: Executor Framework](https://docs.oracle.com/javase/tutorial/essential/concurrency/executors.html)</content:encoded></item><item><title>从Token到Agent Skill，10分钟带你弄清楚AI名词</title><link>https://blog.yeyubaka.top/article/20260330</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260330/</guid><description>AI圈子的高速发展，按月更新的频率不断涌现出各式各样的名词，LLM,Token,Context,Prompt,Tool,MCP,Function Call,RAG,Agent,Skills等等如果你是想快速了解并学会用AI那么这篇文章应该非常适合你，不纠结于底层原理简单概括使用场景及功能</description><pubDate>Mon, 30 Mar 2026 05:06:58 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
AI圈子的发展速度堪比火箭，几乎每个月都有新名词冒出来。LLM、Token、Context、Prompt、Tool、MCP、Agent、Skills……如果你刚接触AI，可能会被这些术语搞得一头雾水。别担心，这篇文章就是为你准备的！我会用最通俗的语言和生活中的例子，带你快速搞懂这些概念，不纠结底层原理，只讲你能听懂的大白话。
:::

## 一切的基础：LLM（大语言模型）

**LLM = Large Language Model，大语言模型**

### 它是什么？

简单来说，LLM就是一个&quot;超级会说话的AI&quot;。它能理解你说的话，也能生成回复。你平时用的豆包、ChatGPT、文心一言、Kimi、通义千问，背后都是LLM在干活。

### 类比理解

把它想象成一个**读过全世界几乎所有书籍、文章、网页的&quot;学霸&quot;**。这个学霸的特点：
- 记忆力超群，看过什么基本都能记住
- 表达能力极强，能用各种风格说话
- 但它**没有真正的意识**，它只是在&quot;预测下一个最可能出现的字是什么&quot;

就像你玩&quot;成语接龙&quot;，LLM做的是类似的事，只不过它接的不是成语，而是每一个字、每一个词。

### 它的作用

LLM的核心作用就是**理解和生成自然语言**。它能：
- 回答问题
- 写文章、写代码
- 翻译语言
- 总结长文
- 甚至陪你聊天解闷

### 常见场景

- **日常使用**：打开ChatGPT问问题、让Kimi帮你总结PDF
- **产品开发**：客服机器人、智能助手、内容生成工具
- **编程辅助**：GitHub Copilot帮你写代码

### 需要注意的

LLM不是万能的。它**会犯错**（俗称&quot;幻觉&quot;），有时候会一本正经地胡说八道。所以重要信息一定要核实，别全信。

![LLM对话界面示例](/images/posts/ai-nouns/llm-hallucination.webp)

*▲ 常见的LLM产品界面，背后都是大语言模型在驱动*

---

## Token：AI的&quot;字数单位&quot;

**Token是LLM处理文字的基本单位**

### 它是什么？

Token是AI处理文字的&quot;最小计量单位&quot;。AI不是按&quot;字&quot;或&quot;词&quot;来理解文字的，而是把文字拆成更小的片段——这就是Token。

中文里，**一个Token大约等于0.5~1.5个汉字**。英文里，一个Token大约是0.75个单词。

### 类比理解

想象你去菜市场买菜：
- 你不能按&quot;粒&quot;买米，老板会烦死
- 所以你按&quot;斤&quot;买，&quot;斤&quot;就是一个计量单位

Token就是AI世界的&quot;斤&quot;。AI不按&quot;字&quot;算，按&quot;Token&quot;算。

### 它的作用——为什么Token很重要？

**1. 计费单位**
AI服务通常按Token收费，就像打电话按分钟计费、坐出租车按公里计费一样。你用的越多，花的钱越多。

**2. 输入比输出贵**
这是一个很多人不知道的事实：**输入Token通常比输出Token贵好几倍**。不同厂商定价不同，但普遍规律是：
- 输入Token（你发给AI的内容）：更贵
- 输出Token（AI回复你的内容）：便宜

为什么？因为AI&quot;理解&quot;你说的话比&quot;生成&quot;回复要消耗更多计算资源。

**3. 长度限制**
每次对话能处理的Token数量有限。就像手机短信有字数限制、微博有140字限制一样。

### 常见场景

- **看账单**：用API调用AI服务时，账单上会显示&quot;输入Token xxx，输出Token xxx，费用xx元&quot;
- **选模型**：不同模型有不同Token限制，有的能处理4000 Token，有的能处理128000 Token
- **控制成本**：发很长的文章给AI分析会消耗大量Token，省钱的话可以先截取关键段落

### 举个例子

&gt; 你说：&quot;今天天气怎么样？&quot;（约5个Token）
&gt; AI回答：&quot;今天晴天，气温25度，适合出门。&quot;（约12个Token）
&gt; 这次对话你就消耗了约17个Token

如果输入Token价格是输出Token的3倍，那这次对话的成本 = 5×3 + 12×1 = 27个&quot;计费单位&quot;。

![Token计费示例](/images/posts/ai-nouns/token-pricing.png)

*▲ API调用时的Token计费明细，输入和输出分别计算*

---

## Context：AI的&quot;短期记忆&quot;

**Context = 上下文，AI能&quot;记住&quot;的对话内容**

### 它是什么？

Context Window（上下文窗口）就是AI一次性能&quot;记住&quot;的内容总量，单位也是Token。

### 类比理解

想象你在和朋友打电话聊天：
- 如果只聊了几句，前面的内容你记得清清楚楚
- 如果聊了一个小时，你可能就忘了开头说了啥

人的短期记忆有限，AI也一样。**Context Window就是AI的&quot;短期记忆容量&quot;**。

### 它的作用

Context决定了AI能同时处理多少信息：
- **Context小**（比如4000 Token）：AI容易&quot;忘事&quot;，适合简单问答
- **Context大**（比如128000 Token）：AI能处理超长内容，比如整本书、长会议记录

### 实际影响

**场景1：改论文**
&gt; 你让AI帮你改一篇5000字的论文。
&gt; - Context小：AI改到后面就忘了你前面说的&quot;用学术风格&quot;的要求
&gt; - Context够大：AI能从头到尾保持一致的修改风格

**场景2：长对话**
&gt; 你和AI聊了50轮天。
&gt; - Context小：AI早就忘了你第一句说了什么
&gt; - Context大：AI能记住整个对话脉络

### 常见场景

- **选择产品时**：不同AI产品的Context大小不同，需要处理长文档就选Context大的
- **对话中**：聊太久发现AI开始&quot;答非所问&quot;，很可能是Context满了
- **开发时**：开发者需要合理管理Context，把最重要的信息放在最前面

### 一个数据对比

| 产品/模型 | Context大小 | 大约能处理 |
|-----------|------------|-----------|
| 早期GPT-3 | 4000 Token | 约3000字 |
| GPT-4 | 128000 Token | 约10万字 |
| Claude | 200000 Token | 约15万字 |
| Kimi | 2000000+ Token | 约150万字 |

![Context对比示意图](/images/posts/ai-nouns/context-comparison.png)

*▲ 不同模型的Context Window大小对比，越大能记住的内容越多*

---

## Prompt：和AI沟通的&quot;话术&quot;

**Prompt = 提示词，你告诉AI该做什么的指令**

### 它是什么？

Prompt就是你给AI的&quot;指令&quot;或&quot;要求&quot;。你说的话，就是Prompt。

### 类比理解

想象你在餐厅点菜：
- 你说&quot;来份辣的&quot; → 厨师可能做麻婆豆腐，也可能做辣子鸡
- 你说&quot;来份麻婆豆腐，微辣，不要葱，多加点豆腐&quot; → 厨师做出来的就是你想要的

**Prompt就是点菜的话术**。你说得越清楚，AI做出来的东西越符合你的预期。

### 它的作用——为什么现在的产品都进化成Agent了？

早期的AI产品就是&quot;你问一句，它答一句&quot;。但现在的AI产品，不仅仅是个LLM了，基本都进化成了**Agent（智能体）**。

**进化的核心就是因为Prompt。**

在Agent中，Prompt的作用发生了质的变化：
- 不只是&quot;问问题&quot;
- 更多是**定义Agent本身的能力和行为约束**

### Prompt的分类

**1. System Prompt（系统提示词）—— 用户看不见的&quot;幕后设定&quot;**

这是开发者提前写好的&quot;人设&quot;和规则，用户看不到。它规定了：
- AI的身份（&quot;你是一个专业的翻译助手&quot;）
- 行为准则（&quot;只翻译，不要解释&quot;）
- 能力范围（&quot;你能调用搜索工具和翻译API&quot;）
- 输出格式（&quot;用JSON格式返回结果&quot;）

**2. User Prompt（用户提示词）—— 你实际输入的内容**

这就是你打字输入的东西，比如&quot;你好&quot;、&quot;帮我写篇文章&quot;、&quot;翻译这段话&quot;。

### 重要认知

**当你输入简单的&quot;你好&quot;时，背后发生了什么？**

Agent会携带大量System Prompt一起发送给LLM。所有这些内容的组合，才是真正的输入Token。

```
实际发送给LLM的内容 =
  System Prompt（开发者写的设定）+
  历史对话记录（Context）+
  可用工具列表（Tools）+
  你的输入（User Prompt）
```

所以你输入2个字，实际可能消耗了几百个Token的输入额度。

### 举个例子

&gt; 你输入：&quot;帮我写首诗&quot;
&gt;
&gt; 实际发送给AI的内容可能包括：
&gt; - 系统设定：&quot;你是一个专业的诗人，擅长现代诗创作，风格偏向浪漫主义&quot;
&gt; - 行为约束：&quot;请用中文回复，不要超过20行，每行不超过15个字&quot;
&gt; - 工具信息：&quot;你可以调用【联网搜索】工具获取灵感&quot;
&gt; - 你的输入：&quot;帮我写首诗&quot;
&gt;
&gt; 这些加起来才是真正的输入Token，可能有好几百个。

### 常见场景

- **日常使用**：你给ChatGPT写的每一句话都是Prompt
- **提示词工程**：有人专门研究怎么写Prompt让AI输出更好的结果
- **产品开发**：开发者花大量时间优化System Prompt，让Agent表现更稳定

### 一句话总结

**Prompt写得好，AI像专家；Prompt写得差，AI像智障。**

![Prompt对比效果](/images/posts/ai-nouns/prompt-comparison.png)

*▲ 同一个问题，不同的Prompt写法，输出质量天差地别*

---

## Tool：AI的&quot;手脚&quot;

**Tool = 工具，让AI从&quot;能说&quot;变成&quot;能做&quot;**

### 它是什么？

光会说话不够，AI还需要能&quot;动手&quot;干活。Tool就是AI的能力扩展，让它能真正去执行操作。

### 类比理解

LLM就像一个**被绑在椅子上的天才**：
- 脑子极好使，什么知识都知道
- 但手脚被绑着，什么都做不了

Tool就是**给它松绑，给它装上各种工具**：
- 给它电脑 → 它能操作文件
- 给它手机 → 它能发消息
- 给它浏览器 → 它能上网搜索

### 它的作用

Tool让AI从&quot;纸上谈兵&quot;变成&quot;真刀真枪&quot;：
- 没有Tool：AI只能&quot;说&quot;，告诉你怎么做
- 有了Tool：AI能&quot;做&quot;，直接帮你完成任务

### 工具的分类

**1. 内置工具 —— AI自带的&quot;基础装备&quot;**

有些AI Agent只有很少的内置工具，但凭借这些工具就能完成几乎所有事情。

比如 **OpenClaw 只有4个内置工具**：
- `read` → 读取文件内容
- `write` → 写入/创建文件
- `edit` → 编辑修改文件
- `bash` → 执行命令行操作

就这4个工具，它就能帮你写代码、改配置、跑程序、管理文件，几乎无所不能。

**2. 外置工具 —— 可以无限扩展的&quot;外挂装备&quot;**

- **自己写**：你可以开发任何你需要的工具，比如&quot;发送邮件&quot;、&quot;操作数据库&quot;、&quot;调用某个API&quot;
- **用别人的**：社区里有很多人写好了现成的工具，拿来就能用

### 常见场景

- **编程助手**：AI用read/write/edit工具帮你改代码
- **数据分析**：AI用Python工具帮你跑数据分析
- **自动化办公**：AI用邮件工具帮你发邮件，用日历工具帮你安排会议
- **网页操作**：AI用浏览器工具帮你填表单、点按钮

### 举个例子

&gt; 你说：&quot;帮我把项目里所有的console.log都删掉&quot;
&gt;
&gt; AI的执行过程：
&gt; 1. 用`read`工具读取文件
&gt; 2. 找到所有console.log的位置
&gt; 3. 用`edit`工具删除它们
&gt; 4. 用`bash`工具运行测试确保没出错
&gt;
&gt; 整个过程你只需要说一句话，AI自己搞定。

![Tool工具执行示例](/images/posts/ai-nouns/ai-readwrite-tools.jpg)
*▲ AI调用read/write/edit工具自动完成代码修改*

---

## MCP：AI工具的&quot;标准化插座&quot;

**MCP = Model Context Protocol，模型上下文协议**

### 它是什么？

MCP可以理解为**AI工具的外包平台**。它把某一个领域的所有工具都打包在一起，统一提供给AI使用。

### 类比理解

**没有MCP之前：**
想象你搬了新家，要买各种电器：
- 电视 → 一种插头
- 冰箱 → 另一种插头
- 洗衣机 → 又一种插头
- 空调 → 还得专门装个插座

每个电器都要单独接线，麻烦得要死。

**有了MCP之后：**
就像**USB接口**统一了所有外设：
- 鼠标 → USB插上就能用
- 键盘 → USB插上就能用
- U盘 → USB插上就能用

MCP就是AI世界的&quot;USB接口&quot;。你只需要接入一个MCP服务，这个领域的所有工具都能用。

### 它的作用

**1. 标准化**
不用每个工具都单独对接，统一接口，即插即用。

**2. 生态化**
一个MCP可以包含几十上百个工具。比如&quot;GitHub MCP&quot;包含了操作代码仓库的所有能力，&quot;数据库MCP&quot;包含了所有数据库操作能力。

**3. 降低门槛**
开发者不用为每个AI产品单独写工具适配，写一次MCP工具，所有支持MCP的AI都能用。

### 通俗总结

&gt; MCP = Agent的外包公司
&gt;
&gt; 你把一个领域的所有工具需求&quot;外包&quot;给这个MCP，它来统一管理、统一提供。

### 常见场景

- **GitHub MCP**：让AI能操作你的代码仓库，看Issue、改代码、提PR
- **数据库MCP**：让AI能查询、修改你的数据库
- **文件MCP**：让AI能管理你的本地文件
- **网页MCP**：让AI能操作浏览器，填表单、抓数据

### 举个例子

&gt; 你想让AI帮你管理项目，需要它能：
&gt; - 查Issue
&gt; - 改代码
&gt; - 提PR
&gt; - 看CI状态
&gt;
&gt; **没有MCP**：你需要分别对接GitHub的4个不同API，写4段适配代码
&gt; **有MCP**：接入一个GitHub MCP，所有能力都有了

![MCP标准化接口示意图](/images/posts/ai-nouns/mcp-extension.jpg)

*▲ MCP就像USB Hub，一个接口连接所有工具*

---

## Loop：AI的&quot;循环思考&quot;

**Loop = 循环，Agent如何反复调用LLM来完成任务**

### 它是什么？

Agent不是一次调用AI就完事了，而是会**循环往复**地思考、行动、检查结果，直到达成目标。这个循环过程就是Loop。

### 类比理解

想象你让一个助手帮你整理房间：

**没有Loop（一次性）：**
&gt; 你说：&quot;帮我整理房间&quot;
&gt; 助手看了一眼，说了一句&quot;好的&quot;，然后就没然后了

**有Loop（循环式）：**
&gt; 你说：&quot;帮我整理房间&quot;
&gt; 助手开始干活：
&gt; 1. 先看看房间有多乱（思考）
&gt; 2. 开始收拾书桌（行动）
&gt; 3. 检查书桌收拾好了没（检查结果）
&gt; 4. 接下来收拾衣柜（思考下一步）
&gt; 5. 收拾衣柜（行动）
&gt; 6. 检查衣柜（检查结果）
&gt; 7. 继续收拾床铺……
&gt; 直到整个房间整洁了才停下来

这就是Loop的力量——**不完成不罢休**。

### 它的作用

Loop让Agent具备了**自主完成任务**的能力：
- 不需要你一步步指导
- Agent自己判断下一步该做什么
- 做完一步自动进入下一步
- 直到任务完成或遇到无法解决的问题

### 工作流程

```
1. 接收任务 → &quot;帮我订明天去北京最便宜的机票&quot;
2. 调用LLM思考 → &quot;我应该先搜索机票信息&quot;
3. 执行工具 → 调用搜索工具查机票
4. 查看结果 → 搜到了10个航班
5. 回到思考 → &quot;我需要对比价格，选最便宜的&quot;
6. 执行工具 → 对比价格，选出最便宜的
7. 继续思考 → &quot;接下来需要填写乘客信息&quot;
8. 执行工具 → 填写信息
9. 继续思考 → &quot;确认下单&quot;
10. 执行工具 → 下单
11. 检查 → 出票成功，任务完成！
```

### 常见场景

- **复杂任务**：需要多步骤才能完成的任务，Agent靠Loop一步步推进
- **错误重试**：某一步失败了，Agent可以自己重试或换一种方式
- **条件判断**：根据不同结果走不同的分支路径

### 举个例子

&gt; 你让Agent：&quot;帮我调研一下市面上最好的3款机械键盘，做个对比表格&quot;
&gt;
&gt; Agent的Loop过程：
&gt; - 第1轮：搜索&quot;2026年机械键盘推荐&quot;
&gt; - 第2轮：读取搜索结果，提取关键信息
&gt; - 第3轮：搜索第一款键盘的详细参数
&gt; - 第4轮：搜索第二款键盘的详细参数
&gt; - 第5轮：搜索第三款键盘的详细参数
&gt; - 第6轮：整理信息，生成对比表格
&gt; - 第7轮：检查表格是否完整，补充缺失信息
&gt; - 完成！

![Agent Loop循环工作流程](/images/posts/ai-nouns/agent-loop.png)

*▲ Agent的Loop循环：思考→行动→检查→再思考，直到任务完成*

---

## Agent：AI的&quot;完全体&quot;

**Agent = 智能体，能自主完成任务的AI系统**

### 它是什么？

Agent不是一个新的技术，而是**把前面所有概念组合起来的整体架构**。

### 类比理解

如果说LLM是一个**被绑在椅子上的天才**，那Agent就是：
1. 给他松绑（解除限制）
2. 给他工具（Tool）
3. 告诉他目标和规则（Prompt）
4. 让他自己决定怎么干（Loop）
5. 给他足够的记忆（Context）
6. 告诉他怎么使用各种工具（MCP）

然后你就可以说：&quot;帮我把这件事搞定&quot;，然后去喝杯咖啡，回来就完事了。

### 它的作用

Agent的核心价值是**自主性**：
- 你只需要说&quot;目标&quot;，不需要说&quot;步骤&quot;
- Agent自己拆解任务、自己执行、自己检查
- 遇到问题自己想办法解决

### 所有概念的组合

```
┌─────────────────────────────────────────┐
│              Agent（智能体）              │
│                                         │
│  ┌───────────┐    ┌──────────────────┐  │
│  │  LLM（大脑）│    │  Prompt（指令）   │  │
│  │  思考决策  │    │  人设+规则       │  │
│  └─────┬─────┘    └────────┬─────────┘  │
│        │                   │            │
│  ┌─────▼───────────────────▼─────────┐  │
│  │         Loop（循环执行）           │  │
│  │  思考 → 行动 → 检查 → 再思考...    │  │
│  └─────┬───────────────────┬─────────┘  │
│        │                   │            │
│  ┌─────▼─────┐    ┌────────▼─────────┐  │
│  │ Tool（手脚）│    │ MCP（工具库）     │  │
│  │ 执行操作   │    │ 标准化接口       │  │
│  └───────────┘    └──────────────────┘  │
│                                         │
│  消耗：Token（成本）                     │
│  记忆：Context（上下文窗口）              │
└─────────────────────────────────────────┘
```

### 常见场景

- **编程助手**：Claude Code、Cursor、GitHub Copilot Workspace
- **自动化办公**：自动处理邮件、整理数据、生成报告
- **个人助理**：安排日程、订机票、查信息
- **内容创作**：从调研到写作到排版一条龙

### 举个例子

&gt; 你是一个创业者，想让AI帮你做竞品分析。
&gt;
&gt; **传统方式（用LLM）：**
&gt; 1. 你自己去搜竞品信息
&gt; 2. 把信息复制粘贴给AI
&gt; 3. 让AI总结
&gt; 4. 你自己整理成报告
&gt;
&gt; **Agent方式：**
&gt; 1. 你说：&quot;帮我分析一下XX产品的竞品，出一份报告&quot;
&gt; 2. Agent自己去搜索、去读网页、去对比
&gt; 3. Agent自己整理数据、生成图表、写出报告
&gt; 4. 你收到一份完整的报告
&gt;
&gt; 你只需要说一句话，剩下的Agent全包了。

![Agent自主完成任务示例](/images/posts/ai-nouns/agent-completing-task.png)

*▲ Agent自主完成复杂任务的完整过程*

---

## Skill：AI的&quot;技能包&quot;

**Skill = 技能，可复用的高级能力模块**

### 它是什么？

Skill是在Agent基础上的进一步封装。把常用的、复杂的任务流程打包成一个&quot;技能包&quot;，下次直接调用就行。

### 类比理解

如果Tool是**单个动作**，Skill就是**一套连招**：

- Tool = 游戏里的&quot;普通攻击&quot;（打一下）
- Skill = 游戏里的&quot;技能&quot;（一套华丽的连招，伤害爆炸）

或者用职场来类比：
- Tool = 员工会的&quot;单项技能&quot;（会用Excel、会写邮件、会做PPT）
- Skill = 员工的&quot;岗位能力&quot;（&quot;市场分析能力&quot; = 收集数据 → 分析数据 → 做图表 → 写报告 → 做PPT汇报）

### 它的作用

**1. 复用性**
一次配置，反复使用。不用每次都从头教AI怎么做。

**2. 降低门槛**
用户不需要知道背后有多少步骤，直接调用Skill就行。

**3. 标准化**
同一个Skill在不同场景下表现一致，质量有保障。

### Tool vs Skill 的区别

| | Tool（工具） | Skill（技能） |
|---|---|---|
| 粒度 | 单个动作 | 一套流程 |
| 复杂度 | 简单 | 复杂 |
| 例子 | &quot;读取文件&quot; | &quot;写SEO文章&quot; |
| 用户操作 | 需要一步步指导 | 一句话搞定 |

### 举个例子

**Skill：&quot;SEO文章写作&quot;**

背后包含的完整流程：
1. 搜索关键词相关的热搜问题
2. 分析竞品文章结构
3. 生成文章大纲
4. 分段撰写正文
5. 检查SEO指标（关键词密度、标题结构等）
6. 生成Meta描述
7. 排版优化
8. 输出最终文章

你只需要说：&quot;帮我写一篇关于AI的文章&quot;，Skill自动完成以上所有步骤。

### 常见场景

- **开发者的Skill**：代码审查、Bug修复、文档生成
- **运营的Skill**：社交媒体内容生成、数据分析报告
- **写作的Skill**：SEO文章、技术博客、营销文案
- **设计的Skill**：生成UI组件、配色方案、图标设计

![Skill技能包示例](/images/posts/ai-nouns/skill-example.jpeg)

*▲ 一个Skill背后可能包含多个步骤的自动化流程*

---

## 一张图总结所有概念

```
Token 是基础单位 → 决定成本和长度限制
       ↓
Context Window 是瓶颈 → 影响任务复杂度
       ↓
Prompt 是沟通桥梁 → 决定输出质量
       ↓
Tool 是能力扩展 → 让模型能&quot;动手&quot;
       ↓
MCP 是标准化 → 统一工具生态
       ↓
Loop 是工作方式 → 循环调用直到完成
       ↓
Agent 是整体架构 → 实现自主任务执行
       ↓
Skill 是高级封装 → 可复用的能力模块
```

### 一句话串起来

&gt; 你通过 **Prompt** 给 **Agent** 下达任务，Agent 用 **LLM** 作为大脑思考，在 **Context** 的记忆范围内，通过 **Loop** 循环调用各种 **Tool**（通过 **MCP** 标准化接入），每一步都消耗着 **Token**，最终完成任务。而 **Skill** 就是把这一整套流程打包好，让你下次一键调用。

---

## 总结

看到这里，你应该对这些AI名词有了清晰的认识：

| 名词 | 一句话解释 | 类比 |
|------|-----------|------|
| **LLM** | 会说话的AI大脑 | 读过所有书的学霸 |
| **Token** | AI处理文字的单位，也是计费标准 | 菜市场的&quot;斤&quot; |
| **Context** | AI的短期记忆 | 电话聊天能记住的内容量 |
| **Prompt** | 和AI沟通的话术 | 餐厅点菜的话术 |
| **Tool** | AI的手脚，让它能真正执行操作 | 给天才松绑+给工具 |
| **MCP** | 工具的标准化接口，即插即用 | USB接口 |
| **Loop** | AI循环思考的工作方式 | 助手整理房间的过程 |
| **Agent** | 整合所有能力的智能体 | 能独立干活的员工 |
| **Skill** | 打包好的技能包，开箱即用 | 游戏里的连招技能 |

### 最后的建议

其实，**最好的学习方式就是动手开发一个自己的Agent**。当你真正去搭建的时候：
- 你会看到Token是怎么消耗的
- 你会感受到Context不够用是什么体验
- 你会明白Prompt写得好不好差别有多大
- 你会体会到Tool和MCP带来的能力飞跃
- 你会亲眼看到Loop是怎么循环工作的

这些概念在实践中会自然变得清晰。别被名词吓到，AI真的没有想象中那么复杂！

动手试试吧，你会发现乐趣无穷

---

**参考资料**:
- [Anthropic MCP官方文档](https://modelcontextprotocol.io)
- [OpenAI Agent开发指南](https://platform.openai.com/docs)
- [LangChain框架文档](https://python.langchain.com)</content:encoded></item><item><title>OpenClaw核心技能安装指南</title><link>https://blog.yeyubaka.top/article/20260313</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260313/</guid><description>导读：OpenClaw在2026年彻底爆火，但很多新手安装后却发现它只是个&quot;会聊天的终端&quot;。原因很简单：你没装对Skills。面对ClawHub上超过13,000个技能插件，盲目安装不仅卡顿，还可能遭遇安全风险。本文基于GitHub高星项目及社区实测数据，为你筛选出最适合新手的20个核心Skills</description><pubDate>Fri, 13 Mar 2026 03:22:40 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
本篇文章是一份针对OpenClaw新手的核心技能安装指南，旨在解决新手安装后功能单一、性能卡顿和安全风险的问题。它基于社区实测和高星项目，从超过13,000个技能插件中筛选出最适合新手的20个&quot;真神&quot;Skills，并提供了详细的一键安装命令
:::

**参考资料**:

- [别乱装！2026年OpenClaw新手必装的20个&quot;真神&quot;Skills](https://mp.weixin.qq.com/s/YzMkLud3ITYAcetOCZ-hIw)
- [OpenClaw 完全部署指南：从入门到安全加固](https://zhuanlan.zhihu.com/p/2004187601276540473)
- [OpenClaw爆火，符合所有人利益的一场狂欢?](https://www.bilibili.com/video/BV1srwxzMEdn/)

::vhMusic{id=&quot;3338033128&quot;}

## 什么是OpenClaw？

如果你比较关注AI内容，那你一定最近被OpenClaw（龙虾🦞）刷屏了，甚至连央妈都在讨论，那到底什么是OpenClaw呢？简而言之是能够掌管你电脑的AI助手甚至是你的数字员工，而且你还能通过各种消息平台(WhatsApp、Telegram、Slack、Discord、Signal、iMessage、飞书、钉钉等)来直接指挥它。在GitHub上甚至只用了两个月就超越了React这一现代Web应用根基

&gt;2026 年 3 月 3 日，OpenClaw 正式超越 React，成为 GitHub 上 Star 数最多的软件项目，总 Star 数突破 25 万。React——这个驱动了现代 Web 大部分应用的 JavaScript 框架——花了十多年才达到这个里程碑。而 OpenClaw 只用了大约 60 天。
&gt;
&gt;这些数字令人震撼：250,829 个 Star、48,274 个 Fork、1,075 位贡献者，以及 9,574 个开放 Issue。仅在过去两周内，仓库就新增了约 8 万个 Star。

:::picture
![央妈报道OpenClaw](/images/posts/openclaw-guide/cctv-discussion-1.png)
![OpenClaw GitHub数据](/images/posts/openclaw-guide/github-stars.jpg)
![央妈报道OpenClaw](/images/posts/openclaw-guide/cctv-discussion-2.png)
![OpenClaw安全问题](/images/posts/openclaw-guide/security-issue.png)
:::

我也在第一时间在Ubuntu虚拟机上实装使用了一段时间，现在推荐几个提升效率和体验的Skills。

---

## 前置准备：如何安装Skill？

在开始之前，请确保你已经完成了OpenClaw的基础部署。安装技能非常简单，只需在终端运行以下通用命令：

```bash
# 通用安装命令
npx clawhub@latest install 

# 或者在OpenClaw对话中直接输入
/install 
```

:::note{type=&quot;warning&quot;}
**安全提示**：建议优先安装官方认证或GitHub高星（Star &gt; 1k）的技能，避免使用来源不明的插件，以防API Key泄露。
:::

---

## 第一梯队：基础生存包（必装前5个）

&gt;没有这些，OpenClaw只是一个普通的聊天机器人。

### 1. gog（Google Workspace全能管家）

- **官方链接**: [ClawHub/gog](https://clawhub.ai/steipete/gog)
- **安装命令**: `npx clawhub@latest install gog`
- **能干什么**:
  - 一句话管理Gmail、Google Calendar和Drive
- **实战**: &quot;帮我给老板发封邮件，附上上周的会议纪要，并预约明天下午3点的会议。&quot;
- **评价**: 安装量第一，配置最简单，职场人必备。

### 2. summarize（万物总结神器）

- **官方链接**: [ClawHub/summarize](https://clawhub.ai/steipete/summarize)
- **安装命令**: `npx clawhub@latest install summarize`
- **能干什么**:
  - 总结PDF、长网页、YouTube视频字幕、甚至整个代码库
- **实战**: &quot;总结这个PDF文件的核心观点，生成300字摘要。&quot;
- **评价**: 节省阅读时间的最强利器，准确率极高。

### 3. github（开发者第二大脑）

- **官方链接**: [ClawHub/github](clawhub.ai/steipete/github)
- **安装命令**: `npx clawhub@latest install github`
- **能干什么**:
  - 查Issue、提PR、管理仓库、自动Code Review
- **实战**: &quot;查看我仓库 my-project 中未关闭的Bug列表，并创建一个修复分支。&quot;
- **评价**: 程序员必备，让AI真正参与代码工作流。

### 4. file-manager（本地文件指挥官）

- **官方链接**: [ClawHub/file-manager](https://clawhub.ai/file-manager)
- **安装命令**: `npx clawhub@latest install file-manager`
- **能干什么**:
  - 读写、搜索、整理、重命名本地文件
- **实战**: &quot;把下载文件夹里所有的PDF移动到&apos;文档/2026报告&apos;目录，并按日期重命名。&quot;
- **评价**: 实现本地知识库交互的基础，注意配置好权限目录。

### 5. weather（零配置天气助手）

- **官方链接**: [ClawHub/weather](https://clawhub.ai/weather)
- **安装命令**: `npx clawhub@latest install weather`
- **能干什么**:
  - 无需API Key，快速查询全球天气，支持每日简报
- **实战**: &quot;北京下周会下雨吗？如果会，提醒我周三带伞。&quot;
- **评价**: 新手友好度满分，无需任何复杂配置即可使用。

---

## 第二梯队：办公与知识管理（效率倍增）

### 6. notion-connector

- **官方链接**: [ClawHub/notion](https://clawhub.ai/notion)
- **安装命令**: `npx clawhub@latest install notion`
- **能干什么**: 同步笔记、创建数据库条目、管理任务看板
- **实战**: &quot;在Notion的&apos;任务库&apos;中新建一条任务：完成Q1财报分析，截止日期下周五。&quot;

### 7. obsidian-sync

- **官方链接**: [ClawHub/obsidian](https://clawhub.ai/obsidian)
- **安装命令**: `npx clawhub@latest install obsidian`
- **能干什么**: 双向链接笔记管理，支持本地Markdown文件操作，隐私优先
- **实战**: &quot;在我今天的日记里记录刚才的会议要点，并链接到&apos;项目A&apos;笔记。&quot;

### 8. nano-pdf

- **官方链接**: [ClawHub/nano-pdf](https://clawhub.ai/nano-pdf)
- **安装命令**: `npx clawhub@latest install nano-pdf`
- **能干什么**: 轻量级PDF编辑、转换、提取文字、合并拆分
- **实战**: &quot;把这个Word文档转成PDF，并提取第5页的表格数据。&quot;

### 9. calendar-scheduler

- **官方链接**: [ClawHub/scheduler](https://clawhub.ai/scheduler)
- **安装命令**: `npx clawhub@latest install calendar-scheduler`
- **能干什么**: 跨平台日程冲突检测与自动预约（支持Outlook/本地日历）
- **实战**: &quot;帮我找一个下周一到周三大家都空闲的1小时开会时间。&quot;

### 10. rss-reader

- **官方链接**: [ClawHub/rss](https://clawhub.ai/rss)
- **安装命令**: `npx clawhub@latest install rss`
- **能干什么**: 订阅博客、新闻源，定时推送摘要
- **实战**: &quot;订阅Hacker News和少数派，每天早上8点推送最新热点摘要。&quot;

---

## 第三梯队：开发与极客工具（进阶必备）

### 11. code-executor

- **官方链接**: [ClawHub/code-executor](https://clawhub.ai/code-executor)
- **安装命令**: `npx clawhub@latest install code-executor`
- **能干什么**: 在沙箱环境中运行Python/Node.js代码，处理数据计算、绘图
- **实战**: &quot;用Python画一张过去五年比特币价格走势的折线图。&quot;

### 12. docker-controller

- **官方链接**: [ClawHub/docker](https://clawhub.ai/docker)
- **安装命令**: `npx clawhub@latest install docker`
- **能干什么**: 管理本地Docker容器（启停、日志查看、镜像构建）
- **实战**: &quot;重启名为&apos;web-server&apos;的容器，并显示最近100行日志。&quot;

### 13. ssh-connector

- **官方链接**: [ClawHub/ssh](https://clawhub.ai/ssh)
- **安装命令**: `npx clawhub@latest install ssh`
- **能干什么**: 安全连接远程Linux服务器，进行远程巡检和部署
- **实战**: &quot;连接到生产服务器，检查CPU使用率，如果超过80%就重启服务。&quot;
- **注意**: 务必配置SSH密钥白名单，限制可执行的命令。

### 14. api-tester

- **官方链接**: [ClawHub/api-tester](https://clawhub.ai/api-tester)
- **安装命令**: `npx clawhub@latest install api-tester`
- **能干什么**: 发送HTTP请求，测试API接口连通性，替代Postman
- **实战**: &quot;向 https://api.example.com/users 发送GET请求，返回状态码和响应体。&quot;

### 15. sql-explorer

- **官方链接**: [ClawHub/sql](https://clawhub.ai/sql)
- **安装命令**: `npx clawhub@latest install sql`
- **能干什么**: 连接数据库执行查询、生成报表（建议只读权限）
- **实战**: &quot;查询上个月销售额最高的前10个产品，并生成CSV文件。&quot;

---

## 第四梯队：生活与媒体创作（让AI更有趣）

### 16. media-downloader

- **官方链接**: [ClawHub/media-downloader](https://clawhub.ai/media-downloader)
- **安装命令**: `npx clawhub@latest install media-downloader`
- **能干什么**: 下载YouTube视频、B站视频、Spotify歌单（注意版权）
- **实战**: &quot;下载这个B站视频的音频部分，保存到&apos;音乐&apos;文件夹。&quot;

### 17. image-gen

- **官方链接**: [ClawHub/image-gen](https://clawhub.ai/image-gen)
- **安装命令**: `npx clawhub@latest install image-gen`
- **能干什么**: 调用Stable Diffusion或DALL-E 3生成图片
- **实战**: &quot;生成一张&apos;2077年赛博朋克风格的上海&apos;图片，比例16:9。&quot;

### 18. expense-tracker

- **官方链接**: [ClawHub/expense](https://clawhub.ai/expense)
- **安装命令**: `npx clawhub@latest install expense-tracker`
- **能干什么**: 记录日常开销，生成消费图表，数据存在本地
- **实战**: &quot;记录今天午餐花费35元，类别&apos;餐饮&apos;，并显示本月餐饮总支出。&quot;

### 19. news-digest

- **官方链接**: [ClawHub/news](https://clawhub.ai/news)
- **安装命令**: `npx clawhub@latest install news-digest`
- **能干什么**: 每日定时推送定制领域的新闻摘要（科技、财经等）
- **实战**: &quot;每天早上9点推送关于&apos;人工智能&apos;领域的最新三条新闻。&quot;

### 20. translate-pro

- **官方链接**: [ClawHub/translate](https://clawhub.ai/translate)
- **安装命令**: `npx clawhub@latest install translate-pro`
- **能干什么**: 多语言互译，支持文档、网页、对话实时翻译
- **实战**: &quot;把这份英文合同翻译成中文，并保持原有的格式。&quot;

---

## 新手避坑与建议

### 1. 不要一次性全装

建议先装**第一梯队**的5个，稳定运行一周后，根据实际需求按需安装。一次性安装过多会导致上下文过长，响应变慢，甚至引发逻辑冲突。

### 2. 权限最小化原则

涉及文件读写（`file-manager`）和网络操作（`ssh-connector`）的技能，务必在 `openclaw.json` 配置文件中限制其访问目录和白名单IP。

```json
{
  &quot;skills&quot;: {
    &quot;file-manager&quot;: {
      &quot;allowedPaths&quot;: [&quot;/home/user/documents&quot;],
      &quot;denyPaths&quot;: [&quot;/etc&quot;, &quot;/root&quot;]
    },
    &quot;ssh-connector&quot;: {
      &quot;allowedHosts&quot;: [&quot;192.168.1.100&quot;, &quot;myserver.com&quot;]
    }
  }
}
```

### 3. 关注Token消耗

定期查看日志，观察哪些技能最耗Token。像 `search` 类技能，建议在提示词中限制搜索次数（如&quot;最多搜索3次&quot;）。

### 4. 善用 find-skills

如果你找不到特定功能的技能，可以安装 `find-skills` 插件，直接问OpenClaw：&quot;我想做一个XXX功能，有什么推荐的技能吗？&quot;它会帮你检索ClawHub。

---

## 总结

OpenClaw的强大不在于模型本身，而在于这些**Skills赋予它的&quot;手脚&quot;**。这20个技能是经过社区数万次验证的&quot;黄金组合&quot;，能帮助你从零搭建一个真正能干活的数字员工。

我的建议是：先从**第一梯队的5个基础技能**开始，熟悉OpenClaw的工作方式后，再根据自己的实际需求逐步添加其他技能。不要贪多，够用就好。

如果你已经安装了一些好用的OpenClaw Skills，或者有自己开发的独家技能，欢迎在评论区分享你的配置清单！

---

**相关链接**:
- [OpenClaw 官方文档](https://docs.openclaw.ai/)
- [ClawHub 技能市场](https://clawhub.ai/)
- [OpenClaw GitHub](https://github.com/openclaw/openclaw)</content:encoded></item><item><title>Git入门指南：10分钟从零上手</title><link>https://blog.yeyubaka.top/article/20260222</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260222/</guid><description>Git 太难？10分钟就够了！本指南专为零基础新手设计，跳过复杂理论，直击核心命令，让你快速掌握版本控制精髓。</description><pubDate>Sun, 22 Feb 2026 00:58:56 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
从安装配置到提交推送，本文用最简路径讲解 Git 必备操作。无需编程背景，跟着步骤实操，轻松开启高效协作之旅，适合想快速上手的开发者。
:::

**参考资料**:
- [Git Bash详细教程](https://blog.csdn.net/qq_36667170/article/details/79085301)
- [Git中文文档](https://git-scm.cn/doc)

## Git 超极简入门指南：10分钟从零到上手

### 一、了解Git

#### 1. 什么是 Git？

简单来说，Git 是一个**分布式版本控制系统**。

- 版本控制：能记录文件的每一次改动。写错了？没关系，一键回退到上一个的版本。

- 分布式：每个人的电脑上都有完整的版本库，断网也能工作，同时保证了多个成员的并行作业。

#### 2.为什么需要Git？

| 场景 | 没有 Git | 有 Git |
| :------------: | :------------: | :------------: |
| 代码写错 | 手动备份，容易丢失 | 一键回退到任意版本 |
| 多人协作 | 文件覆盖，冲突频发 | 分支管理，合并审查 |
| 追溯历史 | 靠记忆或文档 | 完整的提交记录 |

### 二、 下载安装与配置
#### 1. 访问Git官网下载对应安装包
[点这里 选择对应系统下载](https://git-scm.cn/install/ &quot;点这里 选择对应系统下载&quot;)

这里我选择我系统(Win11 x64)对应版本

[![](/images/posts/git-guide/git-installer.png)](/images/posts/git-guide/git-installer.png)

安装完成后，打开终端（Windows 下是 Git Bash，Mac/Linux 是 Terminal）
#### 2. 初始配置
```bash
git config --global user.name &quot;你的名字&quot;
git config --global user.email &quot;你的邮箱@example.com&quot;
```
如下图一样list中能看到配置的用户名和邮箱就代表配置成功了！

[![](/images/posts/git-guide/git-config.png)](/images/posts/git-guide/git-config.png)

&gt;这样Git就知道&quot;是谁提交了代码&quot;

### 三、Git的流程
&gt;在了解对代码操作的命令前我们需要先知道代码是怎么从本地设备上到远程存储代码的设备(远程仓库)上的

```
工作区 (Workspace)				你实际修改文件的地方
    ↓ git add
暂存区 (Index/Stage)			确认改动的代码暂存处
    ↓ git commit
本地仓库 (Local Repository)		项目的所有版本历史记录
    ↓ git push
远程仓库 (Remote)			云端保存的项目版本（如 GitHub/Gitee）

```
### 四、常用命令实操
&gt;这里我就以上传我博客的md文章为例
#### 1. 创建仓库

```bash
# 方式一：从零开始
git init #初始仓库时无论本地文件夹内是否为空都可以

# 方式二：克隆现有项目
git clone https://github.com/用户名/项目名.git
```
在博客文章文件夹内右键`Open Git Bash here`
执行`git init`后会在文件夹内生成一个.git隐藏文件夹 ***千万不能删这个文件夹***

[![](/images/posts/git-guide/git-hidden-folder.png)](/images/posts/git-guide/git-hidden-folder.png)

#### 2.查看状态

```bash
git status
```
检查哪些文件被修改、哪些需要添加至版本管理

#### 3.添加文件到暂存区

```bash
git add 文件名      # 添加指定文件
git add .          # 添加所有文件
```
使用`git add .`前，建议配置`.gitignore`文件过滤依赖包、编译文件等

#### 4.提交到本地仓库
```bash
git commit -m &quot;feat: 添加用户登录功能&quot;
```
```
type(必须): 功能类别
  - feat：新功能
  - fix：修复bug
  - test：增加测试
  - revert：回滚版本

subject(必须): 简短描述，不超过50字符，中文更佳
```

#### 5.远程同步
##### 1.在GitHub上新建一个仓库

[![](/images/posts/git-guide/github-new-repo.png)](/images/posts/git-guide/github-new-repo.png)

##### 2.连接远程仓库
```bash
git remote add origin https://github.com/用户名/项目名.git
git remote add origin
```
##### 3.推送代码
```bash
git push -u origin main
```

[![](/images/posts/git-guide/git-push.jpg)](/images/posts/git-guide/git-push.jpg)

##### 4.拉取代码
```bash
git pull origin main
```
&gt;push 前最好先 pull，提前解决协作冲突

###### 5.分支管理
```bash
# 创建并切换分支
git checkout -b feature-login

# 查看分支
git branch

# 合并分支
git merge feature-login
```
&gt;合并前应在 GitHub/Gitee 创建 Pull Request(PR)，邀请审查后再合并

#### 6.避坑指南
##### 1. 版本回退
```bash
# 安全回退（修改保留在暂存区）
git reset --soft HEAD~1

# 彻底回退（丢弃未提交修改）
git reset --hard HEAD~1
```
##### 2.查看提交历史
```bash
git log
```
## 总结

多敲多练，熟能生巧！ 掌握以上内容，就可以进行简单的团队协作了

---</content:encoded></item><item><title>vhAstro-Theme 使用手册</title><link>https://blog.yeyubaka.top/article/20260209</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260209/</guid><description>vhAstro-Theme 主题完整使用手册，包含所有 Markdown 格式和特殊组件的使用方法</description><pubDate>Sun, 08 Feb 2026 16:00:00 GMT</pubDate><content:encoded>## 前言

欢迎使用 vhAstro-Theme！这是一款基于 Astro 开发的优雅博客主题，具有简洁的设计、流畅的动画和丰富的功能。本手册将详细介绍主题支持的所有 Markdown 格式和特殊组件的使用方法，帮助你快速上手并充分利用主题的各项特性。

**主题特点：**
- ✅ 基于 Astro v5.13.10，性能卓越
- ✅ 支持丰富的 Markdown 扩展语法
- ✅ 内置多种特殊组件（音乐、视频、相册等）
- ✅ 代码语法高亮（Shiki）
- ✅ 数学公式支持（KaTeX）
- ✅ 响应式设计，完美适配移动端

**参考资源：**
- 原作者博客：[韩小韩博客](https://www.vvhan.com/)
- 主题教程：[vhAstro-Theme 使用文档](https://www.vvhan.com/article/astro-theme-vhastro-theme)
- GitHub 仓库：[vhAstro-Theme](https://github.com/uxiaohan/vhAstro-Theme)

---

## 一、基础 Markdown 语法

### 1.1 标题

使用 `#` 符号创建标题，支持 H1-H6 六级标题：

```markdown
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题
```

### 1.2 文本格式

```markdown
**粗体文本**
*斜体文本*
***粗斜体文本***
~~删除线文本~~
`行内代码`
```

**效果展示：**
- **粗体文本**
- *斜体文本*
- ***粗斜体文本***
- ~~删除线文本~~
- `行内代码`

### 1.3 列表

**无序列表：**

```markdown
- 列表项 1
- 列表项 2
  - 嵌套列表项 2.1
  - 嵌套列表项 2.2
- 列表项 3
```

**有序列表：**

```markdown
1. 第一项
2. 第二项
3. 第三项
```

### 1.4 引用块

```markdown
&gt; 这是一个引用块
&gt; 可以包含多行内容

&gt; 嵌套引用：
&gt;&gt; 这是嵌套的引用
&gt;&gt;&gt; 可以多层嵌套
```

**效果展示：**

&gt; 这是一个引用块
&gt; 可以包含多行内容

### 1.5 链接和图片

```markdown
[链接文字](https://example.com)
![图片描述](/images/example.jpg)
```

**注意：** 所有图片路径必须包含 `/blog/` 前缀！

### 1.6 分隔线

```markdown
---
或
***
```

---

### 1.7 表格

```markdown
| 列1 | 列2 | 列3 |
|-----|-----|-----|
| 内容1 | 内容2 | 内容3 |
| 内容4 | 内容5 | 内容6 |

| 左对齐 | 居中对齐 | 右对齐 |
|:-------|:--------:|-------:|
| 左 | 中 | 右 |
```

**效果展示：**

| 功能 | 支持 | 说明 |
|------|:----:|------|
| Markdown | ✅ | 完整支持 |
| 代码高亮 | ✅ | Shiki 引擎 |
| 数学公式 | ✅ | KaTeX 渲染 |

---

## 二、特殊提示框组件

vhAstro-Theme 支持多种类型的提示框，用于突出显示重要信息。

### 2.1 默认提示框

```markdown
:::note
这是一个默认提示框
:::
```

:::note
这是一个默认提示框，用于一般性提示信息。
:::

### 2.2 信息提示框（蓝色）

```markdown
:::note{type=&quot;info&quot;}
这是一个信息提示框
:::
```

:::note{type=&quot;info&quot;}
这是一个信息提示框，用于提供额外的信息说明。
:::

### 2.3 成功提示框（绿色）

```markdown
:::note{type=&quot;success&quot;}
这是一个成功提示框
:::
```

:::note{type=&quot;success&quot;}
这是一个成功提示框，用于显示操作成功或正面信息。
:::

### 2.4 警告提示框（黄色）

```markdown
:::note{type=&quot;warning&quot;}
这是一个警告提示框
:::
```

:::note{type=&quot;warning&quot;}
这是一个警告提示框，用于提醒用户注意潜在问题。
:::

### 2.5 错误提示框（红色）

```markdown
:::note{type=&quot;error&quot;}
这是一个错误提示框
:::
```

:::note{type=&quot;error&quot;}
这是一个错误提示框，用于显示错误信息或严重警告。
:::

### 2.6 重要提示框（紫色）

```markdown
:::note{type=&quot;import&quot;}
这是一个重要提示框
:::
```

:::note{type=&quot;import&quot;}
这是一个重要提示框，用于强调关键信息或重要说明。
:::

---

## 三、代码块和语法高亮

### 3.1 行内代码

使用单个反引号包裹代码：`` `code` ``

示例：这是一个 `console.log()` 函数调用。

### 3.2 代码块

使用三个反引号包裹代码块，并指定语言以启用语法高亮：

**JavaScript 示例：**

```javascript
// JavaScript 代码示例
function greet(name) {
  console.log(`Hello, ${name}!`);
}

greet(&quot;vhAstro-Theme&quot;);
```

**TypeScript 示例：**

```typescript
// TypeScript 代码示例
interface User {
  name: string;
  age: number;
}

const user: User = {
  name: &quot;YEYUbaka&quot;,
  age: 25
};
```

**Python 示例：**

```python
# Python 代码示例
def fibonacci(n):
    if n 

  
  vhAstro-Theme

  欢迎使用 vhAstro-Theme

```

**CSS 示例：**

```css
/* CSS 样式示例 */
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.title {
  font-size: 2rem;
  color: #333;
  font-weight: bold;
}
```

**JSON 示例：**

```json
{
  &quot;name&quot;: &quot;vhAstro-Theme&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;优雅的 Astro 博客主题&quot;,
  &quot;author&quot;: &quot;韩小韩&quot;,
  &quot;license&quot;: &quot;MIT&quot;
}
```

### 3.3 支持的语言

主题使用 Shiki 语有主流编程语言：

- JavaScript / TypeScript
- Python / Java / C++ / C# / Go / Rust
- HTML / CSS / SCSS / Less
- Markdown / JSON / YAML / TOML
- Bash / PowerShell / Shell
- SQL / GraphQL
- 以及更多...

### 3.4 代码块特性

- ✅ **自动复制按钮**：每个代码块都会自动添加复制按钮
- ✅ **语法高亮**：使用 `github-light` 主题
- ✅ **行号显示**：自动显示行号（可配置）

---

## 四、媒体组件

### 4.1 音乐播放器组件

vhAstro-Theme 支持嵌入网易云音乐播放器。

**单曲播放：**

```markdown
::vhMusic{id=&quot;3346844433&quot;}
```

**歌单播放：**

```markdown
::vhMusic{id=&quot;3187536304&quot; type=&quot;playlist&quot;}
```

**参数说明：**
- `id`：网易云音乐的歌曲或歌单 ID
- `type`：播放类型，`song`（单曲，默认）或 `playlist`（歌单）

**如何获取音乐 ID：**
1. 打开网易云音乐网页版
2. 找到想要分享的歌曲或歌单
3. 从 URL 中获取 ID，例如：
   - 歌曲：`https://music.163.com/#/song?id=3346844433` → ID 是 `3346844433`
   - 歌单：`https://music.163.com/#/playlist?id=3187536304` → ID 是 `3187536304`

**效果展示：**

**单曲播放**

::vhMusic{id=&quot;3346844433&quot;}

**歌单播放**

::vhMusic{id=&quot;3187536304&quot; type=&quot;playlist&quot;}

### 4.2 视频播放器组件

支持嵌入视频播放器，可播放 MP4、M3U8 等格式。

**语法格式：**

```markdown
::vhVideo{url=&quot;https://example.com/video.mp4&quot;}
```

**参数说明：**
- `url`：视频文件的 URL 地址

**注意事项：**

- 视频 URL 必须是可访问的公网地址
- 支持 MP4、WebM、M3U8 等格式
- 播放器会自动适配移动端

::vhVideo{url=&quot;https://vjs.zencdn.net/v/oceans.mp4&quot;}

### 4.3 实况照片组件

实况照片（Live Photo）是一种结合静态图片和短视频的媒体形式。

**竖向实况照片：**

```markdown
::vhLivePhoto{photo=&quot;/images/photo.jpg&quot; video=&quot;/images/video.mp4&quot; type=&quot;y&quot;}
```

**横向实况照片：**

```markdown
::vhLivePhoto{photo=&quot;/images/photo.jpg&quot; video=&quot;/images/video.mp4&quot; type=&quot;x&quot;}
```

**参数说明：**
- `photo`：静态图片的路径
- `video`：视频文件的路径
- `type`：方向类型，`x`（横向）或 `y`（竖向）

### 4.4 图片相册组件

创建图片相册，支持多图展示和点击放大。

**语法格式：**

```markdown
:::picture
![图片1](/images/pic1.jpg)
![图片2](/images/pic2.jpg)
![图片3](/images/pic3.jpg)
:::
```

**特性：**
- ✅ 自动网格布局

- ✅ 响应式设计

- ✅ 点击放大查看

- ✅ 懒加载优化

:::picture
![芙宁娜实况照片示例](/images/posts/vhastro-theme/furina.gif)
:::

---

## 五、按钮组件

创建美观的按钮链接。

**语法格式：**

```markdown
::btn[按钮文字]{link=&quot;https://example.com&quot;}
```

**效果展示：**

::btn[访问 GitHub]{link=&quot;https://github.com/uxiaohan/vhAstro-Theme&quot;}

::btn[返回首页]{link=&quot;/&quot;}

**特性：**
- ✅ 自动添加 `target=&quot;_blank&quot;` 属性
- ✅ 美观的悬停效果
- ✅ 响应式设计

---

## 六、数学公式

vhAstro-Theme 支持 LaTeX 数学公式，使用 KaTeX 渲染。

### 6.1 行内公式

使用单个 `$` 包裹公式：

```markdown
这是一个行内公式：$E=mc^2$
```

**效果展示：**

这是一个行内公式：$E=mc^2$

### 6.2 块级公式

使用双 `$$` 包裹公式：

```markdown
$$
\int_{a}^{b} f(x) dx = F(b) - F(a)
$$
```

**效果展示：**

$$
\int_{a}^{b} f(x) dx = F(b) - F(a)
$$

### 6.3 更多公式示例

**二次方程求根公式：**

$$
x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
$$

**矩阵表示：**

$$
\begin{bmatrix}
a &amp; b \\
c &amp; d
\end{bmatrix}
$$

**求和公式：**

$$
\sum_{i=1}^{n} i = \frac{n(n+1)}{2}
$$

**极限表示：**

$$
\lim_{x \to \infty} \frac{1}{x} = 0
$$

---

## 七、高级技巧

### 7.1 图片懒加载

主题自动为所有图片启用懒加载，优化页面加载速度。

**工作原理：**
- 图片初始显示占位图
- 滚动到可视区域时加载真实图片
- 自动添加 `loading=&quot;lazy&quot;` 属性

### 7.2 外链自动处理

所有外部链接自动添加以下属性：
- `target=&quot;_blank&quot;`：在新标签页打开
- `rel=&quot;noopener nofollow&quot;`：安全和 SEO 优化

### 7.3 代码块复制功能

每个代码块自动添加复制按钮：
- 点击按钮一键复制代码
- 复制成功后显示提示
- 支持所有代码语言

### 7.4 图片点击放大
片支持点击放大查看：
- 点击图片进入全屏模式
- 支持缩放和拖拽
- ESC 键退出全屏

### 7.5 文章字数和阅读时间

主题自动计算文章字数和预估阅读时间：
- 基于文章内容自动统计
- 显示在文章头部
- 帮助读者评估阅读时长

---

## 八、Front Matter 配置说明

每篇文章的头部需要配置 Front Matter，用于设置文章的元信息。

### 8.1 完整配置示例

```yaml
---
title: &quot;文章标题&quot;
date: 2026-02-09T00:00:00+08:00
categories: &quot;技术分享&quot;
id: &quot;20260209&quot;
cover: /images/posts/cover.jpg
tags:
  - 标签1
  - 标签2
  - 标签3
recommend: true
top: true
hide: false
author: &quot;作者名称&quot;
description: &quot;文章描述，用于 SEO 和摘要显示&quot;
showToc: true
---
```

### 8.2 配置项说明

| 配置项 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `title` | String | ✅ | 文章标题 |
| `date` | DateTime | ✅ | 发布日期（ISO 8601 格式） |
| `categories` | String | ✅ | 文章分类 |
| `id` | String | ✅ | 文章唯一 ID（格式：YYYYMMDD） |
| `cover` | String | ❌ | 封面图片路径（必须包含 `/blog/` 前缀） |
| `tags` | Array | ❌ | 标签数组 |
| `recommend` | Boolean | ❌ | 是否推荐（默认 false） |
| `top` | Boolean | ❌ | 是否置顶（默认 false） |
| `hide` | Boolean | ❌ | 是否隐藏（默认 false） |
| `author` | String | ❌ | 作者名称 |
| `description` | String | ❌ | 文章描述 |
| `showToc` | Boolean | ❌ | 是否显示目录（默认 true） |

### 8.3 日期格式说明

日期必须使用 ISO 8601 格式：

```yaml
date: 2026-02-09T00:00:00+08:00
```

26-02-09`：年-月-日
- `T`：日期和时间的分隔符
- `00:00:00`：时:分:秒
- `+08:00`：时区（东八区）

### 8.4 文章 ID 规范

文章 ID 使用日期格式（YYYYMMDD）：

```yaml
id: &quot;20260209&quot;
```

**注意事项：**
- 必须是 8 位数字
- 格式：年（4位）+ 月（2位）+ 日（2位）
- 确保唯一性，不与现有文章冲突

---

## 九、写作最佳实践

### 9.1 文章结构建议

一篇优秀的博客文章应包含：

1. **前言**：简要介绍文章主题和背景
2. **目录**：使用 `showToc: true` 自动生成
3. **正文**：分段清晰，使用标题组织内容
4. **代码示例**：提供完整可运行的代码
5. **效果展示**：使用截图或实际效果
6. **总结**：概括要点和心得体会
7. **参考资源**：列出相关链接和资料

### 9.2 图片使用建议

- ✅ 使用 WebP 格式以优化加载速度
- ✅ 图片宽度建议 1200px 以内
- ✅ 封面图片建议尺寸：1200x630px
- ✅ 所有图片路径必须包含 `/blog/` 前缀
- ✅ 为图片添加有意义的 alt 文本

### 9.3 代码示例建议

-启用语法高亮
- ✅ 添加注释说明关键代码
- ✅ 提供完整可运行的示例
- ✅ 避免过长的代码块（超过 50 行考虑分段）

### 9.4 SEO 优化建议

- ✅ 填写准确的 `description`
- ✅ 使用相关的 `tags`
- ✅ 标题简洁明了（50 字以内）
- ✅ 使用合适的标题层级（H1-H6）
- ✅ 添加内部链接和外部参考

---

## 十、常见问题

### 10.1 图片不显示？

**可能原因：**
1. 图片路径缺少 `/blog/` 前缀
2. 图片文件不存在
3. 图片路径大小写错误

**解决方案：**
```markdown

![图片](/images/example.jpg)

![图片](/images/example.jpg)
```

### 10.2 代码块没有语法高亮？

**可能原因：**
- 没有指定代码语言

**解决方案：**
````markdown

```
console.log(&quot;Hello&quot;);
```

```javascript
console.log(&quot;Hello&quot;);
```
````

### 10.3 数学公式不显示？

**可能原因：**
- 使用了错误的分隔符
- LaTeX 语法错误

**解决方案：**
```markdown

\(E=mc^2\)

$E=mc^2$
```

### 10.4 音乐播放器不工作？

**可能原因：**
1. 音乐 ID 错误
2. 网易云音乐 API 配置问题
3. 网络连接问题

**解决方案：**
- 检查音乐 ID 是否正确
- 确认 `config.ts` 中配置了 `vhMusicApi`
- 尝试使用其他歌曲 ID

---

## 十一、参考资源

### 11.1 官方资源

- **Astro 官方文档**：[https://docs.astro.build/](https://docs.astro.build/)
- **vhAstro-Theme GitHub**：[https://github.com/uxiaohan/vhAstro-Theme](https://github.com/uxiaohan/vhAstro-Theme)
- **原作者博客**：[https://www.vvhan.com/](https://www.vvhan.com/)

### 11.2 Markdown 资源

- **Markdown 指南**：[https://www.markdownguide.org/](https://www.markdownguide.org/)
- **Markdown 速查表**：[https://www.markdownguide.org/cheat-sheet/](https://www.markdownguide.org/cheat-sheet/)

### 11.3 数学公式资源

- **KaTeX 文档**：[https://katex.org/docs/supported.html](https://katex.org/docs/supported.html)
- **LaTeX 数学符号**：[https://oeis.org/wiki/List_of_LaTeX_mathematical_symbols](https://oeis.org/wiki/List_of_LaTeX_mathematical_symbols)

### 11.4 其他资源

- **Shiki 语法高亮**：[https://shiki.matsu.io/](https://shiki.matsu.io/)
- **网易云音乐 API**：[https://neteasecloudmusicapi.vercel.app/](https://neteasecloudmusicapi.vercel.app/)

---

## 结语

恭喜你！现在你已经掌握了 vhAstro-Theme 的所有使用方法。这份手册涵盖了从基础 Markdown 语法到高级组件的完整内容，希望能帮助你创作出优秀的博客文章。

**写作建议：**
- 📝 保持文章结构清晰，使用标题组织内容
- 🎨 合理使用提示框和组件，增强文章可读性
- 💻 提供完整的代码示例，方便读者学习
- 🖼️ 使用图片和媒体丰富文章内容
- 🔍 注意 SEO 优化，提高文章曝光度

如果在使用过程中遇到问题，欢迎通过以下方式联系：
- 📧 邮件：[你的邮箱]
- 🐙 GitHub：[你的 GitHub]
- 💬 留言：在本文下方评论区留言

祝你写作愉快！✨

---

**相关链接：**
- [返回首页](/) 
- [所有文章](/archives/)
- [关于我](/about/)</content:encoded></item><item><title>Win11 SSH 免密登录配置指南</title><link>https://blog.yeyubaka.top/article/20260207</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20260207/</guid><description>如何配置 SSH 密钥认证实现免密登录</description><pubDate>Sat, 07 Feb 2026 13:38:00 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
最近在弄博客部署到Linux服务器时每次 SSH 登录都要输入密码很麻烦
本文教你如何配置 SSH 密钥认证，实现免密登录
:::

## 生成 SSH 密钥
```bash
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N &quot;&quot;
```
参数解析：
`-t rsa` :指定密钥类型为 RSA。这是目前最常用、兼容性最好的算法
`-b 4096`：指定密钥长度为 4096 位。数字越大越安全（默认是 2048，但我们直接拉满，安全感拉满！）。
`-f ~/.ssh/id_rsa`：指定生成的私钥文件保存路径和名字。这里会生成两个文件：
`~/.ssh/id_rsa`（私钥，千万别泄露！）
`~/.ssh/id_rsa.pub`（公钥，可以随便给）
`-N &quot;&quot;`：设置私钥的密码（passphrase）为空。

&gt;如果你设了密码，每次用私钥时还得再输一次密码——那就违背“免密”初衷了！所以这里留空，直接回车也行。

## 上传公钥到服务器
```bash
ssh-copy-id -i ~/.ssh/id_rsa.pub root@your-server-ip
```

`ssh-copy-id`：专门将密钥添加到服务器的命令

`-i ~/.ssh/id_rsa.pub`：上传的**公钥文件路径**

`root@your-server-ip`：目标服务器的登录登录用户和服务器IP或者域名

## 测试登录
```bash
ssh root@your-server echo &apos;SSH 免密登录测试成功！&apos; &amp;&amp; whoami &amp;&amp; hostname
```
如果看到类似输出*SSH 免密登录测试成功！*和账户名称主机名称即代表SSH免密登录设置成功！

```bash
SSH 免密登录测试成功！
root
my-awesome-server
```

## 提示！！！
- 权限很重要！
服务器上的 ~/.ssh 目录权限必须是 700，~/.ssh/authorized_keys 文件权限必须是 600。否则 SSH 出于安全考虑会直接拒绝使用密钥！
```bash
# （如果遇到权限问题）就在服务器上执行
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```

- 注意私钥不要泄露！（私钥==密码）
把你的 id_rsa（私钥）文件安全地拷贝到新电脑的 ~/.ssh/ 目录下即可。但千万注意：私钥 = 密码，不要上传到 GitHub！

- 连接多台服务器？
  你可以把同一个公钥发给多台你需要连接的服务器上

  使用 `ssh-copy-id` 多跑几次就行，或者手动复制 `id_rsa.pub` 内容追加到各服务器的 `authorized_keys` 里

**参考资料**：

- [SSH的免密登录详细步骤（注释+命令+图）](https://blog.csdn.net/SXY16044314/article/details/90605069)</content:encoded></item><item><title>个人博客网站搭建日记(二)</title><link>https://blog.yeyubaka.top/article/20251123</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20251123/</guid><description>总结了一下最近几天个人博客开发中碰见的问题，以及解决方案</description><pubDate>Sun, 23 Nov 2025 13:56:13 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
本文档总结了最近几天在开发个人主页和博客系统过程中遇到的主要问题及解决方案。
:::

## 开发问题总结

### 一、博客文章显示问题

#### 问题描述
博客文章创建后无法在网站上显示。

#### 原因分析
文章 Front Matter 中的 date 字段设置为未来日期（如 2025-11-19），Hugo 默认不会发布未来日期的文章。

#### 解决方案
将文章日期修改为当前或过去的日期：

date: 2025-01-19T20:00:00+08:00
#### 经验教训
Hugo 默认不发布未来日期的文章，这是为了支持定时发布功能
创建文章时务必检查日期设置
如需定时发布，应使用 Hugo 的 publishDate 字段

---

### 二、主页布局问题

#### 问题描述
1. 音乐播放器歌词遮盖了备案号
2. 备案号显示在右下角，未居中
3. 备案号文字颜色太暗，看不清
4. 备案号和 PowerBy 重叠在同一行
#### 原因分析
CSS 定位和层级问题：
- 音乐播放器使用 position: fixed 且 z-index 较高
- 备案号区域未正确设置定位和层级
- 文字颜色对比度不足
#### 解决方案
多次迭代调整 CSS：

1. 调整音乐播放器位置：

   ```
   #aplayer.aplayer-fixed {
       position: fixed;
       bottom: 110px; /* 为备案号留出空间 */
       z-index: 10000;
   }
   ```

   

2. 备案号区域居中：

   ```
   .remark {
       left: 50%;
       transform: translateX(-50%);
       width: min(90%, 560px);
       text-align: center;
   }
   ```

   

3. 使用块级元素确保换行：

   ```
   备案号内容
   PowerBy 内容
   ```

   

4. 调整文字颜色和样式：

   ```
   .remark-line {
       color: rgba(255, 255, 255, 0.9);
       font-weight: 500;
       letter-spacing: 0.5px;
   }
   ```

   
####经验教训
- 固定定位元素需要考虑层级关系
- 居中布局使用**left: 50% + transform: translateX(-50%) **更可靠
- 文字颜色需要考虑背景对比度
- 多次迭代调试是正常的开发过程

---
### 三、IIS 部署和 SSL 证书问题

#### 问题描述

1. HTTPS 访问出现 `ERR_CONNECTION_RESET` 错误
2. 证书更新后问题依然存在
3. 本地测试正常，但公网访问失败

#### 排查过程

1. 检查证书是否包含两个域名（`yeyubaka.top` 和 `www.yeyubaka.top`）- 确认正常
2. 检查端口 443 是否监听 - `netstat -ano | findstr :443` 显示正常
3. 使用 `Test-NetConnection` 测试外部连接 - 连接正常
4. 使用 `curl` 测试 - 本地可以返回 HTML

#### 最终决策

由于 IIS 配置复杂且问题难以定位，决定切换到 Caddy 服务器。

#### 经验教训

- IIS 的 SSL 配置相对复杂
- 网络问题可能涉及多个层面（防火墙、路由、证书链等）
- 有时切换技术栈是更高效的选择

---

### 四、Caddy 服务器部署

### 问题描述

切换到 Caddy 后，博客页面可以访问，但出现以下问题：

1. 博客 CSS 和 JS 文件 404
2. 静态资源路径不正确

### 原因分析

Caddy 的路由配置问题：

- 使用 `handle /blog*` 时，Caddy 会在 `C:/web/home/blog/blog/` 查找文件
- 需要使用 `handle_path` 自动去掉 `/blog` 前缀

### 解决方案

修改 Caddyfile 配置：

```
# 使用 handle_path 自动去掉 /blog 前缀
handle_path /blog* {
    root * C:/web/home/blog
    file_server
}
```

### 经验教训

- `handle` 和 `handle_path` 的区别很重要
- `handle_path` 会自动去掉匹配的前缀，适合子目录部署
- 路由顺序很重要，更具体的路由应该放在前面

---

### 五、评论系统部署问题

#### 问题 1：502 Bad Gateway

##### 问题描述

评论提交时出现 502 Bad Gateway 错误。

##### 原因分析

1. 评论服务器监听地址问题：默认监听 `0.0.0.0`，但 Caddy 连接 `localhost` 可能失败
2. Node.js 依赖未安装：服务器上缺少 `express` 模块

##### 解决方案

1. 修改监听地址：

   ```bash
   app.listen(PORT, &apos;127.0.0.1&apos;, () =&gt; {
       // 明确监听 127.0.0.1
   });
   ```

   

1. 安装依赖：

   ```bash
   cd C:\web\comments
   npm install express
   ```

   

#### 问题 2：SSL 协议错误

##### 问题描述

前端使用 HTTPS，但评论 API 使用 HTTP，导致混合内容错误。

##### 原因分析

- 主站使用 HTTPS：`https://yeyubaka.top`
- 评论 API 使用 HTTP：`http://localhost:3001`
- 浏览器阻止混合内容请求

##### 解决方案

配置 Caddy 反向代理，将 HTTPS 请求转发到 HTTP 后端：

```bash
handle /api/comments* {
    reverse_proxy localhost:3001
}
```

前端配置使用相对路径：

```bash
[params.comments]
  apiUrl = &quot;https://yeyubaka.top&quot;  # 不带端口
```

#### 经验教训

- 生产环境应该统一使用 HTTPS
- 反向代理是解决混合内容问题的标准方案
- 前端 API 地址应该使用相对路径或完整 HTTPS 地址

---

### 六、博客发布工具部署问题

#### 问题 1：PowerShell 脚本编码错误

##### 问题描述

运行 `start-publisher-server.ps1` 时出现字符串终止符错误。

##### 错误信息

```bash
字符串缺少终止符: &quot;。
语句块或类型定义中缺少右&quot;}&quot;。
```

##### 原因分析

PowerShell 脚本中的中文字符编码问题，Windows 服务器可能使用不同的编码格式。

##### 解决方案

将脚本改为英文版本，避免编码问题：

```bash
# 改为英文提示信息
Write-Host &quot;Press Ctrl+C to stop the server&quot; -ForegroundColor Yellow
```

#### 问题 2：Cannot GET /publisher/blog-publisher.html

##### 问题描述

访问发布工具页面时出现 404 错误。

##### 原因分析

1. 静态文件服务路径配置错误：Express 在 `C:\web\publisher\` 查找文件
2. Caddy 路由配置问题：使用 `handle` 而不是 `handle_path`

##### 解决方案

1. 修改 Express 静态文件路径：

   ```js
   const HOME_DIR = path.join(&apos;C:&apos;, &apos;web&apos;, &apos;home&apos;);
   app.use(express.static(HOME_DIR));
   ```

   

1. 修改 Caddyfile 使用 `handle_path`：

   ```yaml
   handle_path /publisher* {
       reverse_proxy localhost:3002
   }
   ```

   

#### 经验教训

- PowerShell 脚本在 Windows 服务器上可能有编码问题，使用英文更安全
- Express 静态文件服务需要正确配置根目录
- Caddy 的 `handle_path` 和 `handle` 区别要理解清楚

---

### 七、文件同步和部署流程

#### 问题描述

需要明确哪些文件需要上传到服务器，以及如何同步。

#### 解决方案

创建了多个同步脚本和文档：

1. **主页文件同步**：`sync-homepage.ps1`
   - 同步 `index.html`、`about.html`、`resume.html`、`resume.pdf`、`assets/` 等
2. **博客文件同步**：`sync-comments-local-build.ps1`
   - 本地构建 Hugo 博客
   - 同步构建后的 `public/` 目录到服务器
3. **一键部署**：`deploy-blog.ps1`
   - 自动构建并同步博客
4. **手动上传清单**：`MANUAL_UPLOAD_CHECKLIST.md`
   - 详细列出需要上传的文件

#### 经验教训

- 自动化脚本可以大大减少部署错误
- 文档化部署流程很重要
- 区分源文件和构建文件，只上传构建结果

---

### 八、IP 地理位置解析

#### 问题描述

评论系统需要记录评论者的 IP 地址并解析地理位置。

#### 解决方案

使用第三方 IP 地理位置 API：

1. 主要使用 `ip-api.com`（支持中文）
2. 备用使用 `ipapi.co`
3. 中国 IP 显示省份，其他国家显示国家

#### 实现要点

```javascript
async function getIpLocation(ip) {
    if (ip === &apos;::1&apos; || ip === &apos;127.0.0.1&apos;) {
        return &apos;本地&apos;;
    }
    
    // 尝试使用 ip-api.com
    const response = await fetch(`${IP_API_URL}${ip}?lang=zh-CN`);
    // 处理响应...
}
```

#### 经验教训

- 免费 API 可能有速率限制
- 需要处理 API 失败的情况
- 本地 IP 需要特殊处理

---

### 九、Caddy 配置优化

#### 问题描述

需要配置多个路由和反向代理，确保所有功能正常工作。

#### 最终配置结构

```yaml
yeyubaka.top, www.yeyubaka.top {
    # 1. 评论 API（最优先）
    handle /api/comments* {
        reverse_proxy localhost:3001
    }
    
    # 2. 发布工具（使用 handle_path）
    handle_path /publisher* {
        reverse_proxy localhost:3002
    }
    
    # 3. 博客（使用 handle_path）
    handle_path /blog* {
        root * C:/web/home/blog
        file_server
    }
    
    # 4. 主页和其他路径
    handle {
        root * C:/web/home
        file_server
    }
}
```

#### 经验教训

- 路由顺序很重要，更具体的路由应该在前
- `handle_path` 适合子目录部署
- 反向代理配置要明确监听地址

---

### 十、开发工作流程优化

#### 问题描述

需要建立清晰的开发、构建、部署流程。

#### 解决方案

建立了以下工作流程：

1. **本地开发**

   - 使用 `hugo server -D` 预览
   - 使用 `blog-publisher.html` 创建文章

2. **本地构建**

   ```bash
   cd blog
   hugo --minify --baseURL &quot;https://yeyubaka.top/blog/&quot;
   ```

   

3. **部署到服务器**

   ```bash
   .\deploy-blog.ps1  # 一键构建并部署
   ```

   

4. **服务器端服务**

   - 评论服务器：`C:\web\comments\`
   - 发布工具服务器：`C:\web\publisher\`
   - 网站文件：`C:\web\home\`

#### 经验教训

- 本地构建可以减少服务器依赖
- 自动化脚本提高效率
- 清晰的目录结构便于管理

---

### 十一、总结

#### 主要技术栈

- 前端：HTML/CSS/JavaScript
- 博客：Hugo + PaperMod 主题
- 服务器：Caddy
- 后端服务：Node.js + Express
- 部署：PowerShell 脚本

#### 关键决策

1. 从 IIS 切换到 Caddy（简化 SSL 配置）
2. 本地构建，服务器只部署静态文件
3. 使用反向代理解决 HTTPS/HTTP 混合问题
4. 创建自动化部署脚本

#### 最佳实践

1. 使用版本控制管理代码
2. 文档化部署流程
3. 自动化重复操作
4. 测试后再部署
5. 保留备份

#### 待改进点

1. 添加身份验证（发布工具和评论管理）
2. 优化图片加载和压缩
3. 添加文章编辑功能
4. 实现自动备份
5. 监控和日志系统

---

**参考资料**:
- [Hugo 官方文档](https://gohugo.io/)
- [Caddy 官方文档](https://caddyserver.com/docs/)
- [PaperMod 主题](https://github.com/adityatelange/hugo-PaperMod)
- [Express.js 文档](https://expressjs.com/)
- [主页模板](https://github.com/dmego/home.github.io)</content:encoded></item><item><title>个人博客网站搭建日记(一)</title><link>https://blog.yeyubaka.top/article/20251119</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20251119/</guid><description>从个人主页到完整博客网站的搭建过程</description><pubDate>Wed, 19 Nov 2025 00:00:00 GMT</pubDate><content:encoded>## 前言

一直想拥有一个属于自己的网站，既能展示个人作品，又能记录学习心得和技术思考。正好最近薅到了阿里云（爸爸）的2h2g服务器,经过几天的折腾，终于完成了从开源个人模板主页到完整博客网站的搭建。这篇文章记录下搭建过程，希望能给有同样需求的朋友一些参考。后续还会持续更新。

## 项目背景

最初的项目是一个简洁的开源个人主页[`home.github.io-1.0.7`](https://github.com/dmego/home.github.io)，基于静态 HTML 页面，展示个人简介和社交链接。随着内容越来越多，发现需要一个专门的博客系统来管理文章。

## 技术选型

### 静态网站生成器：Hugo

在和AI激情辩论下：经过对比 Jekyll、Hexo、Hugo 等主流静态网站生成器，最终选择了 **Hugo**，原因如下：

- ✅ **极快的构建速度**：Hugo 用 Go 语言编写，构建速度远超其他工具
- ✅ **丰富的主题生态**：有大量高质量主题可选
- ✅ **Markdown 原生支持**：写作体验友好
- ✅ **无需数据库**：纯静态文件，部署简单
- ✅ **活跃的社区**：文档完善，问题容易解决

### 主题选择：PaperMod

选择了 **PaperMod** 主题，这是一个基于 Hugo 的现代化主题，具有：

-  简洁美观的设计
-  内置夜间模式
-  完全响应式
-  内置搜索功能
-  支持文章统计
-  完善的分类和标签系统

## 搭建过程

### 1. 初始化 Hugo 项目

```bash
# 在现有项目目录下创建 blog 子目录
hugo new site blog
cd blog

# 添加 PaperMod 主题（使用 Git Submodule）
git init
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/paper
```

### 2. 配置基础设置

编辑 `config.toml`，配置网站基本信息：

```toml
baseURL = &apos;http://localhost:1313/blog/&apos;
languageCode = &apos;zh-cn&apos;
hasCJKLanguage = true
title = &quot;YEYU的blog&quot;
theme = &quot;paper&quot;

[author]
  name = &quot;YEYUbaka&quot;
```

### 3. 集成音乐播放器

在主页 `index.html` 中集成了 **APlayer** 和 **MetingJS**，实现网易云音乐播放列表：

```html

```

**遇到的问题**：
- 播放器被遮罩层遮挡 → 通过设置 `z-index: 9999` 解决
- 浏览器阻止自动播放 → 移除 `data-autoplay` 属性，改为用户手动播放

### 4. 添加天气组件

集成了**心知天气（Seniverse）** API，在博客导航栏下方显示实时天气：

```javascript
// 自定义天气组件
const config = {
    apiKey: &apos;apikey&apos;,
    location: &apos;wuhan&apos;,
    language: &apos;zh-Hans&apos;,
    unit: &apos;c&apos;
};
```

**优化过程**：
- 最初使用官方 iframe 插件，但无法自定义样式
- 改为使用 API 方式，实现完全自定义的天气卡片
- 添加了蓝灰渐变背景，适配明暗两种主题模式

### 5. 页面布局优化

#### 目录（TOC）右侧固定

通过自定义 CSS，将文章目录固定在页面右侧，方便阅读长文：

```css
.post-single .toc {
    position: fixed;
    top: 130px;
    right: 40px;
    width: 260px;
    max-height: calc(100vh - 180px);
    overflow-y: auto;
}
```

#### 返回顶部按钮

添加了自定义的返回顶部按钮，带有平滑滚动动画效果。

### 6. 备案号添加

根据国内网站要求，在所有页面底部添加了备案号：

```
© 2025 YEYU的blog · 鄂ICP备2025159185号-1 · Powered by Hugo &amp; PaperMod
```

### 7. 关于页面优化

创建了详细的关于页面（`about.md`），包括：
- 个人简介
- 技能栈分类展示
- 技术图标展示（使用 Flexbox 布局，支持响应式换行）
- GitHub 统计图表
- 联系方式

## 部署到 Windows 服务器

### 服务器环境

- **操作系统**：Windows Server
- **Web 服务器**：IIS 10
- **部署方式**：静态文件部署

### 部署步骤

1. **构建静态文件**
   ```bash
   cd blog
   hugo --minify
   ```
   生成的文件在 `public/` 目录

2. **配置 IIS**
   - 创建新网站，指向 `public` 目录
   - 设置默认文档为 `index.html`
   - 配置 URL 重写规则（处理 Hugo 的 clean URLs）

3. **自动化脚本**

   创建了 PowerShell 脚本 `start-iis.ps1`，实现一键启动网站：
   - 检查 IIS 服务状态
   - 检查应用程序池
   - 启动网站
   - 诊断绑定冲突

### 遇到的问题

1. **端口冲突**：默认网站占用 80 端口 → 停止默认网站或修改端口
2. **应用程序池未启动** → 脚本中添加自动启动逻辑
3. **绑定冲突** → 添加详细的诊断和修复功能

## 项目结构

```
home.github.io-1.0.7/
├── index.html              # 主页
├── 404.html                # 404 错误页
├── assets/                 # 静态资源
├── blog/                   # Hugo 博客
│   ├── content/
│   │   ├── posts/          # 文章目录
│   │   └── about.md        # 关于页面
│   ├── layouts/
│   │   └── partials/       # 自定义模板
│   ├── assets/
│   │   └── css/
│   │       └── extended/
│   │           └── custom.css  # 自定义样式
│   ├── static/
│   │   └── js/
│   │       └── weather.js  # 天气组件脚本
│   ├── themes/
│   │   └── paper/          # PaperMod 主题
│   ├── config.toml         # Hugo 配置
│   └── public/             # 构建输出目录
└── start-iis.ps1           # IIS 启动脚本
```

## 功能特性总结

### 已实现功能

- ✅ 完整的博客系统（文章、分类、标签、归档）
- ✅ 音乐播放器集成
- ✅ 实时天气显示
- ✅ 响应式设计，支持移动端
- ✅ 明暗主题切换
- ✅ 文章目录（TOC）右侧固定
- ✅ 返回顶部按钮
- ✅ 备案号显示
- ✅ SEO 优化
- ✅ 关于页面
- ✅ Windows 服务器部署

### 未来计划

- [ ] 添加评论系统（考虑 Giscus 或 Valine）
- [ ] 集成 Google Analytics
- [ ] 添加 RSS 订阅
- [ ] 优化搜索功能
- [ ] 添加友链页面
- [ ] 实现文章阅读量统计
- [ ] 添加文章分享功能

## 技术栈总结

- **静态网站生成器**：Hugo
- **主题**：PaperMod
- **前端**：HTML、CSS、JavaScript
- **音乐播放器**：APlayer + MetingJS
- **天气 API**：心知天气（Seniverse）
- **部署**：IIS（Windows Server）
- **自动化**：PowerShell

## 心得体会

1. **选择合适的工具很重要**：Hugo 的构建速度确实很快，大大提升了开发效率

2. **主题选择要慎重**：好的主题能节省大量开发时间，PaperMod 的设计和功能都很完善  

    **~~模板真好用嘻嘻😁~~**

3. **自定义样式要适度**(~~理科生噩梦~~)：在主题基础上进行适度自定义，既能满足需求，又不会过度复杂化

4. **部署要考虑实际环境**：Windows 服务器部署虽然不如 Linux (~~还是太卡了，技术不到位还不会Linux~~)，但通过脚本自动化也能很好地管理

5. **持续优化**：网站搭建不是一蹴而就的，需要根据实际使用情况不断优化***~~好事多磨!~~***

## 参考资源

- [Hugo 官方文档](https://gohugo.io/documentation/)
- [PaperMod 主题文档](https://github.com/adityatelange/hugo-PaperMod)
- [心知天气 API 文档](https://docs.seniverse.com/)
- [APlayer 文档](https://aplayer.js.org/)

## 结语

经过几天的折腾，终于完成了博客网站的搭建。虽然过程中遇到了不少问题，但通过查阅文档、搜索解决方案，最终都一一解决了。现在有了一个功能完善、设计美观的个人博客，可以开始记录和分享自己的技术学习和生活思考了。

如果你也在考虑搭建个人博客，希望这篇文章能给你一些帮助。如果有什么问题，欢迎通过邮件或 GitHub 联系我！

---

**相关链接：**
- [返回首页](/)
- [所有文章](/posts/)
- [关于我](/about/)</content:encoded></item><item><title>Win11下MySQL安装和Workbench汉化教程</title><link>https://blog.yeyubaka.top/article/20251129</link><guid isPermaLink="true">https://blog.yeyubaka.top/article/20251129/</guid><description>MySQL 8.0.44.0 安装配置、Workbench汉化教程</description><pubDate>Wed, 29 Jan 2025 12:10:08 GMT</pubDate><content:encoded>:::note{type=&quot;success&quot;}
最近弄的一个项目需要使用到MySQL，正好记录总结一下Win11的安装汉化工程
:::

# Windows 11 安装 MySQL 8.0.44.0 详细教程

适用版本：Windows 11
MySQL 版本：8.0.44.0（Community Server）
安装方式：MSI 安装程序（图形化界面）

---

### 一、准备工作

下载 MySQL 安装包

1. 打开 MySQL 官方下载页面

2. 选择 “MySQL Community (GPL) Downloads”

3. 在 “MySQL Community Server” 页面，点击 “Looking for previous GA versions?”

4. 在下拉菜单中选择 8.0.44（注意：截至 2025 年，8.0.44 可能已不是最新版）

5. 下载适用于 Windows 的 MSI 安装包（推荐 mysql-installer-community-8.0.44.0.msi）

   &gt; 备份: 通过网盘分享的文件：8.0.44.0MySQL安装包及汉化文件 链接:https://pan.baidu.com/s/1f8slnx_5KGXwhp7lE-IHrA?pwd=hwvw 提取码: hwvw
   &gt; 建议选择 MSI Installer（带图形界面），比 ZIP 压缩包更简单。

### 二、安装步骤

1. 运行安装程序

   - 双击下载好的 .msi 文件（如 mysql-installer-community-8.0.44.0.msi）
   - 若弹出用户账户控制（UAC）提示，点击 “是”
2. 选择安装类型
  在 Choosing a Setup Type 界面：

  - 推荐选择 “Developer Default”（开发者默认）——包含 MySQL Server + Workbench + Shell 等常用工具
  - 如果仅需数据库服务，可选 “Server only”
  - 点击 Next

3. 检查依赖项（Requirements）

   - 安装程序会自动检测系统是否缺少依赖（如 Visual C++ Redistributable）
   - 如有缺失，点击 Execute 自动下载并安装
   - 安装完成后点击 Next
4. 安装产品

   -  Execute 开始安装所选组件
   - 进度条完成（可能需要几分钟）
   - 后点击 Next
### 三、配置MySQL
1. 配置类型（Configuration Type）
	
	- 通常选择 “Development Computer”（开发用）
	
	- 点击 Next
	
2. 连接设置（Connectivity）
	
	- TCP/IP：默认启用（端口 3306）
	
	- 可勾选 “Open firewall port for network access”（如需远程连接）
	
	- 建议保留默认端口 3306
	
	- 点击 Next
	
3. 认证方式（Authentication Method）
	⚠️ **重要选择**！
	
	- 推荐选择 “Use Strong Password Encryption”（即 caching_sha2_password）
	  这是 MySQL 8.0 默认认证插件，兼容性更好
	- 不要选 “Use Legacy Authentication Method”，除非你有旧客户端必须兼容
	    点击 Next
	
4. 配置root密码
    - 强密码（至少 8 位，含大小写字母、数字、符号）
    - 必记住此密码！
    - 击 Add User 添加其他用户（可选）
    -  Next

5. Windows 服务配置

    - 名称默认为 MySQL80
    -  “Start the MySQL Server at System Startup”（开机自启）
    -  Next

6. 应用配置

    -  Execute 应用所有配置

    - 绿色对勾出现

    -  Finish
### 四、验证安装

方法 1：命令行验证
1. 按 Win + R，输入 cmd 打开命令提示符
2. 输入以下命令：
```bash
mysql -u root -p
```
3. 输入你设置的 root 密码
4. 成功进入 MySQL 命令行（出现 mysql&gt; 提示符）即表示安装成功
```bash
mysql&gt; SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 8.0.44    |
+-----------+
1 row in set (0.00 sec)
```

方法2：使用 MySQL Workbench（如已安装）
    - 打开 MySQL Workbench
    - 默认会有一个 Local instance MySQL80 连接
    - 双击并输入密码即可连接

### 五、MySQL Workbench
&gt;启动Workbench后，发现为纯英文，对很大一部分人来说并不友好，下面将进行汉化版教程（注意，以下教程只针对于Work bench版本8.0.39及以上的版本）

**汉化MySQL Workbench**

下载我网盘中的汉化文件将其中的**main_menu.xml**文件复制到**C:\Program Files\MySQL\MySQL Workbench 8.0\data**或者你自己的**MySQL Workbench**安装目录，替换即可。怕出错可以先将原来的文件先备份，以免重装
替换后重新打开**Work bench**
汉化完毕
当然，如何大家有其他不懂的，可以看看原文：

[MySQL8 Workbench 中文汉化详细教程（附汉化文件）](https://blog.csdn.net/zp8126/article/details/141359391)</content:encoded></item></channel></rss>