Agent、Subagent、Skill、MCP、Hook 的原理、协作与生产级工作流

本文面向已具备一定 AI 开发基础的工程师,目标是帮助你真正理解 Claude Code 的"五层架构",并将它们组合成可落地的生产级工作流。


一、全局视图:五层架构

在深入每个概念之前,先建立一个整体心智模型。Claude Code 的扩展体系可以被理解为五个相互叠加的层次:

┌─────────────────────────────────────────────────────────┐
│  第5层  Plugin(插件)                                    │
│         将 Skill / Slash Command / Hook / MCP 打包分发    │
├─────────────────────────────────────────────────────────┤
│  第4层  Agent Teams(Agent 团队)                         │
│         多个独立 Claude 实例并行协作,拥有各自的上下文窗口  │
├─────────────────────────────────────────────────────────┤
│  第3层  Subagent(子 Agent)                              │
│         在主对话的隔离上下文中执行特定任务并回报结果         │
├─────────────────────────────────────────────────────────┤
│  第2层  Skills / Slash Commands / Hooks                  │
│         技能(按需加载的专家指令)/ 触发器 / 生命周期钩子   │
├─────────────────────────────────────────────────────────┤
│  第1层  MCP(模型上下文协议)                              │
│         连接外部系统:数据库、API、文件系统、SaaS 工具      │
└─────────────────────────────────────────────────────────┘
           ↑  所有层次都建立在 CLAUDE.md 的"项目记忆"之上

每一层都不是孤立的。MCP 让 Claude 能触达外部世界;Skill 让它知道"怎么做";Subagent 让它"并行干活";Plugin 则让整套配置可以在团队里复用。理解这个层次关系,是掌握所有后续概念的前提。


二、CLAUDE.md:一切的地基

在讲任何高级功能之前,CLAUDE.md 必须优先讲清楚,因为它是所有其他组件运行时的上下文基础。

CLAUDE.md 是 Claude Code 在进入项目时自动加载的"项目记忆"文件。放在项目根目录的 CLAUDE.md 文件会被注入每一次对话的 system prompt,相当于给 Claude 做了岗前培训。

一个生产级 CLAUDE.md 示例

假设你在维护一个 ROS2 + HarmonyOS 混合项目:

# 项目上下文:Sc_vision

## 项目结构
- `ros2_ws/` — ROS2 工作空间,使用 colcon 构建
- `harmony_app/` — HarmonyOS ArkTS 前端,使用 DevEco Studio 开发
- `shared_proto/` — 两侧共用的 Protobuf 消息定义

## 核心约束(必须遵守)
- 永远不要在未经确认的情况下 `git push`
- 所有 ROS2 节点必须包含 `rclcpp::shutdown()` 的正确生命周期处理
- ArkTS 代码中禁止使用任何 HarmonyOS API 3.x 以下的接口
- 修改 `package.xml` 前必须检查依赖是否在 ros-humble 中存在

## 构建命令
- ROS2: `cd ros2_ws && colcon build --symlink-install --packages-select <pkg>`
- 清理: `rm -rf ros2_ws/build ros2_ws/install ros2_ws/log`

## 代码风格
- C++: 遵循 Google C++ Style Guide,使用 `clang-format`
- ArkTS: 遵循华为官方 ArkTS 编码规范 v4.x

## 当前已知问题
- camera_driver 包的 CMakeLists.txt 中 OpenCV 路径需要手动指定

这个文件让 Claude 在每次对话开始时就具备项目的全局认知,而不需要你反复解释背景。


三、MCP:AI 的"USB-C 接口"

3.1 核心概念

MCP(Model Context Protocol)是 Anthropic 发布的一个开放标准,解决的是"AI 如何连接外部系统"这个问题。在 MCP 出现之前,每个工具都需要写一套专属集成;MCP 就像 USB-C,提供了一个统一的协议,任何系统只要实现这个协议就能被 Claude 调用。

MCP 的核心工作模型非常简单:

  • MCP Server 暴露工具(tools)和资源(resources),可以是本地进程(stdio 模式)或远程服务(HTTP/SSE 模式)。
  • MCP Client(即 Claude Code 本身)连接到这些 server,将它们暴露的工具当作"原生能力"使用。

