<!-- title: 从零构建依赖升级侦察 Skill -->

# 从零构建依赖升级侦察 Skill：Claude Code 自定义 Skill 开发实战

## 概述

本教程面向已能基本使用 Claude Code、希望掌握**自定义 Skill 开发**的开发者。你将从零构建一个名为 `bump-scout`（依赖升级侦察兵）的完整 Skill：给定一个待升级的依赖包，它能扫描出受影响的文件、判定 SemVer 破坏性变更风险、产出一份带风险评级与回滚策略的升级计划。通过这一个项目，你将掌握 `SKILL.md` 的 frontmatter 触发写法、三层渐进式披露、bundled 脚本与参考文件的组织方式、动态上下文注入、工具权限收敛，以及"模型自动触发 vs 用户手动调用"的取舍。适合 Claude Code 训练营 Day 2 上午的 3 小时实战环节。

## 前置条件

- 已安装 Claude Code（建议 v2.1.145 或以上，本教程用到 `allowed-tools`、动态上下文注入等特性）
- 本机有一个可执行 `git` 的真实项目（用于实战环节）
- 能看懂 YAML frontmatter 与基础 shell 脚本
- 理解语义化版本（SemVer：`主版本.次版本.修订号`）的基本概念

---

## 第一步：建立三层渐进式披露的心智模型

写 Skill 之前，先要理解它**为什么**长这样。Claude Code 的 Skill 遵循 **Agent Skills 开放规范**，其核心设计原则是**渐进式披露（Progressive Disclosure）**——按需加载，省 context。

把一个 Skill 想象成三层抽屉：

| 层级 | 内容 | 加载时机 | 大致成本 |
|------|------|----------|----------|
| 第一层 · 索引层 | `name` + `description` | 会话启动时**始终**扫描 | 约 100 tokens/skill |
| 第二层 · 主指令层 | `SKILL.md` 正文 | 触发匹配后**一次性**加载 | 通常 < 5000 tokens |
| 第三层 · 支撑层 | `scripts/`、`references/`、`templates/` | 正文里被**显式引用**且确实需要时才读 | 按需，可很大 |

关键洞察：**description 是触发器，不是文档**。会话里装 50 个 Skill，没被用到的那些只各占约 100 tokens，几乎免费。但一旦某个 Skill 的正文被加载，它会**常驻整个会话**，每一行都是反复发生的 token 成本。所以正文要短，重内容要下沉到第三层。

```mermaid
flowchart LR
    A[会话启动] -->|扫描所有 skill| B[第一层:索引\n约100 tokens/skill]
    B --> C{用户 prompt\n是否匹配 description?}
    C -->|否| D[不加载, 零成本]
    C -->|是| E[第二层:加载 SKILL.md 正文]
    E --> F{正文是否引用\nscripts/references?}
    F -->|需要| G[第三层:按需读取支撑文件]
    F -->|不需要| H[直接执行]
    G --> H
```

理解了这层"成本经济学"，`bump-scout` 的结构就自然浮现了：把"如何评估升级"的**判断逻辑**放进正文（短），把"扫描受影响文件"的**机械劳动**交给脚本，把"风险矩阵"这种**查表型知识**放进参考文件——只在真的要对号入座时才读。

## 第二步：划定 Skill 的职责边界（一个 Skill 只做一件事）

新手最常见的错误是把 Skill 写成"瑞士军刀"。`bump-scout` 只负责一件事：**评估一次依赖升级的影响并产出升级计划**。它不做的事要明确排除：

- ❌ 不直接执行 `npm install` / `go get`（只评估，不改包锁——执行交给用户）
- ❌ 不写迁移代码（那是另一个 Skill 的活）
- ❌ 不审查整个依赖树的健康度（那是 `audit` 类 Skill）

职责单一，`description` 才好写，触发才精准。Skill 与 MCP、Subagent 的边界也由此清晰：**Skill 封装"我们团队怎么做某件事"的过程知识**；MCP 封装"连接外部服务"的工具；Subagent 封装"隔离上下文的独立工作者"。依赖升级评估是过程知识，用 Skill 正合适。

## 第三步：创建目录与编写 SKILL.md

