# M02《智能体技能工程与技能开发》视频讲课脚本

> **目标时长**：30–40 分钟
> **风格**：口语化讲课，面向零基础但有好奇心的学员
> **配套**：讲义文档 `site/school/lessons/m02.md`

---

## 开场引入（约 3 分钟）

【画面提示：讲师出镜，背景为深色科技感布景，屏幕角落显示课程标题"M02 智能体技能工程与技能开发"】

同学们好，欢迎来到 TaTa School AGI 人类大学的第二课。我是你们的讲师。

上一课 M01，我们聊了一个很根本的问题：在 AGI 时代，你是谁？你不是智能体的"用户"，你是它的立法者、审计员和紧急制动阀。很多同学听完之后给我留言说，道理我都懂，但具体从哪下手呢？我手里到底有什么工具，能真正管住一个智能体？

今天这节课，就是给你递工具的。**而且是递给你手里最实在的一件工具——技能。**

先说一个判断，你们可以先记下来，三年后再回来验证：**未来三年，决定一个组织能不能管住智能体的，不是模型有多强，而是它的技能库设计得有多好。**

为什么这么说？大家想一想，大模型本质上是什么？它是一个概率机器。你给它同样的指令，它今天和明天可能给出不一样的行为。你没法跟一个概率机器签合同，对吧？但技能不一样。技能是人类亲手定义的、边界清晰的、可审计的执行单元。

我打个比方。智能体就像一个能力很强、但有时候不太靠谱的员工。它可以天马行空地想，但它每一次"动手"——发邮件、查数据库、调接口——都必须经过某个技能。**技能就是智能体行为的海关：所有进出的动作，都要在这里报关、检查、盖章。**

所以你看，技能设计不是"开发技巧"，它是**权力设计**。谁定义了技能的输入输出，谁就定义了智能体能做什么、不能做什么；谁规定了技能的权限，谁就规定了智能体能碰什么、不能碰什么。你在这一课学到的每一条设计原则，都是在为未来的智能体划定国境线。

这节课我们分四章来讲。第一章，任务拆解方法论——所有技能工程的第一步，不是写代码，而是把混沌变成清单。第二章，技能设计与编写——一个合格的技能长什么样，六要素缺一不可。第三章，技能挂载与测试——写好的技能，怎么让智能体"看见"并且安全地调用。第四章，边界测试与稳定性验证——技能上线之前，必须经过的"刑讯"环节。最后我们总结，并留一份实操作业。

好，废话不多说，我们进入第一章。

---

## 第一章：任务拆解方法论（约 8 分钟）

【画面提示：切换到幻灯片，标题"第一章：任务拆解——把混沌变成清单"，配一张"一团乱麻的毛线 vs 整齐的清单"的对比图】


同学们，所有技能工程的第一步，不是写技能，而是**拆解任务**。这句话你给我记住了，因为 90% 的技能设计灾难，根子都不在代码，而在拆解这一步就错了。

人类说话是混沌的。老板跟你说："帮我把这周销售数据整理一下，弄份报告出来。"这句话里藏着至少五个不同的动作：找数据源、拉取数据、清洗、分析、排版输出。如果你直接把整句话丢给智能体，让它"看着办"，你就失去了对每一个环节的控制。它可能用了错误的数据源，可能跳过了清洗，可能编造了数字，而你无从察觉。为什么无从察觉？因为你连"它应该分几步做"都不知道，你怎么检查？

任务拆解，就是把一句混沌的人话，变成一张**有序的、可验证的**动作清单。好的拆解遵循三条原则。来，我们一条一条讲。

**第一条，MECE——相互独立、完全穷尽。** 这是麦肯锡那帮人发明的词，Mutually Exclusive, Collectively Exhaustive。翻译成人话就是：每个子任务不重叠，所有子任务加起来等于原任务。就像切蛋糕，每一刀下去，块与块之间不重叠，所有块拼起来是完整的一块。

为什么这条重要？如果两个子任务重叠了，比如 T2 是"清洗数据"、T3 是"去除重复数据"，那去重到底归谁管？智能体可能两边都做，也可能两边都不做，互相甩锅。如果子任务加起来不等于原任务，比如你漏了"分发报告"这一步，那报告生成了躺在服务器上，没人看，等于白干。

**第二条，原子性——拆到"一步只做一件事"。** "生成报告"不是原子任务，它里面藏着"取数""计算""排版"三个动作。原子任务的标准是：它的输入输出可以用一句话说清楚，它的成功失败可以用一个检查来判定。如果一个任务你讲了三分钟还没讲明白它到底干什么，说明它还能再拆。