关键认知:MCP 解决的是"能触达什么"的问题,而不是"怎么做"的问题。 能查数据库、能提 PR、能查 Slack 消息——这些都是 MCP 的职责范围。

3.2 配置 MCP Server

MCP 的配置通过项目根目录的 .claude/config.json 或全局配置文件来管理。下面是一个完整的生产配置示例:

// .claude/config.json
{
  "mcpServers": {
    "github": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "postgres": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "${DATABASE_URL}"
      }
    },
    "filesystem": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/home/user/projects/sc_vision"
      ]
    },
    "slack": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-slack"],
      "env": {
        "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}",
        "SLACK_TEAM_ID": "${SLACK_TEAM_ID}"
      }
    }
  }
}

3.3 自定义 MCP Server(Python 示例)

当现成的 MCP server 不满足需求时,可以自己写。以下是一个给 ROS2 项目暴露"构建状态"和"话题列表"的自定义 MCP server:

# ros2_mcp_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import subprocess
import json

server = Server("ros2-tools")

@server.list_tools()
async def list_tools():
    return [
        types.Tool(
            name="ros2_build",
            description="构建指定的 ROS2 包,返回构建日志",
            inputSchema={
                "type": "object",
                "properties": {
                    "package_name": {
                        "type": "string",
                        "description": "要构建的 ROS2 包名"
                    }
                },
                "required": ["package_name"]
            }
        ),
        types.Tool(
            name="ros2_list_topics",
            description="列出当前 ROS2 环境中的所有话题",
            inputSchema={"type": "object", "properties": {}}
        ),
        types.Tool(
            name="ros2_check_package",
            description="检查 package.xml 中的依赖是否都在 humble 中存在",
            inputSchema={
                "type": "object",
                "properties": {
                    "package_path": {"type": "string"}
                },
                "required": ["package_path"]
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "ros2_build":
        pkg = arguments["package_name"]
        result = subprocess.run(
            ["colcon", "build", "--symlink-install", "--packages-select", pkg],
            cwd="/home/user/ros2_ws",
            capture_output=True,
            text=True,
            timeout=120
        )
        return [types.TextContent(
            type="text",
            text=f"Exit code: {result.returncode}\\n"
                 f"STDOUT:\\n{result.stdout}\\n"
                 f"STDERR:\\n{result.stderr}"
        )]

    elif name == "ros2_list_topics":
        result = subprocess.run(
            ["ros2", "topic", "list"],
            capture_output=True, text=True, timeout=10
        )
        return [types.TextContent(type="text", text=result.stdout)]

    elif name == "ros2_check_package":
        # 解析 package.xml 并验证依赖
        import xml.etree.ElementTree as ET
        tree = ET.parse(f"{arguments['package_path']}/package.xml")
        root = tree.getroot()
        deps = [d.text for d in root.findall("depend")]
        return [types.TextContent(
            type="text",
            text=f"找到 {len(deps)} 个依赖: {', '.join(deps)}"
        )]

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream,
                         server.create_initialization_options())

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

然后在配置中注册:

"ros2": {
  "type": "stdio",
  "command": "python3",
  "args": ["/home/user/ros2_mcp_server.py"]
}

四、Skills:可移植的专家指令集

4.1 Skills 解决了什么问题

你有没有遇到过这种情况:你花了大量时间写了一个很好的 prompt,让 Claude 按照你团队的规范进行代码审查,结果下次开启新对话时,一切又要从头来过?Skills 正是为了解决这个"Prompt 复用"困境而生的。

Skills 的本质是按需加载的专家指令集。它采用了一个精妙的"渐进式披露"设计:

  • Skill 的名字和描述总是在 system prompt 中可见,相当于让 Claude 知道"我有哪些专家可以咨询"。
  • 只有当任务与某个 Skill 相关时,Claude 才会主动加载 SKILL.md 的详细内容。

这种设计极大地节省了 context window 的 token。你可以注册几十个 Skill,但 Claude 在每次对话中只加载真正用到的那几个。

4.2 目录结构

Skills 存放在 .claude/skills/ 目录下,每个 Skill 是一个独立文件夹:

.claude/
└── skills/
    ├── code-review/
    │   ├── SKILL.md          # 必须,主指令文件
    │   ├── checklist.md      # 可选,审查清单模板
    │   └── examples/         # 可选,好坏示例
    │       ├── good.cpp
    │       └── bad.cpp
    ├── ros2-node/
    │   ├── SKILL.md
    │   └── node_template.cpp # 可供参考的代码模板
    └── arkts-component/
        ├── SKILL.md
        └── component_template.ets

4.3 一个完整的 Skill 示例:ROS2 节点规范审查

---
name: ros2-node-review
description: >
  当用户要求审查、创建或调试 ROS2 节点代码时使用此 Skill。
  涵盖 C++ rclcpp 和 Python rclpy 节点,包括生命周期、话题、服务、参数等规范检查。
---

# ROS2 节点规范审查 Skill

## 角色定位
你是一位精通 ROS2 Humble 的嵌入式系统工程师,熟悉生产环境中的最佳实践。

## 审查清单

### 1. 生命周期管理(最高优先级)
- [ ] 是否正确初始化 `rclcpp::init(argc, argv)`
- [ ] 是否使用 `rclcpp::spin()` 或 `rclcpp::spin_some()` 而不是手写循环
- [ ] 是否在退出时调用 `rclcpp::shutdown()`
- [ ] 如果是 LifecycleNode,是否实现了所有必要的状态回调

### 2. 资源管理
- [ ] 订阅者和发布者是否作为类成员变量保存(避免被 GC)
- [ ] Timer 回调是否避免了长时间阻塞
- [ ] 是否正确使用了 `std::shared_ptr` 而非裸指针

### 3. 参数声明
- [ ] 所有参数必须在节点构造函数中用 `declare_parameter()` 声明
- [ ] 动态参数是否注册了 `set_parameters_callback`

### 4. QoS 配置
- [ ] 传感器数据使用 `rclcpp::SensorDataQoS()`
- [ ] 重要状态数据使用 `RELIABLE` 可靠性策略

## 代码生成模板

当被要求创建新节点时,使用以下模板:

```cpp
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class MyNode : public rclcpp::Node {
public:
  MyNode() : Node("my_node") {
    // 1. 声明参数
    this->declare_parameter("rate_hz", 10.0);

    // 2. 创建发布者/订阅者
    publisher_ = this->create_publisher<std_msgs::msg::String>("output", 10);
    subscriber_ = this->create_subscription<std_msgs::msg::String>(
      "input", 10,
      std::bind(&MyNode::callback, this, std::placeholders::_1));

    // 3. 创建定时器
    double rate = this->get_parameter("rate_hz").as_double();
    timer_ = this->create_wall_timer(
      std::chrono::duration<double>(1.0 / rate),
      std::bind(&MyNode::timer_callback, this));

    RCLCPP_INFO(this->get_logger(), "Node initialized");
  }

private:
  void callback(const std_msgs::msg::String::SharedPtr msg) {
    RCLCPP_DEBUG(this->get_logger(), "Received: %s", msg->data.c_str());
  }

  void timer_callback() {
    auto message = std_msgs::msg::String();
    message.data = "Hello";
    publisher_->publish(message);
  }

  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_;
  rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char ** argv) {
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MyNode>());
  rclcpp::shutdown();
  return 0;
}

### 4.4 ArkTS 组件规范 Skill

```markdown
---
name: arkts-component
description: >
  创建或审查 HarmonyOS ArkUI 组件时使用。
  确保组件符合 ArkTS 语法规范(API 12+),
  避免使用已废弃的 API 和常见的幻觉性错误。
---

# HarmonyOS ArkTS 组件开发规范

## 重要约束(防止幻觉)
- 只使用 HarmonyOS API 12(SDK 5.x)及以上的 API
- 禁止使用 `@State` 以外的装饰器作为入口组件的参数
- `@Link` 只能用于父子组件传递,不能跨层级
- `ForEach` 的 keyGenerator 参数不能返回非字符串类型

## 组件结构规范

### 必须遵守的顺序
1. 导入语句(`import { ... } from '@ohos/...'`)
2. 常量/枚举定义
3. 自定义组件(`@Component struct MyComponent`)
4. 入口组件(`@Entry @Component struct Index`)

### 状态管理规则
- 局部状态:`@State`
- 父传子(单向):`@Prop`
- 父子双向绑定:`@Link`
- 跨组件共享:`@Provide` / `@Consume`
- 复杂对象观察:`@Observed` + `@ObjectLink`

## 标准组件模板

```typescript
import { router } from '@kit.ArkUI';

interface ItemData {
  id: number;
  title: string;
  value: string;
}

@Component
struct ItemCard {
  @Prop item: ItemData;
  @State isExpanded: boolean = false;

  build() {
    Column() {
      Row() {
        Text(this.item.title)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .fontColor($r('app.color.text_primary'))
        Blank()
        Image($r('app.media.ic_arrow'))
          .width(20)
          .height(20)
          .rotate({ angle: this.isExpanded ? 180 : 0 })
          .animation({ duration: 200, curve: Curve.EaseInOut })
      }
      .width('100%')
      .padding(12)
      .onClick(() => { this.isExpanded = !this.isExpanded; })

      if (this.isExpanded) {
        Text(this.item.value)
          .fontSize(14)
          .fontColor($r('app.color.text_secondary'))
          .padding({ left: 12, right: 12, bottom: 12 })
      }
    }
    .backgroundColor($r('app.color.card_background'))
    .borderRadius(8)
    .margin({ bottom: 8 })
  }
}

@Entry
@Component
struct Index {
  @State items: ItemData[] = [
    { id: 1, title: '项目一', value: '详细内容一' },
    { id: 2, title: '项目二', value: '详细内容二' }
  ];

  build() {
    Scroll() {
      Column() {
        ForEach(this.items, (item: ItemData) => {
          ItemCard({ item: item })
        }, (item: ItemData) => item.id.toString()) // keyGenerator 必须返回 string
      }
      .padding(16)
    }
    .scrollable(ScrollDirection.Vertical)
  }
}

---

## 五、Subagent:隔离上下文的并行工作者

### 5.1 为什么需要 Subagent

想象你在做一个复杂的功能开发:需要同时调研最佳实践、审查现有代码、编写测试。如果所有这些工作都在同一个对话上下文中进行,会发生两个问题:第一,上下文窗口会被大量的中间输出填满,导致 Claude 对核心任务的关注度下降;第二,某些子任务的产出(比如调研报告)会"污染"主任务的上下文。

Subagent 解决了这个问题。**每个 Subagent 在自己独立的上下文窗口中运行**,完成任务后只将结果摘要返回给主 Agent,就像一个员工完成任务后只向上级汇报结论,而不是把全部工作过程都搬到会议室。

### 5.2 Subagent 文件结构

Subagent 定义文件存放在 `.claude/agents/` 目录:

.claude/ └── agents/ ├── code-reviewer.md ├── test-writer.md ├── documentation-writer.md ├── security-auditor.md └── ros2-debugger.md


### 5.3 完整的 Subagent 示例

**代码审查 Subagent:**

```markdown
---
name: code-reviewer
description: >
  当需要对代码进行深度审查时调用此 subagent。
  适用于 PR 审查、安全审计、性能分析。
  该 subagent 只读取文件,不做任何修改。
tools: Read, Grep, Glob, Bash
model: claude-sonnet-4-6
---

你是一位资深的代码审查工程师,专注于发现安全漏洞、性能问题和代码质量问题。

## 审查维度
1. **安全性**:SQL 注入、XSS、未验证的用户输入、硬编码密钥
2. **性能**:O(n²) 复杂度、不必要的数据库查询、内存泄漏
3. **可维护性**:函数长度(>50行需警告)、魔法数字、缺少注释的复杂逻辑
4. **一致性**:是否遵循了项目的编码规范(参考 CLAUDE.md)

## 输出格式
返回一个结构化报告,包含:
- 🔴 严重问题(必须修复)
- 🟡 警告(建议修复)
- 🟢 建议(可选优化)
- 总体评分(1-10)和一句话总结

## 工作方式
- 使用 Grep 搜索特定模式
- 使用 Glob 遍历相关文件
- 使用 Bash 运行 linter(如 `clang-format --dry-run`)
- 只读不写,所有发现以报告形式返回给主 Agent

测试编写 Subagent:

---
name: test-writer
description: >
  为指定模块编写单元测试和集成测试。
  在代码审查 subagent 完成后使用,基于审查发现的问题编写针对性测试。
tools: Read, Write, Edit, Bash, Glob, Grep
model: claude-sonnet-4-6
---

你是一位测试工程师,专注于编写高覆盖率、有意义的测试用例。

## 测试策略
- 对每个公共函数编写正常路径和边界条件测试
- 对审查报告中标注的 🔴 和 🟡 问题编写回归测试
- 使用 Google Test(C++)或 pytest(Python)

## ROS2 测试规范
- 使用 `rclcpp::executors::SingleThreadedExecutor` 在测试中运行节点
- 话题测试使用 `rclcpp::WaitSet` 等待消息
- 测试文件放在 `test/` 目录,命名为 `test_<module_name>.cpp`

## 输出
- 生成测试文件
- 返回测试覆盖的场景列表和运行命令给主 Agent

ROS2 调试 Subagent:

---
name: ros2-debugger
description: >
  当 ROS2 构建失败或运行时出错时调用。
  分析构建日志、检查依赖关系、诊断话题通信问题。
tools: Read, Bash, Grep, Glob
disallowedTools: Write, Edit
model: claude-sonnet-4-6
---

你是 ROS2 调试专家,熟悉 colcon、ament、rcl 的内部机制。

## 调试步骤
1. 读取构建日志,定位第一个错误(ERROR 关键字)
2. 检查 `package.xml` 的依赖声明
3. 检查 `CMakeLists.txt` 中的 `find_package` 和 `target_link_libraries`
4. 如果是运行时错误,检查话题名称和 QoS 配置是否匹配
5. 使用 `ros2 pkg list | grep <pkg>` 验证包是否已安装

## 常见问题模式
- `No rule to make target` → CMakeLists.txt 中文件路径错误
- `undefined reference to` → `target_link_libraries` 缺少库
- `Could not load library` → 需要 `source install/setup.bash`
- `QoS mismatch` → 发布者和订阅者的可靠性策略不一致

返回:根本原因分析 + 具体修复方案(不直接执行修改,方案由主 Agent 确认后执行)

六、Hooks:生命周期钩子

6.1 概念说明

Hooks 是在 Claude Code 工作流的特定生命周期事件发生时自动触发的脚本或命令。它们类似于 Git 的 pre-commit hooks,但作用于 AI 辅助开发的整个生命周期。

主要的 Hook 触发点包括:

  • PreToolCall:在 Claude 调用任何工具之前
  • PostToolCall:在工具调用完成之后
  • Stop:在 Claude 完成一轮响应之后

6.2 Hooks 配置示例

// .claude/config.json 中的 hooks 配置
{
  "hooks": {
    "PostToolCall": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/format_code.sh $CLAUDE_TOOL_RESULT_FILE"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

自动格式化 Hook 脚本:

#!/bin/bash
# .claude/hooks/format_code.sh
# 在每次文件写入后自动运行 linter

FILE="$1"

if [[ "$FILE" == *.cpp ]] || [[ "$FILE" == *.hpp ]]; then
    echo "[Hook] 运行 clang-format: $FILE"
    clang-format -i "$FILE"
fi

if [[ "$FILE" == *.py ]]; then
    echo "[Hook] 运行 black: $FILE"
    black "$FILE" --quiet
fi

if [[ "$FILE" == *.ets ]] || [[ "$FILE" == *.ts ]]; then
    echo "[Hook] 运行 prettier: $FILE"
    npx prettier --write "$FILE" --log-level warn
fi

任务完成通知 Hook:

#!/bin/bash
# .claude/hooks/notify.sh
# Claude 完成任务时发送桌面通知或 Slack 消息

MESSAGE="Claude Code 完成了任务!"

# 桌面通知(Linux)
if command -v notify-send &> /dev/null; then
    notify-send "Claude Code" "$MESSAGE" --icon=dialog-information
fi

# 或发送 Slack 消息
if [ -n "$SLACK_WEBHOOK_URL" ]; then
    curl -s -X POST "$SLACK_WEBHOOK_URL" \\
         -H 'Content-type: application/json' \\
         --data "{\\"text\\": \\"$MESSAGE\\"}"
fi

七、生产级工作流实战

理论讲完了,现在把所有概念串联起来,构建三个真实的生产级工作流。


工作流一:Feature Branch 全自动交付流水线

场景描述: 开发完成一个 feature 后,通过一条命令完成:代码审查 → 修复问题 → 编写测试 → 格式化 → 提交 PR。

涉及组件: Subagent(code-reviewer、test-writer)+ MCP(GitHub)+ Skill(change-report)+ Hook(格式化)

Slash Command 定义:

<!-- .claude/commands/ship-feature.md -->
你正在执行 "ship-feature" 工作流。请按照以下步骤严格执行:

## 第一步:代码审查
使用 code-reviewer subagent 审查当前 feature branch 相对于 main 的所有改动。
等待审查报告完成后再继续。

## 第二步:修复严重问题
根据审查报告中的 🔴 严重问题,逐一修复。
修复后告知我,等待我确认再继续。

## 第三步:编写测试
使用 test-writer subagent,针对本次改动和审查发现的问题编写测试。
运行测试确保全部通过。

## 第四步:格式化(Hook 自动触发)
此步骤由 PostToolCall Hook 自动完成,无需手动执行。

## 第五步:提交 PR
使用 GitHub MCP 创建 PR。PR 标题和描述按照以下格式:
- 标题:`[Feature] <功能简述>`
- 描述:使用 change-report skill 生成结构化报告

## 进度汇报
每完成一步,用一行简短的 ✅ 标记告知我进度。

执行时,在 Claude Code 中输入:

/ship-feature

Claude 会自动按步骤编排,调用对应的 Subagent 和 MCP 工具。


工作流二:ROS2 包从零到可运行

场景描述: 用自然语言描述一个 ROS2 节点的功能需求,Claude 自动生成完整的包结构、代码、CMakeLists、package.xml,并完成构建验证。

用到的 CLAUDE.md 上下文 + ros2-node Skill + ros2-debugger Subagent + ROS2 MCP Server。

实际对话示例:

用户: 帮我创建一个叫 sc_camera_driver 的 ROS2 包。
功能:订阅 /camera/raw(sensor_msgs/Image),
用 OpenCV 做灰度转换,发布到 /camera/gray(sensor_msgs/Image)。
节点名叫 GrayscaleConverter,参数有 encoding(默认 "mono8")。

Claude 的执行流程(内部):

  1. 检测到 ROS2 相关任务 → 自动加载 ros2-node Skill
  2. 创建完整的包目录结构
  3. 生成符合规范的节点代码(基于 Skill 模板)
  4. 编写 CMakeLists.txtpackage.xml
  5. 调用 ROS2 MCP Server 执行构建:ros2_build("sc_camera_driver")
  6. 如果构建失败 → 启动 ros2-debugger Subagent 分析日志
  7. 返回构建结果和运行命令

生成的代码示例(节选):

// src/grayscale_converter.cpp
#include "rclcpp/rclcpp.hpp"
#include "sensor_msgs/msg/image.hpp"
#include "cv_bridge/cv_bridge.h"
#include "opencv2/imgproc.hpp"

class GrayscaleConverter : public rclcpp::Node {
public:
  GrayscaleConverter() : Node("grayscale_converter") {
    // 遵循 ros2-node Skill:必须先声明参数
    this->declare_parameter("encoding", "mono8");
    encoding_ = this->get_parameter("encoding").as_string();

    subscriber_ = this->create_subscription<sensor_msgs::msg::Image>(
      "/camera/raw",
      rclcpp::SensorDataQoS(),  // 遵循 Skill:传感器数据用 SensorDataQoS
      std::bind(&GrayscaleConverter::image_callback, this,
                std::placeholders::_1));

    publisher_ = this->create_publisher<sensor_msgs::msg::Image>(
      "/camera/gray", rclcpp::SensorDataQoS());

    RCLCPP_INFO(this->get_logger(),
                "GrayscaleConverter initialized, encoding: %s",
                encoding_.c_str());
  }

private:
  void image_callback(const sensor_msgs::msg::Image::SharedPtr msg) {
    try {
      auto cv_ptr = cv_bridge::toCvCopy(msg, "bgr8");
      cv::Mat gray;
      cv::cvtColor(cv_ptr->image, gray, cv::COLOR_BGR2GRAY);
      auto out_msg = cv_bridge::CvImage(msg->header, encoding_, gray).toImageMsg();
      publisher_->publish(*out_msg);
    } catch (const cv_bridge::Exception &e) {
      RCLCPP_ERROR(this->get_logger(), "cv_bridge exception: %s", e.what());
    }
  }

  std::string encoding_;
  rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr subscriber_;
  rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr publisher_;
};

int main(int argc, char **argv) {
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<GrayscaleConverter>());
  rclcpp::shutdown();
  return 0;
}

工作流三:HarmonyOS 页面开发 + 幻觉防护

场景描述: 开发 HarmonyOS ArkTS 页面时,最大的挑战是 Claude 经常生成不存在的 API(幻觉)。通过 Skill + RAG(ChromaDB)+ Hook 的组合来解决这个问题。

架构设计:

用户需求
    │
    ▼
arkts-component Skill(约束 API 版本和语法规范)
    │
    ▼
Claude 生成初版代码
    │
    ▼
PostToolCall Hook 触发 API 验证脚本
    │
    ├── 调用 ChromaDB(存储了官方 SDK API 列表)验证每个 API 调用
    │
    ├── 发现幻觉 API → 注入修正信息到下一轮对话
    │
    └── 全部通过 → 格式化并提交

API 验证 Hook 脚本:

#!/usr/bin/env python3
# .claude/hooks/validate_arkts_api.py
"""
在每次 ArkTS 文件写入后,验证使用的 API 是否存在于官方 SDK 中
"""

import sys
import re
import chromadb

def validate_arkts_file(file_path: str) -> list[str]:
    """返回文件中使用的无效 API 列表"""
    if not file_path.endswith('.ets'):
        return []

    with open(file_path) as f:
        content = f.read()

    # 提取 API 调用模式(简化版)
    api_calls = re.findall(r'(\\w+)\\s*\\(', content)
    component_names = re.findall(r'(?:^|\\s)([A-Z]\\w+)\\s*\\(', content, re.MULTILINE)

    # 查询 ChromaDB
    client = chromadb.PersistentClient(path="/home/user/.arkts_api_db")
    collection = client.get_collection("harmony_api_12")

    invalid_apis = []
    for api in set(component_names):
        results = collection.query(
            query_texts=[api],
            n_results=1
        )
        if results['distances'][0][0] > 0.3:  # 相似度阈值
            invalid_apis.append(api)

    return invalid_apis

if __name__ == "__main__":
    file_path = sys.argv[1]
    invalid = validate_arkts_file(file_path)

    if invalid:
        # 向 Claude Code 注入反馈(写入特殊文件,Claude 会读取)
        with open(".claude/api_validation_errors.md", "w") as f:
            f.write(f"## ⚠️ API 验证警告\\n\\n")
            f.write(f"以下 API 在 HarmonyOS API 12 中未找到,可能是幻觉:\\n\\n")
            for api in invalid:
                f.write(f"- `{api}` — 请查阅官方文档确认正确 API 名称\\n")
        print(f"[Hook] 发现 {len(invalid)} 个可疑 API,已写入验证报告")
        sys.exit(1)  # 非零退出码会通知 Claude Code 有问题
    else:
        print("[Hook] API 验证通过 ✅")
        sys.exit(0)

八、Agent Teams:真正的多 Agent 并行

Agent Teams 是 Claude Code 在 2026 年初引入的实验性功能,允许多个完全独立的 Claude 实例(每个都有自己的完整上下文窗口)并行工作,并相互通信。这与 Subagent 的关键区别在于:Subagent 共享主 Agent 的生命周期,而 Agent Teams 中每个成员都是完全平等的独立进程。

启用方式:

CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 claude
# 或
claude --agent-teams

Agent Teams 适用的场景: 大型重构任务(前后端并行修改)、多模块同步测试、跨代码库的影响分析。

一个复杂的 Agent Teams 工作流示意:

用户: "重构整个认证系统,前端、后端、测试同步完成"

Lead Agent
├── 分解任务
├── 启动 Backend Agent
│   ├── 加载 backend-patterns Skill
│   ├── 修改 auth service、JWT 处理
│   └── 返回:已修改的 API 端点列表
├── 启动 Frontend Agent(并行)
│   ├── 加载 react-patterns Skill
│   ├── 等待 Backend Agent 的 API 列表(通过 SendMessage)
│   └── 更新前端的认证调用
├── 启动 Test Agent(等待前两个完成后)
│   ├── 加载 test-writing Skill
│   ├── 读取前两个 Agent 的修改报告
│   └── 编写端到端测试
└── 汇总所有结果,生成变更报告

九、概念对比速查表

概念 核心职责 持久性 适用场景
CLAUDE.md 项目记忆和全局约束 持久(每次对话加载) 团队规范、项目背景
MCP 连接外部系统和数据 持久(配置后长期有效) 数据库、API、SaaS 集成
Skill 可复用的专家指令集 持久(按需自动加载) 重复性专业任务
Slash Command 手动触发的工作流快捷键 持久(显式触发) 固定流程的启动入口
Subagent 隔离上下文的专项工作者 临时(任务完成即销毁) 防止上下文污染的复杂子任务
Hook 生命周期自动化 持久(事件驱动) 格式化、验证、通知
Agent Teams 真正的并行多 Agent 临时(任务完成即结束) 超大规模并行任务
Plugin 上述所有概念的打包单元 持久(安装后长期有效) 团队配置分发

十、决策流程图:我应该用什么?

我有一个新需求
        │
        ▼
需要访问外部系统(数据库/API/文件)?
        ├── YES → 配置 MCP Server
        └── NO → 继续
                    │
                    ▼
        这是需要重复使用的专业知识或流程?
                    ├── YES → 创建 Skill
                    └── NO → 继续
                                │
                                ▼
                这个任务会产生大量中间输出/需要独立权限?
                                ├── YES → 创建 Subagent
                                └── NO → 继续
                                            │
                                            ▼
                        需要在特定事件后自动执行某操作?
                                            ├── YES → 创建 Hook
                                            └── NO → 直接 prompt 即可

十一、快速上手行动清单

如果你是第一次系统化配置 Claude Code,建议按照以下顺序进行:

第一天:打好地基 在项目根目录创建 CLAUDE.md,写入项目结构、核心约束、构建命令和技术栈信息。花 30 分钟写好这个文件,可以在后续节省无数次重复解释的时间。

第二天:接入外部工具 根据你的技术栈,配置 1-2 个最常用的 MCP Server(GitHub 和数据库通常是优先级最高的)。验证 Claude 能通过 MCP 成功调用这些工具。

第三天:沉淀专业知识 把你团队里最重要的编码规范、代码模板、审查清单,整理成 1-3 个 Skill。优先选择那些你平时在 prompt 里反复解释的内容。

第四天:创建核心 Subagent 至少创建一个代码审查 Subagent 和一个测试编写 Subagent。这是 ROI 最高的两个 Subagent,几乎适用于所有工程项目。

第五天:串联工作流 用 Slash Command 把以上所有组件串联成 1-2 个高频使用的完整工作流(如 ship-feature)。添加必要的 Hook 实现自动化质量门禁。


总结

Claude Code 的真正威力不在于任何单一功能,而在于这五层架构的协同效应。MCP 决定了 Claude 能"看到"什么,给予其更好的于外界交互的能力,如果说大模型本身是一台电脑主机,那么MCP就是它的USB接口,让模型可以从外界获取资源以及调取需要的工具;Skill 决定了它"知道"怎么做,以及按照我们说的做;Subagent 让它能"并行"工作,相当于将一个主任务拆解为多个子任务分配给多个下属来完成;Hook 让流程"自动化",简单说,就是让这个过程变成流水线;CLAUDE.md 则是贯穿一切的"项目灵魂",一个比较粗糙的类比就是项目开发所必须遵循的原则。

当这些组件配合良好时,Claude Code 不再是一个聊天工具,而是一个真正融入你开发流水线的自动化工程师。它能自动发现适用的技能、调度专项 Agent、在你批准后执行修改、通过 Hook 保障质量,最终通过 MCP 将结果推送到你的版本控制系统。

这,才是生产级 AI 辅助开发应有的样子。


Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