### 目录位置决定作用范围

Skill 存哪，决定了谁能用：

| 级别 | 路径 | 作用范围 |
|------|------|----------|
| 个人 | `~/.claude/skills/<name>/SKILL.md` | 你的所有项目 |
| 项目 | `.claude/skills/<name>/SKILL.md` | 仅当前项目 |
| 插件 | `<plugin>/skills/<name>/SKILL.md` | 启用该插件处 |
| 企业 | managed settings | 全组织 |

同名优先级：**企业 > 个人 > 项目**，任意级别都会覆盖同名 bundled skill。`bump-scout` 是通用工具，我们放到**个人级**，这样每个项目都能用：

```bash
mkdir -p ~/.claude/skills/bump-scout/{scripts,references,templates}
cd ~/.claude/skills/bump-scout
```

### 编写 SKILL.md：description 以触发开头

创建 `~/.claude/skills/bump-scout/SKILL.md`。先看最关键的前两块——frontmatter 和正文开头：

```markdown
---
name: bump-scout
description: Assesses the impact of upgrading a dependency and produces a risk-rated upgrade plan. Use when the user wants to bump/upgrade/update a package version, asks "is it safe to upgrade X", or plans a major version migration. Scans affected files, classifies SemVer breaking-change risk, and outputs a rollback-ready plan. Does NOT run installers or write migration code.
argument-hint: [package] [from-version] [to-version]
arguments: package from_version to_version
allowed-tools: Read Grep Glob Bash
---

## What this skill does

You are a dependency-upgrade impact assessor. Given a package and its target version,
produce a concise, risk-rated upgrade plan WITHOUT running any installer.

## Inputs

- Package: $package (from-version: $from_version -> to-version: $to_version)
- If versions are omitted, infer the current version from the lockfile/manifest below.

## Procedure

1. **Read the current dependency state** (already inlined in "Current state" below).
2. **Scan affected files** by running `scripts/affected.sh $package` — count and list
   import sites grouped by directory.
3. **Classify risk** using `references/risk-matrix.md`. A major-version bump is HIGH
   risk until proven otherwise.
4. **Fetch breaking changes**: read the package's CHANGELOG/release notes for the
   target major version; summarize the top 3 breaking changes. If offline, say so
   and mark risk conservatively.
5. **Produce the plan** by filling in `templates/upgrade-plan.md`.

## Output

Fill the template. Lead with a one-line risk verdict (LOW / MEDIUM / HIGH / BLOCKER).
Keep prose minimal — tables and bullet points over paragraphs.
```

**写 description 的四条铁律**（这是整个 Skill 成败的关键）：

1. **以触发场景开头**：第一句就是"What it does AND when to use it"。Claude 在每个回合都在读它，前几十个词决定生死。
2. **列出触发短语**：`bump/upgrade/update a package`、`is it safe to upgrade X`——把用户真实会说的话埋进去。
3. **写明边界**：`Does NOT run installers`——明确不做什么，避免误触发后产生危险操作。
4. **控制在 1536 字符内**：`description` + `when_to_use` 合并后**在索引列表里被截断到 1536 字符**，超出的部分 Claude 永远看不到。把最重要的放最前。

`arguments` 字段定义了三个命名位置参数，正文里就能用 `$package`、`$from_version`、`$to_version` 替换。用户敲 `/bump-scout react 18.2.0 19.0.0`，这些变量会被自动填入。`allowed-tools` 把这个 Skill 能用的工具**收敛**到四个——这是安全护栏，下文第七步详述。

### 预期结果

保存后，**无需重启**——Claude Code 实时监听 skill 目录变更，新 Skill 在当前会话立即生效。敲 `/` 触发自动补全，应能看到 `bump-scout [package] [from-version] [to-version]`。

## 第四步：把机械劳动下沉到 bundled 脚本

正文要保持精炼，"扫描受影响文件"这种重复机械活交给脚本。创建 `scripts/affected.sh`：