**第三条，可验证性——每个子任务都要有验收标准。** 注意，"数据清洗完成"的验收标准不是"感觉干净了"，而是"空值率小于 1%、日期格式统一为 YYYY-MM-DD、重复行已去重"。没有验收标准的拆解，等于没拆。因为到时候智能体跟你说"我洗完了"，你问"怎么证明"，它说"我觉得挺干净的"——你一点办法都没有。

好，理论讲完，我们看一个完整的实例。还是那个需求："整理本周销售数据并生成报告"。

【画面提示：屏幕显示任务拆解树状图，逐层展开动画】



```text
任务：整理本周销售数据并生成报告
├─ T1 获取数据：从 CRM 导出本周（周一~周日）销售订单 CSV
│ └─ 验收：文件存在，行数 > 0，包含字段[订单号, 金额, 日期, 销售员]
├─ T2 清洗数据：去除重复订单，补全缺失字段，统一日期格式
│ └─ 验收：重复率 0%，空值率 < 1%，日期格式统一
├─ T3 汇总分析：按销售员分组统计总额，计算环比
│ └─ 验收：总额与 T2 清洗后数据一致，环比公式正确
├─ T4 生成报告：按模板输出 Markdown，含表格与 Top3 点评
│ └─ 验收：模板字段齐全，数字与 T3 一致
└─ T5 分发：将报告发送到管理群，并归档
└─ 验收：发送成功回执，归档路径可查
```



【画面提示：树状图定格，T2 的验收标准高亮放大】

看到没有？拆解之后，每个环节都有名字、有验收。**管控者的权力就藏在这里**：T2 的验收标准是你定的，T5 发到"哪个群"是你定的。拆解得越细，你的管控抓手就越多。这就是为什么我说，拆解不是技术活，它是权力活。

我再补充一个实战技巧，叫"**逆向拆解法**"。有时候正向拆解想不清楚，你就从最终交付物倒着往回推。报告长什么样？→ 需要哪些数字？→ 数字从哪算出来？→ 原始数据从哪来？→ 数据怎么拿到？倒着推五步，清单自然就出来了。这个方法特别适合新手，因为最终交付物是你最确定的东西。

第一章我们讲到这。记住三句话：**MECE 不重叠不遗漏，原子性一步一件事，可验证性每个都有验收标准。** 下一章，我们把拆解出来的原子任务，封装成真正的技能。

---

## 第二章：技能设计与编写（约 10 分钟）

【画面提示：幻灯片标题"第二章：技能设计——给动作上户口"，背景显示一张"户口本/身份证"的隐喻图】


好，上一章我们把任务拆成了原子任务。现在问题来了：拆出来的这些原子任务，如果每次都让智能体临场发挥，是不是又回到了不可控？对。所以，**值得被反复使用的原子任务，要把它封装成技能。**

我打个比方：任务拆解是把"做一顿饭"拆成"买菜、洗菜、切菜、炒菜"；技能封装就是把"炒青菜"这个动作写成标准菜谱——油温几成、下菜顺序、盐放几克。下次不管谁来炒，照着菜谱做，味道都差不多。**技能，就是智能体世界的标准菜谱。**

一个完整的技能定义包含六个部分，缺一不可。我们一个一个讲，还是用"销售数据清洗"这个例子。

【画面提示：屏幕分六栏，逐栏展开技能六要素】



**第一，技能名称。** 比如 `sales_data_clean`。命名规范：小写字母加下划线，动词开头，一看就知道它是干什么的。不要起 `process_data` 这种名字——处理什么数据？怎么处理？名字是技能的第一张脸，也是智能体搜索匹配时的关键词。

**第二，功能描述。** 一句话说清楚这个技能是干什么的："对销售订单 CSV 进行去重、补全、格式统一"。注意，功能描述是给**人**看的（管控者、审计员），要精确，不要文学修辞。

**第三，输入契约。** 这是重中之重。每一个输入字段，都要有：名称、类型、是否必填、取值说明。看例子：

```yaml
输入契约：
- file_path:
类型：字符串
必填：是
说明：CSV 文件的绝对路径，必须以.csv 结尾
- required_fields:
类型：字符串数组
必填：是
说明：必须存在的字段名，如 ["订单号", "金额", "日期"]
```



