Claude Code Skills 实战:构建生产级 AI 编码 Agent 技能系统
2026年6月,前 Tesla AI 总监、OpenAI 创始成员 Andrej Karpathy 开源了一个看似简单的项目——Claude Code Skills 的示范仓库。短短一个月,该项目在 GitHub 斩获 149K Stars,成为 GitHub 历史增长最快的项目之一。这背后折射出的,是 AI 编码 Agent 正在从「玩具」走向「生产工具」的关键转折点。
引言:AI 编码 Agent 的技能危机
如果你在 2025 年用过 Claude Code、Cursor 或任何形式的 AI 编码助手,你可能已经注意到了一个核心矛盾:
模型越来越强,但输出质量越来越不可控。
同样的 Claude Sonnet 4,在 A 项目里能写出优雅的 Rust 异步代码,在 B 项目里却犯下低级的逻辑错误——把 Arc<Mutex<T>> 当成普通引用传递。问题不在模型,而在上下文:AI 不知道你的项目规范、不敢用你的工具链、不了解你的架构约束。
这就是 Skills 系统要解决的核心问题——给 AI Agent 注入可复用、可版本控制、可协作的结构化知识。
Karpathy 在他的项目中总结了一句话,值得每个 AI 辅助开发者深思:
"The difference between a junior and senior developer is not knowing the syntax — it's knowing the conventions, the pitfalls, and the 'why' behind every decision. Skills are how we teach that to AI."
本文将深入剖析 Claude Code Skills 的设计哲学、技术架构和工程实践,并通过完整可运行的代码示例,教你从零构建生产级 AI 编码 Agent 技能系统。
一、Karpathy 的 Claude Code Skills 现象解析
1.1 为什么是 Skills 而不是 Prompt?
在深入技术细节之前,我们需要厘清一个根本问题:Skills 和 Prompt 有什么区别?
传统 Prompt 工程的核心范式是「一次性指令注入」——你写一段系统提示,模型读取后生成响应。这种方式有三个致命缺陷:
| 问题 | 描述 | Skills 的解法 |
|---|---|---|
| 上下文污染 | 长 Prompt 占用大量 Token,挤压有效上下文 | 按需加载,条件注入 |
| 不可维护 | Prompt 改动影响全局,无法模块化 | 文件级隔离,独立版本控制 |
| 无法协作 | 团队成员各自维护 Prompt 副本 | 共享 Skill 仓库,Git 协作 |
Skills 系统的核心创新在于:将 AI 上下文管理从「黑盒咒语」变成「白盒工程」。
1.2 Karpathy 项目的技术亮点
Karpathy 的 Claude Code Skills 仓库(github.com/karpathy/claude-code-skills)虽然代码量不大,但设计极为精妙。其核心贡献可以归纳为三点:
① 标准化的 Skill 文件格式
每个 Skill 是一个独立的 Markdown 文件,遵循统一的元数据结构:
---
name: skill-name
description: What this skill does, when to use it
---
# Skill Content
Specific instructions, code examples, and constraints...
这种格式的优势在于:
- YAML Front Matter 可被程序解析(自动化加载)
- Markdown 正文对人类友好(可阅读、可编辑)
- 文件即技能(无需数据库,Git 原生支持)
② 条件加载机制
Skills 不是无条件全部注入的。Karpathy 的设计中,每个 Skill 的 description 字段实际上充当路由信号——Claude Code 根据当前任务描述,决定是否加载该 Skill。
这就像操作系统的按需分页(Demand Paging),只不过分页的单元是「技能」而不是「内存页」。
③ CLAUDE.md 作为技能编排层
CLAUDE.md 是 Claude Code 的专属配置文件(类似 Cursor 的 .cursorrules),Karpathy 用它来:
- 定义全局编码规范
- 声明可用 Skills 列表
- 设置项目特定的约束条件
这种分层设计使得「全局规范」和「专项技能」解耦,符合软件工程的关注点分离原则。
1.3 数据说话:为什么 149K Stars 不是偶然
让我们看几组数字:
- Claude Code 的月活开发者:2026年5月突破 200 万
- Skills 生态的 GitHub 仓库数:截至2026年7月,仅
claude-code-skills相关仓库超过 2400 个 - 企业采用率:据 San Francisco AI Engineer Survey 2026,37% 的 AI 编码团队正在评估或已采用 Skills 系统
这背后是一个正在形成的新职业角色:AI Agent 技能工程师(AI Agent Skill Engineer)——专门负责为编码 Agent 设计、测试和维护技能模块。
二、Claude Code Skills 核心架构深度剖析
要真正掌握 Skills 系统,我们需要深入理解其技术架构。这一节将从文件结构、加载机制、注入流程三个维度进行剖析。
2.1 标准项目文件结构
一个生产级 Claude Code Skills 项目的标准结构如下:
your-project/
├── CLAUDE.md # 全局配置与技能编排(核心入口)
├── .claude/
│ ├── skills/ # Skills 目录
│ │ ├── code-review.md # 代码审查技能
│ │ ├── testing.md # 测试编写技能
│ │ ├── security-audit.md # 安全审计技能
│ │ └── _shared/ # 跨技能共享片段
│ │ └── conventions.md
│ ├── commands/ # 自定义斜杠命令
│ │ ├── /review # 触发代码审查
│ │ └── /test # 触发测试生成
│ └── settings.json # Claude Code 本地配置
├── src/ # 你的业务代码
└── README.md
设计要点:
.claude/skills/目录是约定俗成的技能存放位置,Claude Code 会自动扫描此目录CLAUDE.md必须放在项目根目录,这是 Claude Code 的硬性规定commands/目录定义自定义交互命令,用户可以通过/review这样的命令触发特定技能
2.2 CLAUDE.md 的深度配置
CLAUDE.md 是整个技能系统的编排层,它的质量直接决定 AI Agent 的输出质量。以下是一个生产级 CLAUDE.md 的完整示例:
# CLAUDE.md - Project Instructions for Claude Code
## Project Overview
This is a Rust-based microservice for real-time payment processing.
Tech stack: Rust + Axum + SQLx + Redis + Kafka
## Coding Standards (Non-Negotiable)
- All async code MUST use `tokio` with `async/await` (no `std::thread`)
- Error handling MUST use `thiserror` for library code, `anyhow` for application code
- All database queries MUST go through SQLx with compile-time checking
- No `unwrap()` in production code — use `?` or explicit match
- All public functions MUST have doc comments with examples
## Architecture Constraints
- Domain logic goes in `src/domain/`, NEVER in handlers
- All external calls MUST go through `src/adapters/`
- Configuration MUST be loaded via `config.rs` with `dotenvy`
- Feature flags MUST be used for experimental features
## Available Skills
The following skills are available and should be loaded contextually:
1. **code-review** (`/.claude/skills/code-review.md`)
- Load when: Reviewing PRs, refactoring code, or before committing
- Contains: Review checklist, common Rust anti-patterns, performance heuristics
2. **testing** (`/.claude/skills/testing.md`)
- Load when: Writing tests, fixing failing tests, or measuring coverage
- Contains: Test patterns, mock strategies, property-based testing guide
3. **security-audit** (`/.claude/skills/security-audit.md`)
- Load when: Handling user input, auth logic, or crypto operations
- Contains: OWASP checklist, common Rust security pitfalls
## Loading Rules
- Always load `code-review` skill before suggesting any code changes
- Load `testing` skill when test files are modified
- Load `security-audit` skill when dealing with `src/auth/` or `src/payment/`
## Commit Rules
- Commit messages MUST follow Conventional Commits
- NEVER commit secrets, API keys, or `.env` files
- Run `cargo clippy` and `cargo fmt` before every commit
## Known Pitfalls (Project-Specific)
- The `PaymentProcessor` struct in `src/domain/payment.rs` has a known issue
with concurrent idempotency keys — always use the `IdempotencyGuard` adapter
- Kafka consumer group rebalancing can cause duplicate processing —
see `docs/kafka-idempotency.md` for the workaround
这个 CLAUDE.md 的精妙之处:
- 架构约束明确:直接告诉 AI 「domain logic 不能进 handler」,从规范层面防止架构腐化
- 技能加载规则具体:不是模糊的「可用技能列表」,而是「什么时候加载什么技能」
- 已知坑点文档化:把团队的经验教训固化成 AI 可理解的格式,避免重复踩坑
2.3 Skill 文件的内部结构与注入机制
一个高质量的 Skill 文件,其内部结构应当遵循「指令-示例-约束」三段式:
---
name: code-review
description: Load when reviewing Rust code changes. Provides systematic review checklist and common anti-pattern detection.
---
# Code Review Skill
## When to Load This Skill
Load this skill when:
- User asks for code review
- About to commit changes
- Refactoring existing code
- PR title contains "review" or "refactor"
## Review Checklist
### 1. Correctness
- [ ] Is the logic correct for all edge cases?
- [ ] Are error cases handled explicitly?
- [ ] Is there any potential for panic (unwrap, expect)?
**How to check**: Look for `.unwrap()` calls — they should be replaced with `?` or proper error handling.
```rust
// ❌ BAD
let value = hash_map.get(&key).unwrap();
// ✅ GOOD
let value = hash_map.get(&key)
.ok_or_else(|| anyhow!("Key not found: {:?}", key))?;
2. Performance
- Unnecessary clones? (Use
&strinstead ofStringwhere possible) - Heap allocations in hot paths?
- O(n²) algorithms on large datasets?
Common Rust Performance Anti-Patterns:
// ❌ BAD: Allocates a new String for every iteration
for s in string_list {
do_something(s.to_string()); // to_string() allocates
}
// ✅ GOOD: Borrow instead
for s in string_list {
do_something(s); // s is already &str
}
3. Idiomatic Rust
- Using
clippysuggestions? - Proper use of
From/Intotraits? -
matchinstead of chain ofif-let?
Project-Specific Rules
- In this project, all DB types must implement
sqlx::FromRow - Service structs must be
Clone + Send + Sync - No
panic!in async contexts — useResultand propagate
Output Format
When invoked, produce a review in this format:
Summary
One-line assessment: ✅ LGTM / ⚠️ Needs Changes / ❌ Blocking Issues
Issues Found
For each issue:
- File: path/to/file.rs:L42
- Severity: Critical / Major / Minor / Suggestion
- Category: Correctness / Performance / Idiom / Security
- Current Code: (code snippet)
- Suggested Fix: (code snippet)
**注入机制的技术细节**:
Claude Code 在处理用户消息时,会执行以下步骤:
1. **解析 `CLAUDE.md`**:提取技能列表和加载规则
2. **匹配当前上下文**:根据用户消息和当前文件,决定是否加载某个 Skill
3. **注入 Skill 内容**:将匹配的 Skill 文件内容注入到上下文窗口
4. **执行任务**:基于注入的技能指令完成任务
这里的**关键技术挑战**是上下文窗口管理——如果同时加载 10 个 Skill,每个 2000 Token,就占用了 20000 Token,可能挤掉实际的代码上下文。因此,**条件加载不是优化,是必要**。
---
## 三、从零构建生产级 Skills:完整实战
这一节将通过构建一个完整的 Skills 系统,展示从设计到部署的全流程。我们的目标是一个**微服务开发辅助 Skills 套件**,包含代码生成、测试、审查、部署四个技能模块。
### 3.1 项目初始化与目录结构
```bash
# 初始化项目
mkdir -p rust-microservice-skills && cd rust-microservice-skills
git init
# 创建目录结构
mkdir -p .claude/skills .claude/commands
mkdir -p src/{handlers,domain,adapters,config}
mkdir -p tests docs
# 创建基础文件
touch CLAUDE.md .claude/settings.json
touch src/lib.rs src/main.rs
3.2 编写第一个 Skill:api-design.md
我们先从最常用的场景入手——设计符合 Rust 惯用法的 Axum 异步 API。
---
name: api-design
description: Load when creating or modifying Axum HTTP handlers. Provides Rust async API design patterns, error handling strategies, and Axum best practices.
---
# API Design Skill for Axum + Rust
## Design Principles
### 1. Handler 函数签名规范
所有 Handler 必须满足:
- 输入:Extractors(不能手动反序列化)
- 输出:`Result<Response, AppError>`(统一错误类型)
- 异步:`async fn` with `tokio`
```rust
// ✅ CANONICAL HANDLER PATTERN
use axum::{
extract::{Path, Json, State},
http::StatusCode,
response::Json as JsonResponse,
};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
pub struct CreateUserRequest {
pub email: String,
pub name: String,
}
#[derive(Serialize)]
pub struct CreateUserResponse {
pub id: uuid::Uuid,
pub email: String,
}
#[derive(Clone)]
pub struct AppState {
pub db: sqlx::PgPool,
pub redis: redis::Client,
}
/// Create a new user
///
/// # Errors
/// - 409: Email already exists
/// - 400: Invalid email format
/// - 500: Database error
pub async fn create_user_handler(
State(state): State<AppState>,
Json(req): Json<CreateUserRequest>,
) -> Result<JsonResponse<CreateUserResponse>, AppError> {
// Validate input
if !is_valid_email(&req.email) {
return Err(AppError::BadRequest("Invalid email format".into()));
}
// Check duplicate
let existing = sqlx::query!(
"SELECT id FROM users WHERE email = $1",
req.email
)
.fetch_optional(&state.db)
.await?;
if existing.is_some() {
return Err(AppError::Conflict("Email already registered".into()));
}
// Create user
let user_id = uuid::Uuid::new_v4();
sqlx::query!(
"INSERT INTO users (id, email, name) VALUES ($1, $2, $3)",
user_id,
req.email,
req.name
)
.execute(&state.db)
.await?;
Ok(JsonResponse(CreateUserResponse {
id: user_id,
email: req.email,
}))
}
2. 统一错误类型设计
// src/error.rs
use axum::response::{Response, IntoResponse};
use axum::http::StatusCode;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Bad request: {0}")]
BadRequest(String),
#[error("Not found: {0}")]
NotFound(String),
#[error("Conflict: {0}")]
Conflict(String),
#[error("Internal error: {0}")]
Internal(#[from] anyhow::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self {
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
AppError::Conflict(msg) => (StatusCode::CONFLICT, msg),
AppError::Internal(err) => {
tracing::error!("Internal error: {:?}", err);
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".into())
}
};
(status, axum::Json(serde_json::json!({
"error": message,
"code": status.as_u16()
}))).into_response()
}
}
// Implement From<sqlx::Error> for AppError
impl From<sqlx::Error> for AppError {
fn from(err: sqlx::Error) -> Self {
match err {
sqlx::Error::RowNotFound => AppError::NotFound("Resource not found".into()),
_ => AppError::Internal(anyhow::anyhow!("Database error: {}", err)),
}
}
}
3. 中间件模式
// src/middleware.rs
use axum::{
extract::Request,
middleware::Next,
response::Response,
};
use tower_http::trace::TraceLayer;
use tracing::Span;
/// Request ID middleware — adds unique ID to every request for tracing
pub async fn request_id_middleware(
mut request: Request,
next: Next,
) -> Response {
let request_id = uuid::Uuid::new_v4().to_string();
request.extensions_mut().insert(RequestId(request_id.clone()));
// Add request_id to tracing span
let span = tracing::info_span!("request", %request_id);
let _guard = span.enter();
let response = next.run(request).await;
response
}
#[derive(Clone)]
struct RequestId(String);
/// Tracing layer with custom span for Axum
pub fn create_trace_layer() -> TraceLayer {
TraceLayer::new_for_http()
.make_span_with(|request: &Request<_>| {
let method = request.method();
let uri = request.uri();
tracing::info_span!("http_request", %method, %uri)
})
.on_response(|response: &Response<_>, latency: std::time::Duration, _span: &Span| {
let status = response.status().as_u16();
tracing::info!(%status, ?latency, "response sent");
})
}
4. 路由组织模式
// src/routes.rs
use axum::{Router, routing::{get, post, put, delete}};
pub fn create_router(state: AppState) -> Router {
Router::new()
// Health check (no auth required)
.route("/health", get(health_check))
// User routes
.route("/users", post(create_user_handler))
.route("/users/:id", get(get_user_handler))
.route("/users/:id", put(update_user_handler))
.route("/users/:id", delete(deactivate_user_handler))
// Nested API v2
.nest("/api/v2", api_v2_routes())
// With state
.with_state(state)
}
fn api_v2_routes() -> Router<AppState> {
Router::new()
.route("/users", post(v2::create_user))
.route("/analytics", get(v2::get_analytics))
}
Common Pitfalls to Avoid
Pitfall 1: Blocking in async context
// ❌ BAD: Blocking the tokio runtime
pub async fn bad_handler() -> String {
// This blocks the entire runtime!
std::thread::sleep(std::time::Duration::from_secs(1));
"done".into()
}
// ✅ GOOD: Use tokio::time::sleep
pub async fn good_handler() -> String {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
"done".into()
}
Pitfall 2: Not using typed SQLx queries
// ❌ BAD: Runtime query building (no compile-time check)
sqlx::query(&format!("SELECT * FROM users WHERE id = {}", user_id))
.fetch_one(&pool)
.await?;
// ✅ GOOD: Compile-time checked queries
sqlx::query!(
"SELECT id, email, name FROM users WHERE id = $1",
user_id
)
.fetch_one(&pool)
.await?;
Pitfall 3: Forgetting to handle connection pool exhaustion
// Add to AppState creation
pub async fn create_app_state(config: &Config) -> Result<AppState, AppError> {
let db = sqlx::postgres::PgPoolOptions::new()
.max_connections(20) // ← Don't forget this!
.connect_timeout(std::time::Duration::from_secs(5))
.connect(&config.database_url)
.await?;
Ok(AppState { db, .. })
}
### 3.3 编写测试技能:`testing.md`
测试是 Rust 项目的生命线,但 AI 生成的测试往往质量低下——缺少边界条件、不测试并发、忽略属性测试。这个 Skill 解决这些问题。
```markdown
---
name: testing
description: Load when writing or fixing tests. Provides Rust testing patterns including unit tests, integration tests, property-based testing with proptest, and mock strategies.
---
# Testing Skill for Rust Projects
## Testing Strategy Overview
Our testing pyramid:
- **Unit tests** (70%): Fast, isolated, test pure logic
- **Integration tests** (20%): Test API contracts and DB interactions
- **Property tests** (10%): Find edge cases via randomized inputs
## 1. Unit Test Patterns
### Basic Pattern
```rust
// In src/domain/payment.rs
pub struct PaymentProcessor {
idempotency_store: Arc<dyn IdempotencyStore>,
}
impl PaymentProcessor {
/// Process a payment with idempotency guarantee
pub async fn process(
&self,
payment: Payment,
idempotency_key: &str,
) -> Result<PaymentResult, PaymentError> {
// Check idempotency
if let Some(cached) = self.idempotency_store
.get(idempotency_key)
.await?
{
return Ok(cached);
}
// Process payment...
let result = self.do_process(payment).await?;
// Cache result
self.idempotency_store
.set(idempotency_key, &result)
.await?;
Ok(result)
}
}
// ✅ UNIT TESTS in the same file (Rust convention)
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
// Mock store for testing
struct MockIdempotencyStore {
store: std::sync::Mutex<HashMap<String, PaymentResult>>,
}
impl MockIdempotencyStore {
fn new() -> Self {
Self { store: HashMap::new() }
}
}
#[async_trait]
impl IdempotencyStore for MockIdempotencyStore {
async fn get(&self, key: &str) -> Result<Option<PaymentResult>, StoreError> {
Ok(self.store.lock().unwrap().get(key).cloned())
}
async fn set(&self, key: &str, result: &PaymentResult) -> Result<(), StoreError> {
self.store.lock().unwrap().insert(key.to_string(), result.clone());
Ok(())
}
}
#[tokio::test]
async fn test_idempotent_payment() {
// Arrange
let store = Arc::new(MockIdempotencyStore::new());
let processor = PaymentProcessor { idempotency_store: store.clone() };
let payment = Payment::new(100, "USD");
let key = "test-key-123";
// Act: First call
let result1 = processor.process(payment.clone(), key).await.unwrap();
// Act: Second call with same key
let result2 = processor.process(payment.clone(), key).await.unwrap();
// Assert: Results are identical (idempotent)
assert_eq!(result1.transaction_id, result2.transaction_id);
}
#[tokio::test]
async fn test_payment_validation_rejects_negative_amount() {
let processor = create_test_processor();
let payment = Payment::new(-100, "USD"); // Invalid!
let result = processor.process(payment, "key-1").await;
assert!(matches!(result, Err(PaymentError::InvalidAmount(_))));
}
}
2. Integration Test Pattern
// In tests/api_test.rs (separate file for integration tests)
use axum::body::Body;
use axum::http::{Request, StatusCode};
use tower::ServiceExt; // for `oneshot`
#[sqlx::test]
async fn test_create_user_api(pool: sqlx::PgPool) {
// Arrange: Setup app with test pool
let state = AppState { db: pool };
let app = create_router(state);
let payload = serde_json::json!({
"email": "test@example.com",
"name": "Test User"
});
// Act: Send request to the router (no server needed!)
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/users")
.header("content-type", "application/json")
.body(Body::from(payload.to_string()))
.unwrap()
)
.await
.unwrap();
// Assert
assert_eq!(response.status(), StatusCode::CREATED);
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert_eq!(json["email"], "test@example.com");
assert!(json["id"].is_string()); // UUID present
}
#[sqlx::test]
async fn test_create_user_duplicate_email(pool: sqlx::PgPool) {
let state = AppState { db: pool };
let app = create_router(state);
// Create first user
let payload = serde_json::json!({
"email": "dup@example.com",
"name": "User One"
});
app.clone()
.oneshot(create_post_request("/users", &payload))
.await
.unwrap();
// Try to create second user with same email
let response = app
.oneshot(create_post_request("/users", &payload))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::CONFLICT);
}
3. Property-Based Testing with Proptest
// In src/domain/amount.rs
/// A verified non-negative payment amount
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PaymentAmount(f64);
impl PaymentAmount {
pub fn new(amount: f64) -> Result<Self, PaymentError> {
if amount < 0.0 {
return Err(PaymentError::InvalidAmount("Amount cannot be negative".into()));
}
if amount > 1_000_000.0 {
return Err(PaymentError::InvalidAmount("Amount exceeds maximum".into()));
}
// Check for NaN/Infinity
if !amount.is_finite() {
return Err(PaymentError::InvalidAmount("Invalid amount value".into()));
}
Ok(PaymentAmount(amount))
}
pub fn value(self) -> f64 {
self.0
}
}
// ✅ PROPERTY TEST: Test invariants hold for ALL valid inputs
#[cfg(test)]
mod prop_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn amount_always_non_negative(amount in 0.0..1_000_000.0f64) {
let result = PaymentAmount::new(amount);
prop_assert!(result.is_ok());
prop_assert!(result.unwrap().value() >= 0.0);
}
#[test]
fn negative_amount_always_rejected(
amount in f64::NEG_INFINITY..0.0 // All negative numbers
) {
let result = PaymentAmount::new(amount);
prop_assert!(result.is_err());
}
#[test]
fn large_amount_always_rejected(
amount in 1_000_000.01..f64::INFINITY
) {
let result = PaymentAmount::new(amount);
prop_assert!(result.is_err());
}
// Invariant: Creating amount, then calling value(), gives original
#[test]
fn value_roundtrip(amount in 0.0..1_000_000.0f64) {
let amt = PaymentAmount::new(amount).unwrap();
prop_assert!((amt.value() - amount).abs() < f64::EPSILON);
}
}
}
4. Test Coverage Configuration
# In Cargo.toml
[dev-dependencies]
tarpaulin = "0.27" # For coverage (CI only)
# Run coverage
# cargo tarpaulin --out Html --output-dir coverage/
Checklist Before Committing Tests
- Every
pub fnhas at least one unit test - Error paths are tested (not just happy path)
- Concurrent code is tested with
tokio::join!ortokio::spawn - Property tests cover boundary values (0, MAX, NaN, Infinity)
- Mocks implement the same traits as production code
### 3.4 技能协同:让多个 Skills 一起工作
单个 Skill 的价值有限,**技能协同**才是生产力爆发点。在 `CLAUDE.md` 中定义技能之间的协作规则:
```markdown
# In CLAUDE.md
## Skill Orchestration Rules
### When user says "implement [feature]":
1. Load `api-design` skill → Generate the handler
2. Load `testing` skill → Generate tests for the handler
3. Run tests → If fail, load `debugging` skill
4. Load `code-review` skill → Final review before commit
### When user says "refactor [module]":
1. Load `code-review` skill → Understand current code
2. Load `testing` skill → Ensure tests exist before refactoring
3. Refactor + run tests → Ensure no regression
4. Load `performance` skill → Check for optimization opportunities
### Skill Conflict Resolution
- If `api-design` and `performance` skills give conflicting advice:
- Prioritize correctness (api-design) over performance
- Only optimize if benchmark proves it's necessary
- If `testing` and `code-review` disagree on test strategy:
- Follow `testing` skill for test implementation
- Follow `code-review` skill for test coverage assessment
四、高级技能设计模式
当你掌握了基础 Skills 编写后,可以进一步探索高级模式。这些模式是生产环境中经过验证的最佳实践。
4.1 条件注入模式(Conditional Injection Pattern)
问题:有些技能内容只在特定条件下有用,但无条件注入会浪费 Token。
解法:在 Skill 文件中使用「触发条件」标记,让 Claude Code 根据条件决定是否注入完整内容。
---
name: kubernetes-deploy
description: Load when deploying to K8s. Contains K8s manifests, Helm values, and deployment runbooks.
conditions:
- "user mentions deploy/staging/production"
- "file modified matches *.k8s.yaml or helm/**"
- "git branch is develop or main"
---
# Kubernetes Deployment Skill
> **NOTE TO CLAUDE**: Only load the sections below that match the current task.
> If user is only asking about staging deploy, skip the Production section.
## Staging Deployment
### Pre-deployment Checks
1. Run `cargo audit` — no known vulnerabilities
2. Run `cargo clippy -- -D warnings` — no lint warnings
3. Ensure `STAGING_DB_URL` is set in GitHub Secrets
### Deploy Command
```bash
# Deploy to staging
helm upgrade --install payment-svc ./helm \
--namespace staging \
--set image.tag=${GITHUB_SHA::7} \
--set environment=staging \
--dry-run # Always do dry-run first!
Production Deployment (Load ONLY when explicitly asked)
Additional Requirements
- PR must be approved by 2 reviewers
- Must include database migration rollback plan
- Deploy during low-traffic window (02:00-04:00 UTC)
Production Helm Values (Override)
# helm/values-production.yaml
replicaCount: 5 # Higher availability
resources:
limits:
memory: 2Gi
cpu: 2000m
Rollback Procedure
If deployment fails health check:
helm rollback payment-svc --namespace production
### 4.2 工具链编排模式(Toolchain Orchestration Pattern)
**问题**:AI 知道该做什么,但不知道如何用你的具体工具链做。
**解法**:在 Skill 中嵌入完整的工具调用序列,包括 `cargo` 命令、`curl` 请求、甚至 `kubectl` 操作。
```markdown
---
name: local-dev-setup
description: Load when setting up local dev environment or debugging local issues.
---
# Local Development Setup Skill
## First-Time Setup (Run once)
```bash
# 1. Install Rust toolchain (specific version)
rustup install 1.82.0
rustup default 1.82.0
# 2. Install required tools
cargo install cargo-watch # Auto-restart on file change
cargo install cargo-audit # Security vulnerability scanner
cargo install sqlx-cli # Database migration tool
# 3. Setup pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
cargo fmt --check && cargo clippy -- -D warnings && cargo test
EOF
chmod +x .git/hooks/pre-commit
# 4. Start local infrastructure (Docker)
docker run -d --name local-postgres \
-e POSTGRES_PASSWORD=localdev \
-e POSTGRES_DB=payment_local \
-p 5432:5432 \
postgres:16-alpine
docker run -d --name local-redis \
-p 6379:6379 \
redis:7-alpine
# 5. Run migrations
sqlx migrate run --database-url postgres://postgres:localdev@localhost:5432/payment_local
# 6. Load test data
psql postgres://postgres:localdev@localhost:5432/payment_local < tests/fixtures/test_data.sql
Development Workflow
Start development server (with auto-reload)
cargo watch -x 'run --bin payment-service'
Run specific test file
cargo test --test api_test -- --nocapture
Check what the AI changed
# Good practice: Always review AI-generated code
git diff --staged
Debugging Common Issues
Issue: "connection refused" to database
# Check if Postgres is running
docker ps | grep local-postgres
# If not, start it
docker start local-postgres
# Check logs
docker logs local-postgres
Issue: sqlx compile error "database URL not set"
# Ensure .env file exists
cat > .env << EOF
DATABASE_URL=postgres://postgres:localdev@localhost:5432/payment_local
REDIS_URL=redis://localhost:6379
RUST_LOG=debug
EOF
# Source it
export $(cat .env | xargs)
### 4.3 上下文压缩模式(Context Compression Pattern)
**问题**:当项目变大,完整的 `CLAUDE.md` + 所有 Skills 会超出上下文窗口。
**解法**:使用「分层注入」——先注入索引,再按需注入细节。
```markdown
---
name: project-map
description: Always load. Provides a compressed project overview for navigation.
---
# Project Map (Compressed Context)
## Directory Structure (One-Line Descriptions)
- `src/domain/` — Pure business logic, no framework dependencies
- `src/adapters/` — External service integrations (DB, Redis, Kafka)
- `src/handlers/` — Axum HTTP handlers (thin, delegate to domain)
- `src/config.rs` — Configuration loading from env + .env
- `migrations/` — SQLx database migrations
- `helm/` — Kubernetes Helm charts
- `tests/` — Integration tests
## Key Types (For Quick Reference)
- `PaymentId` (uuid::Uuid) — Unique payment identifier
- `PaymentAmount` (f64 wrapper) — Validated non-negative amount
- `IdempotencyKey` (String) — Prevents duplicate processing
- `AppError` (enum) — All possible errors, implements IntoResponse
## Critical Constraints (Must Remember)
1. All handlers must use `State<AppState>` extractor
2. No raw SQL — always use `sqlx::query!` macro
3. All async functions must be `Send + Sync`
4. Kafka messages must be idempotent (check `IdempotencyKey`)
## When You Need More Details
- For domain logic details: Read `src/domain/README.md`
- For API contract details: Read `docs/api-contract.md`
- For deployment details: Load `kubernetes-deploy` skill
- For testing strategy: Load `testing` skill
这个 Skill 只有约 30 行,但提供了足够的「导航信息」,让 AI 知道去哪里找更详细的信息——就像一个微型索引。
五、与 Cursor / Windsurf / Devin 的技能系统对比
Skills 系统不是 Claude Code 独有的概念。让我们对比主流 AI 编码工具的技能/上下文管理机制:
| 工具 | 技能系统名称 | 核心机制 | 优势 | 劣势 |
|---|---|---|---|---|
| Claude Code | CLAUDE.md + Skills | 文件级 Markdown 注入 | 简单、透明、Git 原生 | 无官方 Skill 市场 |
| Cursor | .cursorrules + Rules | 全局/项目级规则注入 | 支持通配符文件匹配 | 规则是黑盒,不可调试 |
| Windsurf | Cascade Memories | 自动记忆重要上下文 | 低维护成本 | 记忆质量不稳定 |
| Devin | Knowledge Base | 云端知识库 | 团队协作友好 | 闭源,不可本地编辑 |
| GitHub Copilot | Prompt File (Preview) | 类似 Skills 的文件注入 | 与 GitHub 深度集成 | 功能尚不完善 |
Claude Code Skills 的独特优势:
- 完全本地化:所有 Skill 文件在本地 Git 仓库中,可以 code review、可以 PR、可以 CI/CD
- 可调试:你可以直接编辑
CLAUDE.md,下次对话立即生效——不需要重新训练模型 - 可组合:Skills 是纯文本文件,可以用任何模板引擎生成(比如根据 OpenAPI spec 自动生成 API 调用 Skill)
六、性能优化:让 AI 编码 Agent 快起来的技巧
用了 Skills 之后,你可能会发现 AI 变慢了——因为它要处理更多上下文。这一节介绍优化技巧。
6.1 Token usage 分析
先测量,再优化。Claude Code 的 Token 消耗主要来自:
Total Token Budget (200k for Claude Sonnet 4):
├── System Prompt (固定) ~3,000
├── CLAUDE.md ~2,000
├── Loaded Skills ~5,000-15,000
├── Conversation History ~10,000-50,000
├── Current File(s) ~2,000-10,000
└── Remaining for Output ~20,000-50,000
优化目标:把 Loaded Skills 控制在 5,000 Token 以内。
6.2 技能精简技巧
技巧1:删除「显而易见」的内容
<!-- ❌ BAD: Wasting tokens on obvious things -->
## What is Rust?
Rust is a systems programming language that is fast and safe...
<!-- ✅ GOOD: Assume the reader (AI) knows Rust -->
## Rust Version Requirements
- Edition 2021 or later
- rustc >= 1.82.0 (for `impl Trait` in return position)
技巧2:用代码代替文字描述
<!-- ❌ BAD: 100 words to describe a pattern -->
When handling errors in Rust, you should never use unwrap() because
it can cause panics in production. Instead, you should use the ? operator
to propagate errors to the caller...
<!-- ✅ GOOD: 10 lines of code say more -->
```rust
// ❌ BAD
let value = hash_map.get(&key).unwrap();
// ✅ GOOD
let value = hash_map.get(&key)
.ok_or_else(|| AppError::NotFound("Key not found".into()))?;
**技巧3:使用「参见」模式**
```markdown
## Database Patterns
For complete database patterns, see:
- `src/domain/README.md#database-patterns` (detailed examples)
- `migrations/README.md` (migration conventions)
Quick reference:
- Always use `sqlx::query!` (compile-time check)
- Always wrap in transaction for multi-table updates
6.3 上下文窗口管理策略
对于大型项目,即使精简了 Skills,上下文仍然可能溢出。此时需要主动上下文管理:
# In CLAUDE.md
## Context Management Strategy
### High-Priority Context (Always Load)
- This section (CLAUDE.md project overview)
- `src/domain/types.rs` (core type definitions)
- Current file being edited
### Medium-Priority Context (Load on Demand)
- Related test files
- Related adapter implementations
- Skill files matching current task
### Low-Priority Context (Reference Only)
- Full project history
- Deprecated modules
- Third-party integration details
### Context Eviction Rules
When context window is >70% full:
1. Remove conversation history older than 10 turns
2. Keep only the current file + immediate dependencies
3. Summarize (don't delete) low-priority context
七、真实案例:用 Skills 重构一个微服务
理论讲完了,这一节展示一个完整的实战案例:用 Skills 辅助重构一个支付微服务。
7.1 初始状态(问题代码)
假设我们有一个运行了 2 年的支付服务,代码已经部分腐化:
// src/handlers/payment.rs (BEFORE - The "bad" version)
use actix_web::{web, HttpResponse, Responder};
pub async fn process_payment(
req: web::Json<serde_json::Value>,
db: web::Data<sqlx::PgPool>,
) -> impl Responder {
let amount = req.get("amount").unwrap().as_f64().unwrap();
let user_id = req.get("user_id").unwrap().as_str().unwrap();
// Direct SQL — no compile-time check!
let result = sqlx::query("INSERT INTO payments (user_id, amount) VALUES ($1, $2)")
.bind(user_id)
.bind(amount)
.execute(db.get_ref())
.await;
match result {
Ok(_) => HttpResponse::Ok().json(serde_json::json!({"status": "ok"})),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
问题清单:
- 手动 JSON 解析(不安全)
unwrap()会导致 panic- 字符串拼接 SQL(虽然用了参数化,但没有编译时检查)
- 错误处理粗糙(直接返回 error string)
- 没有输入验证
- 没有幂等性保证
7.2 用 Skills 辅助重构(AFTER)
在配置了完整 Skills 的 Claude Code 中,我们只需要说:
"Refactor
src/handlers/payment.rsto follow our project conventions. Load theapi-designandtestingskills."
Claude Code(在 Skills 指导下)会生成:
// src/handlers/payment.rs (AFTER - The "good" version)
use axum::{
extract::{State, Json},
http::StatusCode,
response::Json as JsonResponse,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Deserialize)]
pub struct ProcessPaymentRequest {
pub user_id: Uuid,
pub amount: f64,
pub idempotency_key: String, // ← New: idempotency support
}
#[derive(Serialize)]
pub struct ProcessPaymentResponse {
pub payment_id: Uuid,
pub status: String,
pub amount: f64,
}
#[derive(Clone)]
pub struct AppState {
pub db: sqlx::PgPool,
pub idempotency_store: RedisPool,
}
/// Process a payment with idempotency guarantee
pub async fn process_payment_handler(
State(state): State<AppState>,
Json(req): Json<ProcessPaymentRequest>,
) -> Result<JsonResponse<ProcessPaymentResponse>, AppError> {
// Input validation (from api-design skill)
if req.amount <= 0.0 {
return Err(AppError::BadRequest("Amount must be positive".into()));
}
if req.amount > 1_000_000.0 {
return Err(AppError::BadRequest("Amount exceeds maximum".into()));
}
// Idempotency check (from business logic skill)
if let Some(cached) = state.idempotency_store
.get(&format!("payment:{}", req.idempotency_key))
.await?
{
let response: ProcessPaymentResponse = serde_json::from_str(&cached)?;
return Ok(JsonResponse(response));
}
// Process payment in transaction
let payment_id = Uuid::new_v4();
let result = sqlx::transaction(&state.db, |tx| {
Box::pin(async move {
// Compile-time checked query (from api-design skill)
sqlx::query!(
"INSERT INTO payments (id, user_id, amount, status)
VALUES ($1, $2, $3, $4)",
payment_id,
req.user_id,
req.amount,
"pending"
)
.execute(&mut *tx)
.await?;
Ok(())
})
})
.await?;
let response = ProcessPaymentResponse {
payment_id,
status: "pending".into(),
amount: req.amount,
};
// Cache idempotency result
state.idempotency_store
.set(
&format!("payment:{}", req.idempotency_key),
&serde_json::to_string(&response)?,
)
.await?;
Ok(JsonResponse(response))
}
// Corresponding unit tests (generated by testing skill)
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_process_payment_success() {
let state = create_test_state().await;
let req = ProcessPaymentRequest {
user_id: Uuid::new_v4(),
amount: 99.99,
idempotency_key: "test-key".into(),
};
let result = process_payment_handler(State(state), Json(req)).await;
assert!(result.is_ok());
let response = result.unwrap();
assert_eq!(response.amount, 99.99);
}
#[tokio::test]
async fn test_process_payment_idempotent() {
let state = create_test_state().await;
let req = ProcessPaymentRequest {
user_id: Uuid::new_v4(),
amount: 50.0,
idempotency_key: "idem-key".into(),
};
// First call
let r1 = process_payment_handler(State(state.clone()), Json(req.clone())).await.unwrap();
// Second call — should return same result
let r2 = process_payment_handler(State(state), Json(req)).await.unwrap();
assert_eq!(r1.0.payment_id, r2.0.payment_id);
}
}
重构差异对比:
| 维度 | BEFORE | AFTER |
|---|---|---|
| 输入验证 | 无 | 类型安全 + 业务规则 |
| 错误处理 | unwrap() + 裸字符串 | AppError 枚举 + IntoResponse |
| SQL 安全 | 运行时拼 SQL | 编译时检查 sqlx::query! |
| 幂等性 | 无 | IdempotencyKey + Redis 缓存 |
| 测试 | 无 | 单元测试 + 幂等性测试 |
| 代码行数 | 25 行 | 120 行(但质量天差地别) |
八、开源生态与 Skill 市场
随着 Skills 系统的普及,开源社区正在形成一个技能生态。以下是值得关注的项目和趋势:
8.1 优质开源 Skills 仓库
karpathy/claude-code-skills
Karpathy 的原作,包含通用编程技能模板anthropics/claude-code-skills-official
Anthropic 官方的 Skills 示例集合(2026年6月发布)rust-skills/axum-patterns
专注于 Axum Web 开发的 Skill 集合go-skills/gin-best-practices
Go + Gin 框架的 Skills 套件
8.2 Skill 描述语言标准化尝试
社区正在推动 Skill Description Language (SDL) 的标准化——一种 YAML/JSON 格式的 Skill 描述规范,使得 Skills 可以在不同 AI 编码工具之间移植。
# skill-manifest.yaml (proposed standard)
name: api-design
version: 1.2.0
description: Axum API design patterns for Rust
authors:
- "Your Name <you@example.com>"
compatible_tools:
- claude-code
- cursor
- windsurf
tags:
- rust
- axum
- api-design
dependencies:
- name: rust-conventions
version: ">=1.0.0"
loading_rules:
- condition: "file_modified matches **/*handler*.rs"
priority: high
- condition: "user_mentions contains 'api' or 'endpoint'"
priority: medium
九、总结与展望
关键要点回顾
- Skills 不是 Prompt,是结构化的、可版本控制的知识模块
CLAUDE.md是编排层,定义何时加载哪些 Skills- 条件加载是核心机制,避免 Token 浪费
- 代码即文档,Skill 中的代码示例比文字描述更有价值
- 技能协同产生复利,1 + 1 > 2
未来展望
短期(2026年下半年):
- Claude Code 官方 Skill 市场上线
- Skill 描述语言(SDL)成为行业标准
- 主流 IDE 插件支持 Skills 导入
中期(2027年):
- Skills 自动生成:从代码库自动提取「项目特定 Skills」
- 跨项目 Skill 迁移:将一个项目的 Skills 适配到另一个项目
- Skill 质量评估:自动检测低质量 Skills(过时、冲突、冗余)
长期愿景:
- Skill 作为一种服务(Skill as a Service):团队内部 Skill 仓库,新人入职自动加载团队 Skills
- AI Agent 技能图谱:类似依赖树,可视化 AI Agent 的知识结构
- Skills 驱动的代码生成:不是「生成代码」,而是「生成符合 Skills 约束的代码」
参考资源
- Karpathy's Claude Code Skills — 原始项目
- Claude Code Official Docs — 官方文档
- Axum Official Examples — Axum 示例
- Rust Web Framework Benchmarks 2026 — 最新性能对比
- SQLx Documentation — 编译时检查数据库查询
如果你正在构建 AI 编码 Agent 的技能系统,欢迎在评论区分享你的经验和 Skill 文件——让我们一起推动这个领域向前发展。
最后,记住 Karpathy 的话:
"The goal is not to make the AI smarter. The goal is to give it the same context that a senior developer would have on their first day."