```bash
#!/usr/bin/env bash
# 扫描某依赖的所有引用点，按目录分组统计。
# 用法: affected.sh <package-name>
set -euo pipefail

pkg="${1:?usage: affected.sh <package-name>}"
# 取包名最后一段做宽匹配：@scope/react -> react
short="${pkg##*/}"

echo "## Affected files for \`$pkg\`"
echo

# 1. 源码 import/require/from
mapfile -t files < <(grep -rIlE \
  "(from|import|require)\s+['\"][^'\"]*${short}(@|/|['\"])" \
  --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' \
  --include='*.py' --include='*.go' -- . 2>/dev/null || true)

if [ "${#files[@]}" -eq 0 ]; then
  echo "- No direct imports found."
  exit 0
fi

# 2. 按顶层目录分组计数
echo "| Directory | Files |"
echo "|-----------|-------|"
printf '%s\n' "${files[@]}" | \
  awk -F/ '{print $2}' | sort | uniq -c | \
  awk '{printf "| %s | %d |\n", $2, $1}'

echo
echo "Total: ${#files[@]} files"
```

给它可执行权限：

```bash
chmod +x ~/.claude/skills/bump-scout/scripts/affected.sh
```

**为什么用脚本而不是让 Claude 自己 grep？** 三个理由：(1) 脚本是一次性、确定性的，避免 Claude 在大仓库里反复试错浪费 token；(2) 脚本输出的是结构化数据，Claude 直接填表即可；(3) 脚本可以独立测试——这是 Skill 工程化的基础。注意脚本要做"防御性编程"：`set -euo pipefail`、`|| true` 兜底 grep 无匹配时的非零退出码，避免 Claude 把脚本崩溃误判为"项目出问题"。

### 常见问题

- **脚本跑不起来**：90% 是 `chmod +x` 没做，或 shebang 写错。Skill 调用的是真实 shell，路径用绝对或相对仓库根。
- **Windows 上没 bash**：改写为 `scripts/affected.ps1`，正文里根据平台选择调用哪个。

## 第五步：把查表型知识放进参考文件

风险分级是"查表"而非"推理"——这种知识最适合放第三层参考文件。创建 `references/risk-matrix.md`：

```markdown
# Upgrade Risk Matrix

Classify the bump, then adjust based on findings.

| Version delta | Base risk | What to check |
|---------------|-----------|---------------|
| patch (x.y.**Z**) | LOW | Bug fixes; run test suite |
| minor (x.**Y**.z) | LOW–MED | New APIs/deprecations; check removed deprecations |
| major (**X**.y.z) | **HIGH** | Breaking API/behavior; peer-dep range; migration guide |
| cross-major (e.g. 17→19) | **HIGH+** | Multiple breaking layers; treat as BLOCKER until reviewed |

## Downgrade triggers (move risk one level UP)
- Package is a framework (react, next, vue, express) — ripples through app
- Used in > 50 files OR in critical path (auth, payment, routing)
- Current version is > 2 majors behind (accumulated breaking changes)
- No tests cover the package's usage

## Upgrade triggers (move risk one level DOWN)
- Patch-only with a published security advisory (upgrade is safer than waiting)
- Used in 1–2 trivial files with full test coverage

## Verdict mapping
- LOW: ship directly after tests pass
- MEDIUM: ship behind a feature flag or in a staging canary
- HIGH: staged rollout + rollback plan required (template section 5)
- BLOCKER: do not upgrade without a migration spike first
```

正文里只用一行引用它（`Classify risk using references/risk-matrix.md`），Claude 只在真要分级时才读这个文件。**这就是渐进式披露的精髓**：一个没被触发的 `bump-scout` 只花 100 tokens；被触发后正文也就几百 tokens；只有真的在评估一次 React 18→19 这种大版本升级时，风险矩阵才会被读进上下文。

## 第六步：用模板约束输出结构

模板让 Skill 的输出可预测、可 diff、可纳入团队规范。创建 `templates/upgrade-plan.md`：