【画面提示：输入契约部分放大，三个字段（名称/类型/必填）用不同颜色标注】

同学们注意，接口设计的铁律是：**输入输出的每个字段，都要有类型、有必填/选填标记、有取值范围说明。** "字符串"不够，要写"字符串，CSV 文件的绝对路径，必须以.csv 结尾"。"数字"不够，要写"数字，单位为元，保留两位小数，范围 0 到 99999999"。

为什么这么较真？因为接口是智能体"看见"的部分。智能体在规划任务时，它看不到你的代码，它只能看到："哦，这里有个叫 sales_data_clean 的技能，需要一个文件路径，会返回清洗后的文件。"它基于这个理解来决定要不要调用、怎么调用。如果你的接口含糊——输入写的是"数据"，输出写的是"处理好的数据"——智能体就会靠猜。**猜，就意味着不可控。**

记住一句话：**你今天在接口上省下的每一个字，都是明天审计时要流的每一滴泪。**

**第四，输出契约。** 同样，每个输出字段都要定义清楚：

```yaml
输出契约：
- cleaned_path:
类型：字符串
说明：清洗后文件的绝对路径
- stats:
类型：对象
说明：{总行数, 去重数, 补全数, 空值率}
```

注意这个 `stats`，它是技能的"体检报告"。一个好的技能，执行完之后要告诉你"我干了什么"——去了多少重、补了多少空值。这不只是为了好看，这是 M04 取证审计的数据来源。

**第五，执行逻辑。** 这是技能的"菜谱正文"，用伪代码或流程描述写清楚：

```text
执行逻辑：
1. 读取 CSV，校验 required_fields 是否齐全
→ 缺失则报错，列出缺失字段，不生成任何输出
2. 按订单号去重，记录去重数
3. 空值处理：
- 金额为空 → 填 0 并标记为"待核实"
- 日期缺失 → 整行移入异常表，不丢弃
4. 日期统一为 YYYY-MM-DD 格式
5. 输出清洗文件 + 统计报告
```



【画面提示：执行逻辑逐行显示，第 1 步和第 3 步标红，旁注"失败语义前置"】

看到第 1 步和第 3 步了吗？失败的处理逻辑**写在执行逻辑里面**，而不是事后补。这就引出了第六个要素。

**第六，失败语义。** 这是新手最容易漏掉、但管控者最看重的部分：

```yaml
失败语义：
- 缺失必填字段 → 报错并列出缺失字段，不生成任何输出
- 空值率 > 5% → 警告 + 生成异常报告，不阻断流程
- 文件不存在 → 直接失败，错误码 FILE_NOT_FOUND
- 磁盘空间不足 → 失败，错误码 DISK_FULL，保留已处理部分
```

同学们，一个没有失败语义的技能，就像一辆没有刹车的车——你只知道它能跑，不知道它失控时会怎样。**失败语义回答的是："出问题时，它会怎么做，而不是瞎猜。"**

我还要加一个很多教程不讲的要素，算我送你们的：**权限声明**。虽然严格说它是注册时的元数据，但你在编写技能的时候就要想好：

```yaml
权限声明：
- 读：指定目录下的 CSV 文件（只读）
- 写：输出目录（仅.csv 后缀）
- 不需要：网络访问、数据库写入、外部 API 调用
```



【画面提示：权限声明部分，"不需要"三个字放大加粗，配警示图标】

注意最后那一行"不需要"。**一个技能要诚实地声明它"不需要什么"，这和声明"需要什么"同样重要。** 回头我们讲评审的时候，管控者第一件事就是看权限声明：一个清洗本地文件的技能，如果写着"需要网络访问"，你就要打个问号——它要把数据发到哪里去？

好，六要素讲完了。我们来做个对比，感受一下"好技能"和"坏技能"的差距。看"发送邮件"这个技能。

【画面提示：屏幕左右分栏，左侧红色"坏技能"，右侧绿色"好技能"】



坏的版本，新手经常这么写：

```text
功能：发送邮件
输入：收件人、内容
执行：调用邮件服务发送
```

就这。没有输出契约——发送成功了还是失败了？没有权限声明——能给谁发？能发附件吗？没有失败语义——收件人地址写错了怎么办？没有频率限制——智能体一秒钟发一万封呢？

这个技能一旦挂载，等于给了智能体一张**空白支票**。它可以给任何人发任何内容，无限次。这不是技能，这是后门。

好的版本应该是这样：