```markdown
# Upgrade Plan: {{package}} {{from_version}} → {{to_version}}

**Risk verdict:** [LOW / MEDIUM / HIGH / BLOCKER]

## 1. Summary
- One line: what changes and why it matters.

## 2. Affected surface
| Directory | Files | Notes |
|-----------|-------|-------|

## 3. Top breaking changes (from CHANGELOG)
1. ...
2. ...
3. ...

## 4. Required code changes
- [ ] ...

## 5. Rollback plan
- Commit hash before upgrade: `<fill>`
- Rollback command: `git revert <hash>` or lockfile restore command
- Verify-rollback check: <which test/command proves rollback worked>

## 6. Verification
- [ ] `npm test` (or equivalent) green
- [ ] Manual smoke: <critical path>
```

**模板 ≠ 参考文件**：参考文件是"给 Claude 看的判断依据"，模板是"给 Claude 填的输出骨架"。两者职责不同，分开放，正文里分别引用。

至此，`bump-scout` 的完整目录结构是：

```
~/.claude/skills/bump-scout/
├── SKILL.md                      # 第二层：主指令（短）
├── scripts/
│   └── affected.sh               # 第三层：机械劳动
├── references/
│   └── risk-matrix.md            # 第三层：查表知识
└── templates/
    └── upgrade-plan.md           # 第三层：输出骨架
```

## 第七步：动态上下文注入——把当前状态内联进 prompt

依赖评估最怕 Claude "猜"。官方的动态上下文注入语法 `!`command`` 能在 Claude 看到 Skill 内容**之前**先跑命令、把输出替换进去。在 `SKILL.md` 末尾追加：

```markdown
## Current state

### Installed version & manifest
!`cat package.json 2>/dev/null | grep -A2 -B2 "$package" || grep -rI "$package" go.mod requirements.txt pyproject.toml Cargo.toml 2>/dev/null | head -20`

### Recent commits touching this dependency
!`git log --oneline -10 -- '*package.json' '*go.mod' '*requirements.txt' 2>/dev/null | head -10`
```

`!`...`` 这一行在 Skill 加载时被 Claude Code **执行并替换为输出**，于是 Claude 读到的正文里，当前版本号和最近的相关提交已经是真实数据。这比让 Claude 自己去翻文件更省 token、也更准。

注意：动态注入的命令也算 `allowed-tools` 里的 `Bash` 权限范围，且每次 Skill 加载都会执行——**别放慢命令**（如全仓库 `rg` 大表），也别放会改写文件的命令（`!` 是只读注入，写操作应留给正文里的显式步骤）。

## 第八步：用 allowed-tools 收敛权限（安全护栏）

第七步的 frontmatter 里我们写了 `allowed-tools: Read Grep Glob Bash`。这意味着这个 Skill 一旦激活，Claude **只能**用这四个工具——不能写文件（`Edit`/`Write`）、不能开子代理（`Agent`）、不能调 MCP 工具。对于"只评估、不执行"的 `bump-scout`，这是恰当的收敛：即便 Claude 误判想"顺手帮你升级"，它也没有写权限，只能产出计划交给你。

写文件工具（Edit/Write）和 Bash 默认仍受你的全局权限模式（permission mode）约束，`allowed-tools` 是在此基础上**再做减法**。判断原则：**Skill 只应拥有完成任务所必需的最小工具集**。一个只读分析 Skill 就不该有写权限；一个生成代码的 Skill 才需要 `Edit`。

## 第九步：测试触发并迭代

Skill 有两种触发方式，都要测：

**方式 A · 模型自动触发（model-invoked）**——开一个有 `package.json` 的项目，自然地说：

> 我想把 react 从 18 升到 19，先帮我看看影响多大

Claude 读到"upgrade react"匹配 `description` 里的触发短语，应自动加载 `bump-scout`，无需敲 `/`。

**方式 B · 用户显式调用**：

```
/bump-scout react 18.2.0 19.0.0
```

`$package` 等参数自动填入正文。

### 触发排错速查

| 症状 | 原因 | 解决 |
|------|------|------|
| 完全不触发 | `description` 没写或没含触发短语 | 把用户真实会说的话放进描述前两句 |
| 触发太频繁（误伤） | 描述太宽泛（如只写 "helps with code"） | 收窄到具体动作，加 `Does NOT...` 边界 |
| 自动补全里描述被截断 | 超过 1536 字符 | 删掉次要内容，关键信息前置 |
| 想强制手动调用 | 不想让模型自动触发 | frontmatter 加 `disable-model-invocation: true` |
| 想隐藏不让用户敲 `/` | 仅作内部子能力 | frontmatter 加 `user-invocable: false` |