```yaml
技能名称：send_email_v2
输入契约：
- to: 字符串数组，收件人邮箱（必填，最多 5 个）
- subject: 字符串，标题（必填，最多 100 字符）
- body: 字符串，正文（必填，最多 5000 字符）
- attachments: 文件数组（选填，单个 < 10MB，总计 < 25MB）
输出契约：
- message_id: 字符串，邮件服务返回的消息 ID
- sent_at: 时间戳
权限声明：
- 仅允许发送到 @company.com 域名（白名单）
- 每小时最多 20 封（频率限制）
- 附件仅允许读取 /attachments/ 目录
失败语义：
- 收件人非白名单域名 → 拒绝，错误码 DOMAIN_NOT_ALLOWED
- 超过频率限制 → 拒绝，错误码 RATE_LIMITED，并记录审计日志
- 发送失败 → 重试 1 次，仍失败则报错，绝不静默返回成功
审计：
- 每次调用记录：调用者、时间、收件人、标题哈希
- 注意：存的是标题哈希，不是正文原文——可审计，又保护隐私
```



【画面提示：右侧好技能的五个亮点逐条弹出：①粒度 ②幂等性 ③可观测性 ④权限诚实 ⑤失败不撒谎】

对比一下，差距在五个地方：

**第一，粒度。** 坏技能的粒度是"能发邮件就行"，好技能的粒度是"在明确约束下发邮件"。约束不是束缚，约束是**让智能体的行为可预测**。可预测，才可管控。

**第二，幂等性。** 好技能要考虑：如果智能体因为网络超时，重发了一次，会不会发出两封重复邮件？成熟的技能设计会加入去重键。这是细节，但魔鬼全在细节里。

**第三，可观测性。** 好技能的每一次调用都留下痕迹。注意它存的是"标题哈希"而不是正文原文——既保证了可审计，又保护了隐私。**管控不是要看穿一切，而是要在需要时能查到一切。**

**第四，权限诚实。** 声明的权限和实际需要的权限完全一致，每一条都是管控者可以独立验证的。

**第五，失败时不撒谎。** 坏技能发送失败可能就静默返回"成功"了——很多新手代码都这么写，try-except 然后 pass。好技能明确规定：失败就是失败，报错就是报错，**绝不伪造成功**。同学们，这句话给我记住了，后面 M04 我们会讲到，"误报成功"是智能体系统里最危险的行为模式之一，而它的根子，往往就埋在技能的失败语义里。

总结成一句话：**好技能的定义，不是"功能强大"，而是"行为可预测、边界可验证、失败可追溯"。** 功能强大是智能体的事，边界清晰是你的事。

讲到这里，我多补充一个实战中经常被问到的问题：**技能上线之后，需求变了怎么办？**

比如 `send_email_v2` 上线三个月后，业务说："我们现在需要支持抄送功能。"你有两种选择。第一种，直接改 `send_email_v2`，加个 `cc` 字段。听起来简单，但风险是：所有正在调用这个技能的智能体，它们的调用代码都是按老接口写的，你一改，它们可能就报错了。这叫"破坏性变更"。

正确的做法是发新版本：`send_email_v3`，在 v2 的基础上加 `cc` 字段，老版本 v2 继续保留。给调用方一个迁移期，比如 30 天，30 天后 v2 标记为 deprecated（已废弃），再过 60 天才真正下线。这就是语义化版本的意义：**主版本号升级，就意味着"我变了，你要注意"。**

【画面提示：版本演进时间线动画，v2 → v3 并行期 → v2 废弃 → v2 下线】

记住这个原则：**技能的接口一旦发布，就是承诺。** 你可以加新版本，但不能偷偷改老版本。因为每一个老版本背后，都可能有几十个智能体在依赖它。你改一行代码，可能影响的是一整条业务链。这就是为什么我们说，技能设计是权力设计——你的每一次变更，都是在行使权力，而权力必须有规则约束。

第二章到这里。下一章，我们讲写好的技能怎么挂载到智能体身上——这里有一道最重要的防火墙，叫"挂载不等于授权"。

---

## 第三章：技能挂载与测试（约 8 分钟）

【画面提示：幻灯片标题"第三章：技能挂载——让智能体'看见'技能"，配一张"图书馆书架"的隐喻图】


好，技能写好了，六要素齐全，代码也测试通过了。现在的问题是：智能体怎么知道有这个技能？怎么调用它？这个过程叫**挂载**。

挂载分三层：注册、发现、调用。我们一层一层讲。