**迭代循环**：改 `description` → 重新自然语言提问 → 看是否触发 → 调整。改 `SKILL.md` 正文或脚本**无需重启**，实时生效；只有首次创建一个顶层 `skills/` 目录才需要重启 Claude Code。

## 第十步：在子代理里跑（可选进阶）

对于扫大仓库这种重活，可让 Skill 在隔离的子代理里执行，避免污染主会话上下文。frontmatter 加一行：

```yaml
context: fork
```

`context: fork` 会让该 Skill 在一个 fork 出来的子上下文里运行，其大量的中间读取（翻 CHANGELOG、扫文件）不占主会话窗口，只把最终计划返回。代价是子代理看不到主会话的对话历史——所以 `bump-scout` 这种需要当前依赖状态的任务，靠动态注入（第七步）把状态带进去，正好和 `fork` 配合得天衣无缝。

## 实战案例：评估一次 React 18 → 19 升级

在某个中型前端项目根目录：

```
/bump-scout react 18.3.1 19.0.0
```

Claude 加载 `bump-scout`，动态注入读出当前 `package.json` 里 react 实为 `18.3.1`，随后：

1. 运行 `scripts/affected.sh react` → 发现 `src/components/` 下 42 个文件、`src/pages/` 下 18 个文件引用了 react。
2. 因跨主版本、框架级、>50 文件，查 `risk-matrix.md` → 命中多条"上调一级"触发器 → 判定 **HIGH**。
3. 读 React 19 release notes → 提炼三大破坏性变更：`ReactDOM.render` 移除、`propTypes`/`defaultProps` 函数组件弃用、`ref` 直接作 prop。
4. 填 `upgrade-plan.md`，产出：

```markdown
# Upgrade Plan: react 18.3.1 → 19.0.0
**Risk verdict: HIGH**

## 1. Summary
跨主版本升级，60 个文件受影响，需迁移已弃用 API；建议分阶段灰度。

## 2. Affected surface
| Directory | Files | Notes |
|-----------|-------|-------|
| src/components | 42 | 大量使用 defaultProps |
| src/pages | 18 | 普遍用 ReactDOM.render |

## 3. Top breaking changes
1. `ReactDOM.render` 被移除 → 须改用 `createRoot`
2. 函数组件 `defaultProps` 弃用 → 改默认参数
3. `ref` 作为普通 prop 传递 → 移除 `forwardRef` 的多数用法

## 4. Required code changes
- [ ] 全局替换 `ReactDOM.render` → `createRoot(...).render(...)`
- [ ] 扫描并迁移所有 `defaultProps` 为参数默认值
- [ ] 审查 `forwardRef` 使用，能简化则简化

## 5. Rollback plan
- Commit hash before upgrade: `<在本步骤前先提交>`
- Rollback command: `git revert <hash>` 并 `npm ci` 恢复锁文件
- Verify-rollback: `npm test` 全绿 + 首页冒烟

## 6. Verification
- [ ] `npm test` green
- [ ] 冒烟：首页渲染、路由跳转、表单提交
```

整个过程 Claude **没有执行任何 `npm install`**（因为 `allowed-tools` 没给、`description` 也声明了不做），只产出计划。你 review 后再手动执行。这正是 `bump-scout` 的价值：把"升级前要不要慌、慌什么"这件需要经验的事，变成可复用的流程。

## 工作流程总览

```mermaid
flowchart TD
    U[用户: 我想升级 react 到 19] --> M{description 匹配?}
    M -->|是| L[加载 bump-scout 第二层]
    L --> INJ[动态注入: 读 package.json + git log]
    INJ --> S1[运行 scripts/affected.sh\n扫描受影响文件]
    S1 --> S2[读 references/risk-matrix.md\n判定风险等级]
    S2 --> S3[读 CHANGELOG\n提炼破坏性变更]
    S3 --> S4[填 templates/upgrade-plan.md]
    S4 --> OUT[输出风险评级 + 升级计划]
    M -->|否| X[不加载, 零成本]
    style L fill:#fef3c7
    style OUT fill:#dcfce7
    style X fill:#fee2e2
```