**第一层：注册。** 技能要登记到一个"技能注册表"里。你可以把它理解成手机的应用商店：App 写好了不能直接跑，要先上架。注册时要提交四样东西：技能定义文档、安全评估报告、权限声明、版本号。



【画面提示：显示注册表界面示意图，四个提交物逐个打勾】

注册表是管控者的第一道关。**不合规的技能，连被看见的资格都没有。** 比如你的权限声明里写着"需要访问整个互联网"，但功能只是"清洗本地 CSV"，注册审核直接打回。这道关存在的意义是：把问题拦在上线之前，而不是等出事了再救火。

版本号也很重要。技能不是一成不变的，业务变了、数据源变了、威胁变了，技能也要迭代。版本号用语义化版本：`主版本.次版本.修订号`，比如 `1.2.3`。主版本号升级意味着"接口变了，调用方要注意"；次版本号是"加了新功能，接口兼容"；修订号是"修 bug，不影响使用"。**没有版本号的技能，就是没有历史的技能，出了问题你都不知道回滚到哪一版。**

**第二层：发现。** 智能体在执行任务时，会根据当前子任务的描述，去注册表里搜索匹配的技能。"我需要清洗一份销售数据" → 搜索到 `sales_data_clean` → 读取它的接口文档 → 决定调用。

这里的关键是：**智能体只能发现它被授权可见的技能。** 财务技能不对客服智能体开放，HR 技能不对财务智能体开放。这是挂载时的权限隔离。就像公司内网，财务部的共享文件夹，客服部的人连看都看不到。

我给你们看一段简化的发现逻辑伪代码：

```python
def discover_skills(agent, task_description):
# 第一步：从注册表获取该智能体"可见"的技能列表
visible = registry.list_visible_skills(agent.role)
# 注意：不是所有技能，是"该角色可见"的子集

# 第二步：语义匹配
candidates = []
for skill in visible:
score = semantic_match(task_description, skill.description)
if score > 0.7:
candidates.append((skill, score))

# 第三步：按匹配度排序，返回 Top 3
return sorted(candidates, key=lambda x: x[1], reverse=True)[:3]
```



【画面提示：代码逐行高亮，第一行 `list_visible_skills(agent.role)` 特别标注"权限隔离点"】

看到第一行了吗？`list_visible_skills(agent.role)`——传入的是智能体的**角色**。这意味着，发现机制本身就带权限过滤。智能体想搜它不该看的技能，注册表直接不返回。这就是纵深防御：**即使智能体"想"干坏事，它连工具都找不到。**

**第三层：调用。** 智能体按接口契约传入参数，技能执行，返回结果。调用过程全程记录日志：谁在什么时间、用什么参数、调用了哪个技能、返回了什么。这是 M04 取证审计的数据来源。

```python
def call_skill(agent, skill_id, params):
# 1. 校验参数是否符合输入契约
validate(params, skill.input_contract) # 不符合直接拒绝

# 2. 检查调用权限（注意：这是第二道授权！）
if not check_permission(agent, skill_id):
raise PermissionDenied("该智能体无权调用此技能")

# 3. 记录审计日志（调用前）
audit_log.write({
"who": agent.id,
"when": now(),
"skill": skill_id,
"params_hash": hash(params), # 存哈希，不存原文
})

# 4. 执行
result = skill.execute(params)

# 5. 记录审计日志（调用后）
audit_log.write({
"skill": skill_id,
"result_status": result.status,
"duration_ms": result.duration,
})
return result
```



【画面提示：代码第 2 步高亮，弹出大字"挂载 ≠ 授权"】

同学们，看到第 2 步了吗？这里我要讲本章最重要的一句话，你们给我记住了：**挂载不等于授权。**

一个技能挂载到了智能体身上，只代表"它能看见这个技能"；至于"它能不能调用"，那是另一套授权规则决定的。就像你手机里装了银行 App（挂载），不代表你能直接转账，还要指纹、密码、短信验证码（授权）。**把"看得见"和"用得了"分开，是管控设计里最基本、也最容易被忽略的一道防火墙。**

为什么要分开？因为场景是动态的。平时客服智能体可以调用"查询订单"技能；但在大促期间，系统负载高，管控者可以临时收回调用授权，而不需要把技能"卸载"。挂载是静态的（装一次），授权是动态的（随时可调）。**静态和动态分开，你才有"一键熔断"的能力。** M03 我们讲熔断机制，根子就在这里。

好，挂载三层讲完了。接下来是测试。技能挂载之后、上线之前，必须经过三轮测试。

**第一轮：单元测试。** 针对技能本身，输入各种参数，看输出对不对。正常输入、边界输入、非法输入，都要覆盖。这是开发的基本功，我不多讲。

**第二轮：集成测试。** 把技能放到真实的任务流里跑。比如 `sales_data_clean`，给它一份真实的脏数据，看它能不能和 T1（获取数据）、T3（汇总分析）顺畅衔接。重点测试**接口契约的兼容性**：T1 输出的文件路径格式，T2 能不能正确读取？

**第三轮：对抗测试。** 这是管控者最关心的。故意给技能喂"坏"输入：超大文件、畸形 CSV、SQL 注入字符串、路径穿越攻击（比如 `../../etc/passwd`）。看技能是优雅拒绝，还是崩溃，还是——最可怕的——"热心"地执行了恶意指令。

```python
# 对抗测试用例示例
test_cases = [
("正常文件", "sales_normal.csv", "应该成功"),
("超大文件", "sales_10GB.csv", "应该拒绝或分块处理"),
("路径穿越", "../../etc/passwd", "必须拒绝，错误码 PATH_INVALID"),
("畸形CSV", "sales_broken.csv", "应该报错，不生成脏输出"),
("空文件", "sales_empty.csv", "应该返回空结果，不崩溃"),
("SQL注入", "'; DROP TABLE--.csv", "必须当作普通文件名处理"),
]
```



【画面提示：测试用例列表逐条显示，"路径穿越"和"SQL注入"标红】

同学们，对抗测试的哲学是：**假设调用者不可信**。你不是在为"听话的智能体"测试，你是在为"可能被提示注入、可能被误导、可能自己推理出歪点子的智能体"测试。**技能设计的第一性原理：默认不信任调用者，只信任契约。**

第三章到这里。下一章，我们把测试再推向极致——边界测试与稳定性验证。这是技能上线前的最后一道"刑讯"。

---

## 第四章：边界测试与稳定性验证（约 7 分钟）

【画面提示：幻灯片标题"第四章：边界测试——上线前的'刑讯'"，背景为暗色，配"压力测试仪表盘"图】


同学们，上一章我们讲了三轮测试。但说实话，那三轮还只是"常规体检"。这一章我们讲的是"刑讯"——把技能逼到极限，看它会不会说谎、会不会崩溃、会不会干出格的事。

为什么需要这么狠？因为生产环境比你想象的残酷。真实世界的数据是脏的、网络是会断的、磁盘是会满的、上游 API 是会超时的。一个只在"岁月静好"时能跑的技能，上线第一天就会教你做人。

边界测试分四个维度。我们一个一个来。

**维度一：输入边界。** 每个输入参数，都要测试它的"边缘值"。字符串有多长？数字有多大？数组有多少个元素？

```python
# 以 send_email_v2 的 subject 字段为例（限制 100 字符）
boundary_tests = [
("空字符串", "", "应该拒绝：标题必填"),
("1个字符", "a", "应该成功"),
("99个字符", "a" * 99, "应该成功"),
("100个字符", "a" * 100, "应该成功：边界值"),
("101个字符", "a" * 101, "应该拒绝：超长"),
("特殊字符", "<script>alert(1)</script>", "应该转义，不执行"),
("换行注入", "标题\nBcc: hacker@evil.com", "必须过滤换行符"),
]
```



【画面提示：测试用例逐条显示，最后一条"换行注入"标红放大，旁注"邮件头注入攻击"】

看到最后一条了吗？"换行注入"——如果技能直接把用户输入拼进邮件头，攻击者可以用换行符注入 `Bcc` 字段，把邮件密送给自己。这是一个真实存在过的攻击手法。**边界测试不只是测"功能对不对"，更是测"攻击防不防得住"。**

**维度二：资源边界。** 技能运行时会消耗 CPU、内存、磁盘、网络。每一个都要有上限。

```yaml
资源限制声明：
- 最大执行时间：30 秒（超时则终止，返回 TIMEOUT）
- 最大内存：512MB（超限则终止，不拖垮宿主机）
- 最大输出文件：100MB（防止磁盘被写满）
- 网络请求：禁止（本技能不需要联网）
```



【画面提示：四个资源限制以仪表盘形式显示，指针指向红色区域时触发"熔断"】