绿色是产出，黄色是第二层加载点，红色是"没触发则免费"。整条链路里，`risk-matrix.md` 这种重内容只有在"真要分级"时才进入上下文。

## 常见问题与最佳实践速记

- **正文要短**：加载后常驻整个会话，每行都是反复成本。说"做什么"而非"为什么"，把"为什么"下沉到参考文件。
- **命名具体、可搜索**：`bump-scout` 优于 `dependency-tool`；命令名即用户要敲的 `/bump-scout`。
- **一个 Skill 一件事**：职责单一，触发才精准，团队才好复用。
- **脚本要防御性**：`set -euo pipefail`、兜底非零退出、可独立测试。
- **自定义命令已并入 Skill**：老的 `.claude/commands/deploy.md` 仍可用，但 Skill 多了支撑文件、frontmatter 控制和自动触发——新项目优先用 Skill。
- **分享**：把整个 `bump-scout/` 目录提交到团队的 `.claude/skills/`（项目级，随仓库共享），或封装成插件（加 `.claude-plugin/plugin.json`）跨项目分发。

## 延伸阅读

- **官方 Skills 文档**：`code.claude.com/docs/en/skills` —— frontmatter 字段表、动态注入、子代理执行、嵌套目录发现的权威来源
- **skill-creator 内置 Skill**：用 `/skill-creator` 脚手架生成新 Skill 并跑 eval 度量触发准确率，是迭代优化的标准工具链
- **Agent Skills 开放规范**：Skill 格式跨 Claude Code / Codex / ChatGPT 通用，了解规范有助写出可移植 Skill
- **渐进式披露的"导航者模式"**：把第三层文件组织成"Claude 先读哪个、再读哪个"的决策树，控制大 Skill 的 context 成本

## 训练营适用性评估

- **适用类型**：Claude Code 中级训练营（学员已过入门关，开始构建自定义能力）、企业内部研发效能工作坊、AI 编程实战 Day 2
- **预计教学时长**：3 小时（30 分钟心智模型 + 90 分钟分步构建 + 45 分钟实战案例 + 15 分钟排错答疑）
- **分组建议**：2 人一组，一人写 Skill、一人扮演"用户"测试触发，互换角色能快速暴露 description 写得不好的问题
- **考核点**：能否独立产出一个 description 触发精准、正文精炼、含至少一个脚本与一个参考文件、并用 `allowed-tools` 收敛权限的可用 Skill
- **进阶衔接**：本教程完成后，可自然过渡到"用 skill-creator 跑 eval 量化触发准确率"（度量与盲测迭代），形成"构建 → 度量 → 优化"的完整闭环

## 参考来源

1. [Extend Claude with skills — Claude Code 官方文档](https://code.claude.com/docs/en/skills) — Skill 创建、frontmatter 字段表、动态上下文注入、存储位置与优先级、渐进式披露的权威说明
2. [Claude Code Skills: The Complete Guide (2026) — Duet](https://duet.so/guides/claude-code-skills-complete-guide) — Skill vs MCP vs Subagent 决策矩阵、三层加载成本模型、description 触发写法、500 行规则等最佳实践
3. [Claude Code Skills 2026 — Progressive Disclosure — anhtu.dev](https://anhtu.dev/claude-code-skills-2026-progressive-disclosure-and-how-to-standardise-workflow-for-engineering-teams-1062) — 三层渐进式披露的工程化拆解与团队标准化落地
4. [AI Agent Skills Guide 2026 — The Prompt Index](https://www.thepromptindex.com/how-to-use-ai-agent-skills-the-complete-guide.html) — Agent Skills 开放规范、跨平台（Claude Code/Codex/ChatGPT）兼容性与安全考量
5. [Deep Dive into SKILL.md: Progressive Disclosure — AgentUpdate](https://www.agentupdate.ai/tutorial/claude-code-tutorial/lesson-29/) — SKILL.md 模块化分层设计与复杂场景下的 context 成本控制

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