同学们注意，"超时则终止"这五个字，背后是一整套工程实现。很多新手写的技能，超时了就一直卡着，智能体在那傻等，等到天荒地老。正确的做法是：**超时必须是一种明确的失败语义**，返回 `TIMEOUT` 错误码，而不是无声无息地 hanging。M04 我们会讲，"无响应的技能"和"撒谎的技能"一样危险，因为智能体不知道它到底成功了没有。

**维度三：并发边界。** 如果两个智能体同时调用同一个技能，会发生什么？如果同一个智能体在 1 秒内调用 100 次，会发生什么？

```python
# 并发测试场景
concurrency_tests = [
("10 并发正常调用", "应该全部成功，结果互相不干扰"),
("100 并发突增", "应该触发频率限制，拒绝多余请求"),
("同一文件并发写", "应该加锁排队，不产生脏数据"),
("并发 + 其中一个超时", "超时的不影响其他正常调用"),
]
```

这里的关键概念叫**幂等性**，我们在第二章提过。并发场景下，幂等性更重要：如果智能体因为超时重试了，你的技能能不能保证"重试一次"和"只调一次"的效果一样？做不到幂等，就不要让技能被并发调用——在注册时声明"仅支持串行调用"。

**维度四：依赖边界。** 技能依赖的外部服务挂了怎么办？数据库连不上、API 超时、文件系统只读——每一种都要有预案。

```text
依赖故障预案表：
├─ 发票验真 API 超时（3秒无响应）
│ └─ 预案：转人工审核，不默认通过，不默认拒绝
├─ CRM 数据库连接失败
│ └─ 预案：使用昨日缓存数据，并在报告中明确标注"数据截至昨日"
├─ 磁盘空间不足
│ └─ 预案：停止写入，保留已处理部分，报错 DISK_FULL
└─ 上游技能返回异常
└─ 预案：不猜、不补、全链路报错，让调用方决定
```



【画面提示：预案表逐行展开，最后一行"不猜、不补"放大加粗】

看到最后一行了吗？"不猜、不补"——这是技能工程里最重要的一句心法。上游返回异常，你的技能**不要**"智能地"猜一个值补上，不要"好心"地跳过这一步继续往下走。**异常必须向上传播，让有全局视野的调用方来决定。** 一个"热心"的技能，比一个"诚实报错"的技能危险十倍。因为热心意味着它在替你做决定，而它没有资格做这个决定。

好，四个维度讲完了。我给你们一个**上线前检查清单**，打印出来贴在工位上：

```text
□ 输入边界：每个参数的边缘值都测了吗？（空、最大、超限、特殊字符）
□ 资源边界：超时、内存、磁盘、网络，都有上限吗？
□ 并发边界：多并发下结果正确吗？频率限制生效吗？
□ 依赖边界：每个外部依赖挂了，都有预案吗？
□ 失败语义：所有失败路径都返回明确错误码吗？没有静默成功吧？
□ 审计日志：每次调用都留下痕迹了吗？
□ 权限复核：声明的权限和实际需要的一致吗？没有多要的吧？
□ 版本号：打了吗？变更日志写了吗？
□ 回滚方案：如果新版本出问题，能一键回滚到上一版吗？
□ 主人：这个技能有明确的负责人吗？（没主人的技能就是孤儿）
```



【画面提示：检查清单逐条打勾，最后一条"主人"特别标注】

最后一条我多说两句。"主人"——每个技能都要有一个明确的负责人。业务变了谁来更新？出 bug 了谁来修？权限要调整谁来批？没有主人的技能，三个月后就是没人敢动的"祖传代码"。**技能是资产，资产就需要治理，而治理的第一步，是知道它是谁的孩子。**

在进入总结之前，我想跟你们分享一个更高阶的理念，叫**混沌工程**。这个词是从 Netflix 那边传过来的，他们有个工具叫 Chaos Monkey，专门在生产环境随机"杀掉"服务器，看系统能不能自愈。

技能工程也需要这种思维。等你的技能体系成熟了，不要只在测试环境做边界测试，要定期在生产环境做"故障演习"：故意让某个技能超时，看调用方能不能正确处理；故意把某个技能的权限收回，看智能体会不会优雅降级；故意注入脏数据，看监控告警能不能及时响。

【画面提示：混沌工程示意图，一只"猴子"随机拔掉系统中的某个模块，其他模块自动补位】

听起来很疯狂对吧？但你要明白：**你不主动制造故障，故障就会在你最不希望的时候主动找上门。** 与其等到大促当天技能崩了全公司救火，不如每个月挑个业务低峰期，自己"引爆"一颗小炸弹。炸弹越小越好，越早炸越好。这就是混沌工程的精髓：在可控的小混乱中，验证系统的反脆弱性。

当然，这是进阶内容，新手先把四个维度的边界测试做扎实。等你管了 50 个以上的技能，再来玩混沌工程。

第四章到这里。技能从拆解、设计、挂载、测试，走完了全生命周期。最后，我们总结一下，并留一份作业。

---

## 总结与实操作业（约 4 分钟）

【画面提示：讲师出镜，背景切换为总结页，显示本课知识地图】


好，我们快速回顾一下这节课。

第一章，任务拆解。记住三句话：**MECE 不重叠不遗漏，原子性一步一件事，可验证性每个都有验收标准。** 拆解是管控的起点，你拆得越细，你的抓手就越多。

第二章，技能设计。六要素：名称、功能描述、输入契约、输出契约、执行逻辑、失败语义，再加上权限声明。好技能的标准不是"功能强大"，而是**行为可预测、边界可验证、失败可追溯**。接口上省下的每一个字，都是审计时要流的泪。

第三章，技能挂载。三层：注册、发现、调用。最重要的一个区分：**挂载不等于授权**。"看得见"和"用得了"分开，你才有"一键熔断"的能力。

第四章，边界测试。四个维度：输入边界、资源边界、并发边界、依赖边界。核心心法：**默认不信任调用者，只信任契约；异常不猜不补，向上传播。**

把四章串起来，是一句话：**技能设计即权力设计。** 你定义的每一个输入字段、每一条权限声明、每一种失败语义，都是在为智能体划定国境线。而你，就是那个划线的人。



【画面提示：屏幕显示"实操作业"标题】

好，留一份实操作业。这份作业请你亲手做一遍，写下来。光看不练，等于没学。

**作业背景**：你是一家公司的 IT 管控者。老板提了个需求：

> "每周一早上 9 点，把上周的客服工单汇总成一份问题分布报告，发到管理群。"

**请你完成以下四步**：

**Step 1：拆解任务。** 把这句人话拆成原子任务，每个标注验收标准。提示：想想"工单从哪来""汇总的维度是什么""报告长什么样""发到哪个群""发完要不要确认"。

**Step 2：识别技能。** 哪些子任务值得封装成可复用的技能？至少识别出 2 个，说明复用价值。（比如"拉取工单"可能每周都用，"生成 Markdown 报告"可能别的场景也用。）

**Step 3：完整定义其中一个技能。** 按本课的六要素（名称、功能描述、输入契约、输出契约、执行逻辑、权限声明、失败语义）写出来。**重点写好权限声明和失败语义**，这是管控者的基本功。

**Step 4：标注风险点。** 站在"攻击者"的角度想：如果这个技能被滥用，最坏的情况是什么？（比如：工单数据里有客户隐私，技能会不会泄露？报告发错群了怎么办？）针对每个风险点，写下你的管控措施。

**自查清单**（做完对照）：
- [] 我的拆解里，每个子任务都有验收标准吗？
- [] 我的技能输入输出，每个字段都有类型和必填标记吗？
- [] 权限声明里，有没有"顺手"多要的权限？
- [] 失败语义里，有没有"静默成功"的漏洞？
- [] 我能说清楚"这个技能**不能**做什么"吗？（说清楚"不能"，比说清楚"能"更重要）



【画面提示：自查清单逐条显示，最后一条高亮】

特别注意最后一条。**能说清楚"不能"，比说清楚"能"更重要。** 这是管控者思维和开发者思维最根本的区别。开发者关心"它能做什么"，管控者关心"它不能做什么"。当你能清晰地说出一个技能的"三不"——不能访问什么、不能超过什么、失败时不会什么——你才真正理解了这个技能。

这份作业做完，可以发到课程评论区，或者找你的学习伙伴互相评审。**能经受住"找茬"的技能定义，才是合格的。**

下一课 M03《AGI 安全边界设计与管控体系》，我们把视角从单个技能拉大到整个系统：当几十个技能、上百次调用编织成一张网，边界应该画在哪里？熔断应该装在哪个位置？单个安全，不等于组合安全——这是管控者最难的一课。



【画面提示：下课画面，显示"M03 预告"和课程二维码/链接】

好，M02 就到这里。同学们，下节课见。

---

*脚本完。总字数约 9500 字，预计时长 35 分钟。*
