普通视图

发现新文章,点击刷新页面。
今天 — 2026年2月28日首页

从 Next.js 完全迁移到 vinext 的实战踩坑指南

作者 HashTang
2026年2月27日 13:46

一次真实项目从 Next.js (Cloudflare Pages) 迁移到 vinext (Cloudflare Workers) 的全过程记录,涵盖构建、部署、运行时、国际化、认证等 16+ 个坑位及其解决方案。

项目简介

本文基于开源项目 edge-next-starter 的真实迁移经验撰写。

edge-next-starter 是一个面向 Cloudflare 全栈开发的 Next.js 生产级启动模板,集成了 D1 数据库、R2 对象存储、KV 缓存、better-auth 认证、next-intl 国际化、Stripe 支付等企业级能力,开箱即用。项目采用 Repository 模式 + Edge Runtime 架构设计,所有代码均运行在 Cloudflare Workers 全球边缘网络上。

GitHub: github.com/TangSY/edge… ⭐ 欢迎 Star

背景与动机

2026 年 2 月 24 日,Cloudflare 发布了一篇震动 Web 开发社区的博客:How we rebuilt Next.js with AI in one week。一名 Cloudflare 工程师 Steve Faulkner 使用 Anthropic 的 Claude AI 模型,仅花费约 1100 美元的 token 费用,在一周内从零重建了 Next.js 94% 的 API。产物就是 vinext(发音 "vee-next")—— 一个基于 Vite 构建的 Next.js 替代实现,专门针对 Cloudflare Workers 优化。

Cloudflare CTO Dane Knecht 称之为「Next.js 的解放日」。项目绝大部分代码由 AI 编写,人类负责架构方向、优先级和设计决策。

vinext 相比传统的 @cloudflare/next-on-pages 或 OpenNext 方案,有以下显著优势:

  • 原生 Workers 部署:不再需要逆向工程 Next.js 的构建输出,一条命令直接部署
  • Vite 构建:取代 Turbopack/webpack,生产构建速度最高提升 4 倍
  • 更小的 bundle:客户端包体积最高缩小 57%
  • 开发环境一致性vite dev 直接在 Workers 运行时中运行,可以测试 D1、KV、Durable Objects 等平台 API
  • RSC 原生支持:完整的 React Server Components、流式渲染、Server Actions 支持

但 vinext 仍处于实验阶段(🚧 Experimental),迁移过程远非一帆风顺 — 本文记录了我们在真实生产项目中遇到的 16 个坑位

项目技术栈

组件 技术
框架 Next.js App Router (RSC)
运行时 Cloudflare Workers (Edge Runtime)
数据库 Cloudflare D1 (SQLite)
ORM Prisma + @prisma/adapter-d1
认证 better-auth (从 NextAuth v5 迁移)
国际化 next-intl (en/zh)
存储 Cloudflare R2
缓存 Cloudflare KV
包管理 pnpm

迁移概览

整个迁移涉及 25 个 commits,修改了 50+ 个文件。问题主要集中在以下几个维度:

构建阶段 ─── Prisma Client 模块解析 (3 次迭代)
          └── Wrangler 配置格式转换

运行时 ───── ESM 导入方式变更
          ├── Proxy 导出签名
          ├── 环境变量访问方式
          └── NextURL 只读属性

框架兼容 ─── Middleware matcher 语法
          ├── notFound() 错误处理
          ├── RSC 条件导出
          ├── .rsc 请求处理
          └── Link 组件 vs 原生 <a>

认证系统 ─── DateInt 类型转换
          ├── OAuth State 清理查询
          ├── VerificationToken 主键
          ├── String ↔ Int ID 转换
          └── emailVerified BooleanInt

第一关:Vite 构建 — Prisma Client 解析

症状pnpm build 失败,报 No such module ".prisma/client/default"

原因:Prisma 生成的客户端使用 .prisma/client/default 作为裸模块标识符 (bare specifier)。虽然它以 . 开头,但并不是相对路径 — Node.js 会从 node_modules 中解析它。Vite 把它当作相对路径处理,找不到模块后将其标记为 external,导致 Workers 运行时报错。

踩坑过程(3 次迭代):

第一次:resolve.alias(失败)

// vite.config.ts — 第一次尝试
export default defineConfig({
  resolve: {
    alias: {
      '.prisma/client/default': resolve(import.meta.dirname, 'node_modules/.prisma/client/wasm.js'),
    },
  },
});

问题import.meta.dirname 在 Vite 编译 TypeScript 配置时,可能解析到临时目录而非项目根目录,导致 CI 环境路径错误。

第二次:process.cwd()(部分成功)

// 改用 process.cwd()
'.prisma/client/default': resolve(
  process.cwd(),
  'node_modules/.prisma/client/wasm.js'
),

问题:在 pnpm 的 store 布局下,.prisma/client/wasm.js 不在 node_modules 直接目录中,ENOENT 错误。

第三次:Vite resolveId 插件(最终方案)

function prismaClientResolve(): Plugin {
  const _require = createRequire(import.meta.url);
  let prismaDir: string | null = null;

  // 策略 1: 项目根目录直接查找
  const directPath = resolve(process.cwd(), 'node_modules', '.prisma', 'client');

  // 策略 2: 从 @prisma/client 包位置反向查找(兼容 pnpm store)
  let pnpmPath: string | null = null;
  try {
    const prismaClientPkg = _require.resolve('@prisma/client/package.json');
    pnpmPath = resolve(dirname(prismaClientPkg), '..', '..', '.prisma', 'client');
  } catch {}

  // 选择第一个存在的路径
  for (const candidate of [directPath, pnpmPath].filter(Boolean) as string[]) {
    if (existsSync(candidate)) {
      prismaDir = candidate;
      break;
    }
  }

  return {
    name: 'prisma-client-resolve',
    enforce: 'pre',
    resolveId(source) {
      if (!source.startsWith('.prisma/client')) return null;
      if (!prismaDir) return null;

      const subpath = source === '.prisma/client' ? '' : source.slice('.prisma/client/'.length);

      if (subpath === 'default' || subpath === '') {
        // 优先使用 wasm.js(Workers WASM 引擎)
        const wasmPath = resolve(prismaDir, 'wasm.js');
        if (existsSync(wasmPath)) return wasmPath;

        // 回退到 default.js
        const defaultPath = resolve(prismaDir, 'default.js');
        if (existsSync(defaultPath)) return defaultPath;
      }
      return null;
    },
  };
}

关键点

  • 使用 createRequire@prisma/client 的实际位置反向定位 .prisma/client
  • existsSync 检查避免路径猜测
  • 优先 wasm.js(Workers 兼容)而非 default.js(Node.js 专用)

第二关:Wrangler 配置 — Pages → Workers 格式转换

症状:部署到 Cloudflare 后,环境变量显示为 "preview" 而非 "production"

原因:vinext 使用 Workers 部署(wrangler deploy),但项目的 wrangler 配置还是 Cloudflare Pages 格式。

修改前(Pages 格式):

pages_build_output_dir = ".vercel/output/static"

修改后(Workers 格式):

main = "dist/server/index.js"
no_bundle = true

# vinext 的构建产物是预打包的,告诉 Wrangler 不要重新用 esbuild 打包
[[rules]]
type = "ESModule"
globs = ["**/*.js", "**/*.mjs"]

[assets]
not_found_handling = "none"
directory = "dist/client"

关键点

  • no_bundle = true 是必须的,否则 Wrangler 会用 esbuild 重新打包 vinext 的构建产物,遇到 .prisma/client/default 外部导入时直接报错
  • [[rules]] ESModule 类型声明让 Wrangler 正确处理 vinext 输出的 ES Module 文件
  • [assets] 替代 pages_build_output_dir,指向 vinext 的客户端构建产物

第三关:Workers 运行时 — ESM 与 Proxy 导出

症状:部署后访问任何页面返回 500,Workers 日志无明显错误

问题 1cloudflare:workers 模块必须用静态 ESM 导入

// ❌ 错误 — CJS 动态导入在 Workers 中不工作
const { env } = require('cloudflare:workers');

// ✅ 正确 — 静态 ESM 导入
import { env as cloudflareEnv } from 'cloudflare:workers';

问题 2:vinext 要求 proxy 使用命名导出而非默认导出

// ❌ 错误 — Next.js middleware 的默认导出
export default function middleware(req) { ... }

// ✅ 正确 — vinext 要求命名导出 { proxy }
export async function proxy(req: NextRequest) { ... }

Vitest 兼容cloudflare:workers 在测试环境中不存在,需要 mock:

// vitest.cloudflare-stub.ts
export const env = {};
export default { env };

// vitest.config.ts
resolve: {
  alias: {
    'cloudflare:workers': resolve(__dirname, 'vitest.cloudflare-stub.ts'),
  },
}

第四关:Middleware → Proxy — matcher 语法不兼容

症状:所有页面返回 404,/en 路径抛出 Error 1101(next-intl locale context 缺失)

原因:vinext 的 matchMiddlewarePath 实现中对 matcher pattern 做了 .replace(/\./g, "\\.") 处理。这会破坏 Next.js 标准的正则表达式 matcher:

// Next.js 标准 matcher(包含正则语法)
export const config = {
  matcher: ['/((?!api|_next/static|_next/image|.*\\..*).*)'],
};
// 经过 vinext 的 .replace 后变成:
// /((?!api|_next/static|_next/image|.*\\.\\..*)\\..*)
// 完全无法匹配任何路径 → proxy 永远不执行

解决方案:使用 vinext 支持的 :param 语法:

export const config = {
  matcher: ['/:path*'],
};
// vinext 将 :path* 转换为 (?:.*) → 匹配所有路径

附带说明:在 Workers 中不需要排除 _next/static 等静态资源路径,因为 Cloudflare CDN 在请求到达 Worker 之前就会直接提供静态文件。但为了安全,我们在 proxy 中增加了静态文件扩展名检测:

const STATIC_EXTENSIONS =
  /\.(ico|png|jpg|jpeg|gif|svg|webp|css|js|map|woff2?|ttf|eot|webmanifest|txt|xml|json)$/i;

export async function proxy(req: NextRequest) {
  if (STATIC_EXTENSIONS.test(req.nextUrl.pathname)) {
    return NextResponse.next();
  }
  // ...
}

第五关:next-intl — NextURL.port 只读属性

症状:访问任何页面抛出 TypeError: Cannot set property port which has only a getter

原因next-intlcreateIntlMiddleware 内部会修改 NextURL 对象的 port 属性。在标准 Next.js 中 NextURL.port 是可读写的,但 vinext 的 Workers 运行时将其实现为只读 getter。

解决方案:用轻量级的自定义 i18n 路由处理器替代 createIntlMiddleware

function handleI18nRouting(req: NextRequest): NextResponse {
  const { locales, defaultLocale } = routing;
  const pathname = req.nextUrl.pathname;

  // 路径已包含 locale 前缀,直接通过
  const hasLocale = locales.some(
    locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );
  if (hasLocale) return NextResponse.next();

  // 从 Accept-Language 检测首选 locale
  const acceptLanguage = req.headers.get('accept-language') || '';
  let detectedLocale = defaultLocale;
  for (const locale of locales) {
    if (acceptLanguage.toLowerCase().includes(locale)) {
      detectedLocale = locale;
      break;
    }
  }

  // 使用原生 URL 重定向(避免 NextURL setter 问题)
  const url = new URL(req.url);
  url.pathname = `/${detectedLocale}${pathname}`;
  return NextResponse.redirect(url);
}

关键点:使用 new URL(req.url) 而非 req.nextUrl.clone(),因为后者会触发 NextURL 的 setter 逻辑。


第六关:环境变量 — cloudflare:workers vs process.env

症状:认证功能在本地开发正常,部署后报 CLIENT_ID_AND_SECRET_REQUIRED

原因:通过 wrangler secret put 设置的密钥(如 GOOGLE_CLIENT_IDNEXTAUTH_SECRET),只能通过 cloudflare:workersenv 绑定访问,不会出现在 process.env 中。

// ❌ 在 Workers 中永远是 undefined
const secret = process.env.NEXTAUTH_SECRET;

// ✅ 正确方式
import { env as cloudflareEnv } from 'cloudflare:workers';
const secret = (cloudflareEnv as Record<string, unknown>).NEXTAUTH_SECRET;

最终方案:创建统一的环境变量解析函数:

function getEnvVar(key: string): string | undefined {
  // 优先从 Cloudflare Workers 绑定读取
  const cfEnv = cloudflareEnv as Record<string, unknown>;
  if (cfEnv?.[key]) return String(cfEnv[key]);
  // 回退到 process.env(本地开发、测试环境)
  return process.env[key];
}

第七关:notFound() 异常 — NEXT_NOT_FOUND 未捕获

症状:某些页面间歇性返回 500,日志显示 NEXT_NOT_FOUND 异常

原因:vinext 在错误恢复时会用 undefined params 重新渲染 locale layout。代码中的 notFound()(来自 next/navigation)抛出 NEXT_NOT_FOUND 错误,但 vinext 不会像标准 Next.js 那样捕获它,导致整个 RSC 渲染链崩溃。

// ❌ vinext 中不工作
export default async function LocaleLayout({ params }: Props) {
  const { locale } = await params;
  if (!routing.locales.includes(locale)) {
    notFound(); // 抛出 NEXT_NOT_FOUND → 500
  }
}

// ✅ 回退到默认 locale
export default async function LocaleLayout({ params }: Props) {
  const resolvedParams = await params;
  const locale = routing.locales.includes(resolvedParams?.locale)
    ? resolvedParams.locale
    : routing.defaultLocale;
  // 继续渲染...
}

第八关:NextIntlClientProvider — RSC 条件导出冲突

症状:所有页面路由报 Error 1101,日志显示 headers() is not a function

原因next-intlNextIntlClientProvider 使用了 package.json 的 exports 条件导出。在 vinext 的 RSC 环境中,它解析到了一个异步服务端版本,该版本会调用 headers()(来自 next/headers)。但 vinext 不支持在该上下文中调用 headers()

解决方案:创建本地的 'use client' 包装组件:

// app/[locale]/intl-provider.tsx
'use client';

import { IntlProvider } from 'use-intl';

export function ClientIntlProvider({
  locale,
  messages,
  children,
}: {
  locale: string;
  messages: Record<string, unknown>;
  children: React.ReactNode;
}) {
  return (
    <IntlProvider locale={locale} messages={messages as IntlMessages}>
      {children}
    </IntlProvider>
  );
}

关键点

  • 直接从 use-intl(next-intl 的底层库)导入 IntlProvider
  • 'use client' 指令确保 vinext 将其序列化为客户端引用
  • 不使用 NextIntlClientProvider,避免触发服务端条件导出

第九关:RSC 导航 — .rsc 请求被 Auth 拦截

症状:浏览器前进/后退按钮导致页面空白

原因:vinext 使用 .rsc 后缀进行客户端 RSC payload 请求(例如 /en/dashboard.rsc)。proxy 将这些请求当作普通页面请求处理,检查认证状态后重定向到 /login。但 .rsc 请求期望的是 RSC 流数据,收到重定向后 React 无法解析,导致页面空白。

解决方案:在 proxy 中对 .rsc 请求只做 i18n 路由,跳过 auth 检查:

if (pathname.endsWith('.rsc')) {
  // 去掉 .rsc 后缀检查 i18n 路由
  const cleanPath = pathname.slice(0, -4);
  const hasLocale = locales.some(
    locale => cleanPath.startsWith(`/${locale}/`) || cleanPath === `/${locale}`
  );

  if (hasLocale) return NextResponse.next();

  // 添加 locale 前缀并重定向
  const url = new URL(req.url);
  url.pathname = `/${detectedLocale}${pathname}`;
  return NextResponse.redirect(url);
}

安全性:认证在服务端组件层(getSessionSafe())强制执行,不依赖 proxy。


第十关:Link 组件与 API 路由 — RSC 流损坏

症状:用 <Link> 跳转到 API 端点后,浏览器后退显示原始 JSON 而非页面

原因:Next.js 的 <Link> 组件触发客户端 RSC 导航。API 端点返回 JSON 而非 RSC payload,React 尝试解析 JSON 作为 RSC 流失败,破坏了浏览器历史记录中的 React 根节点。

解决方案:对 API 链接使用原生 <a> 标签:

// ❌ 会触发 RSC 导航
<Link href="/api/health">Health Check</Link>

// ✅ 强制全页面导航
<a href="/api/health" target="_blank" rel="noopener noreferrer">
  Health Check
</a>

第十一关:better-auth — Date ↔ Int 类型不匹配

症状:better-auth 的所有数据库写入操作失败

原因:better-auth 内部所有日期字段使用 JavaScript Date 对象,但我们的 Prisma schema 使用 Int(Unix 时间戳,秒)来兼容 D1/SQLite。Prisma 的 SQLite provider 不会自动做这个转换。

解决方案:创建 Prisma Client Proxy,拦截所有 auth 相关模型的操作:

// lib/db/auth-prisma-proxy.ts
const AUTH_DATE_FIELDS: Record<string, string[]> = {
  user: ['emailVerified', 'createdAt', 'updatedAt'],
  account: ['expiresAt', 'createdAt', 'updatedAt'],
  session: ['expires', 'createdAt', 'updatedAt'],
  verificationToken: ['expires', 'createdAt', 'updatedAt'],
};

function deepConvertInputs(obj: unknown, parentKey?: string): unknown {
  if (obj instanceof Date) return Math.floor(obj.getTime() / 1000);
  if (Array.isArray(obj)) return obj.map(item => deepConvertInputs(item));
  if (obj !== null && typeof obj === 'object') {
    const result: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
      result[key] = deepConvertInputs(value, key);
    }
    return result;
  }
  return obj;
}

export function createAuthPrismaProxy<T>(prisma: T): T {
  return new Proxy(prisma as object, {
    get(target, prop, receiver) {
      const value = Reflect.get(target, prop, receiver);
      if (typeof prop !== 'string') return value;
      const modelKey = resolveModelKey(prop);
      if (!modelKey) return value;

      // 为 auth 模型的每个方法创建代理
      return new Proxy(value as object, {
        get(modelTarget, methodName, modelReceiver) {
          const method = Reflect.get(modelTarget, methodName, modelReceiver);
          if (typeof method !== 'function') return method;
          if (!ALL_OPS.has(methodName as string)) return method.bind(modelTarget);

          return async function (...args: any[]) {
            // 输入:递归转换 Date → Unix timestamp
            if (args[0] && typeof args[0] === 'object') {
              args[0] = deepConvertInputs(args[0]);
            }
            const result = await method.apply(modelTarget, args);
            // 输出:Unix timestamp → Date(仅已知日期字段)
            return convertOutputDates(result, modelKey);
          };
        },
      });
    },
  }) as T;
}

使用方式:

// lib/auth/index.ts
export const auth = betterAuth({
  database: prismaAdapter(createAuthPrismaProxy(prisma), { provider: 'sqlite' }),
  // ...
});

第十二关:OAuth 状态管理 — deleteMany 中的 Date 未转换

症状:Google OAuth 回调后重定向到 ?error=please_restart_the_process

原因:better-auth 的 OAuth 流程中,findVerificationValue 函数在查找状态后会执行清理操作:

// better-auth 内部代码 (state.mjs)
await adapter.deleteMany({
  model: 'verification',
  where: [{ field: 'expiresAt', operator: 'lt', value: new Date() }],
});

我们的 proxy 第一版只拦截了 createupdatefind 操作,且只转换 datacreate/update 字段中的 Date。deleteManywhere 子句中的 new Date() 没有被转换,导致 SQLite 比较失败。

修复:将拦截范围扩展到所有 Prisma 操作(ALL_OPS),并使用递归的 deepConvertInputs 处理整个 args 树:

const ALL_OPS = new Set([
  'create',
  'createMany',
  'update',
  'updateMany',
  'upsert',
  'delete',
  'deleteMany',
  'findFirst',
  'findUnique',
  'findMany',
  'count',
  'aggregate',
]);

第十三关:VerificationToken — 主键缺失与 ID 类型错误

症状:Google OAuth 回调返回 internal_server_error,Workers 日志显示两个错误

错误 1verificationToken.delete({ where: { id: null } })

原因:VerificationToken 模型的 id 字段定义为 String?(可选),没有设置为主键。配合 generateId: false,better-auth 不会为 VerificationToken 生成 ID,导致删除操作传入 id: null

错误 2user.findFirst({ where: { id: "1" } })

原因:better-auth 内部使用 z.coerce.string() 将所有 ID 转换为字符串。但 Prisma schema 中 User 的 idInt,字符串 "1" 无法匹配整数 1

修复

  1. 数据库迁移 — 重建 verification_tokens 表:
-- 0007_fix_verification_token_pk.sql
CREATE TABLE IF NOT EXISTS verification_tokens_new (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  identifier TEXT NOT NULL,
  token TEXT NOT NULL UNIQUE,
  expires INT NOT NULL,
  created_at INT DEFAULT (strftime('%s', 'now')),
  updated_at INT DEFAULT (strftime('%s', 'now')),
  UNIQUE(identifier, token)
);
INSERT INTO verification_tokens_new (identifier, token, expires, created_at, updated_at)
  SELECT identifier, token, expires, created_at, updated_at FROM verification_tokens;
DROP TABLE verification_tokens;
ALTER TABLE verification_tokens_new RENAME TO verification_tokens;
CREATE INDEX idx_verification_tokens_expires ON verification_tokens(expires);
  1. Prisma schema 更新
model VerificationToken {
  id    Int    @id @default(autoincrement())  // 原来是 String?
  // ...
}
  1. 启用 useNumberId — better-auth 内置的 String ↔ Int ID 转换:
// lib/auth/index.ts
export const auth = betterAuth({
  advanced: {
    database: {
      useNumberId: true, // 替代 generateId: false
    },
  },
});

useNumberId: true 让 better-auth 的适配器层自动做 String → Number(输入)和 Number → String(输出)的 ID 转换。


第十四关:emailVerified — Boolean vs Int

症状:Google OAuth 登录时,创建用户失败

原因:当用户通过 Google OAuth 登录时,better-auth 认为邮箱已被 Google 验证,设置 emailVerified: true(布尔值)。但 schema 中 emailVerifiedInt?(Unix 时间戳)。

修复:在 proxy 的 deepConvertInputs 中添加布尔值 → 时间戳转换:

const BOOLEAN_TO_TIMESTAMP_FIELDS = new Set(['emailVerified']);

function deepConvertInputs(obj: unknown, parentKey?: string): unknown {
  if (obj instanceof Date) return Math.floor(obj.getTime() / 1000);

  // 特定字段的 boolean → timestamp 转换
  if (typeof obj === 'boolean' && parentKey && BOOLEAN_TO_TIMESTAMP_FIELDS.has(parentKey)) {
    return obj ? Math.floor(Date.now() / 1000) : null;
  }

  // ... 递归处理
}

第十五关:D1 迁移 — ALTER TABLE 不支持动态默认值

症状wrangler d1 migrations apply 报错 non-constant default

原因:SQLite 的 ALTER TABLE ADD COLUMN 语句不允许使用非常量默认值(如 strftime('%s', 'now'))。

-- ❌ 错误
ALTER TABLE accounts ADD COLUMN created_at INT DEFAULT (strftime('%s', 'now'));

-- ✅ 正确 — 先用常量默认值,再用 UPDATE 回填
ALTER TABLE accounts ADD COLUMN created_at INT DEFAULT 0;
UPDATE accounts SET created_at = strftime('%s', 'now') WHERE created_at = 0;

第十六关:CI/CD — Workers 域名包含账户子域

症状:GitHub Actions 部署成功但健康检查失败(HTTP 000)

原因:Cloudflare Workers 的默认域名格式是 <worker-name>.<account-subdomain>.workers.dev,而不是 <worker-name>.workers.dev。CI 配置中的回退 URL 少了账户子域。

# ❌ 错误
DEPLOYMENT_URL="https://my-worker.workers.dev"

# ✅ 正确
DEPLOYMENT_URL="https://my-worker.t-ac5.workers.dev"

最佳实践:在 GitHub Repository Variables 中配置 TEST_DEPLOYMENT_URLPRODUCTION_DEPLOYMENT_URL,不要依赖 hardcoded 回退值。


核心代码清单

迁移后的核心文件结构:

├── vite.config.ts              # Vite 配置 + Prisma resolveId 插件
├── proxy.ts                    # 替代 middleware.ts(i18n + auth + CORS/CSRF)
├── wrangler.toml               # 本地开发配置(Workers 格式)
├── wrangler.test.toml          # 测试环境配置
├── wrangler.prod.toml          # 生产环境配置
├── vitest.cloudflare-stub.ts   # cloudflare:workers 测试 mock
├── lib/
│   ├── auth/
│   │   ├── index.ts            # better-auth 配置 + getEnvVar
│   │   ├── session.ts          # getSessionSafe (RSC 安全包装)
│   │   └── password.ts         # PBKDF2 密码哈希(Edge 兼容)
│   └── db/
│       ├── client.ts           # Prisma 单例 + cloudflare:workers ESM 导入
│       └── auth-prisma-proxy.ts # Date↔Int + Boolean→Int 类型转换代理
├── app/[locale]/
│   └── intl-provider.tsx       # 本地 'use client' IntlProvider 包装
└── migrations/
    └── 0007_fix_verification_token_pk.sql  # VerificationToken 主键修复

总结

从 Next.js 迁移到 vinext 不是简单的"换个构建工具"那么简单。主要挑战来自三个层面:

  1. Vite vs webpack 的模块解析差异:Prisma 的裸模块标识符、条件导出的解析优先级等,在两个构建系统中行为完全不同。

  2. Workers vs Node.js 运行时差异process.env 不可用、NextURL 属性只读、notFound() 未被捕获 — 这些都是 Workers 运行时的限制。

  3. 第三方库假设:next-intl 假设 NextURL.port 可写,better-auth 假设日期字段是 Date 对象 — 这些假设在标准 Next.js 中成立,但在 vinext 的 Workers 环境中全部失效。

核心教训:vinext 不是 Next.js 的替代品,而是 Next.js API 在 Workers 运行时上的重新实现。任何依赖 Next.js 实现细节(而非公开 API)的代码,都可能需要适配。

本文基于 2026 年 2 月的实际迁移经验,vinext 仍在快速迭代中,部分问题可能在后续版本中得到解决。

昨天 — 2026年2月27日首页

Everything Claude Code 完全长篇指南

作者 王小酱
2026年2月27日 16:54

image.png


在上一篇Everything Claude Code 速查指南中,我涵盖了基础配置:技能(skills)和命令(commands)、钩子(hooks)、子代理(subagents)、MCPs、插件(plugins),以及构成高效 Claude Code 工作流骨干的配置模式。那是一份配置指南和基础设施。

这篇长篇指南深入探讨了将高效会话与浪费性会话区分开来的技术。如果你还没有读过简明指南,请先回去配置好你的设置。接下来的内容假设你已经配置好了技能、代理、钩子和 MCPs 并且它们已正常运行。

这里的主题包括:token 经济学、记忆持久化、验证模式、并行化策略,以及构建可复用工作流的复合效应。这些是我在超过10个月的日常使用中精炼出的模式,它们决定了你是在第一个小时内就被上下文腐烂(context rot)所困扰,还是能够维持数小时的高效会话。

简明和长篇文章中涵盖的所有内容都可在 GitHub 上获取:everything-claude-code


Context & Memory Management

上下文与记忆管理

对于跨会话共享记忆,最好的方法是使用一个技能或命令来总结并检查进度,然后保存到你的 .claude 文件夹中的 .tmp 文件,并在会话结束前持续追加内容。第二天它可以将其作为上下文来使用,从你上次中断的地方继续,为每个会话创建一个新文件,这样你就不会把旧的上下文污染到新的工作中。最终你会有一个很大的会话日志文件夹——只需将其备份到有意义的地方,或者修剪你不需要的会话对话。Claude 会创建一个总结当前状态的文件。查看它,如果需要可以要求修改,然后开始新的对话。对于新对话,只需提供文件路径。当你达到上下文限制并需要继续复杂工作时,这特别有用。这些文件应该包含——哪些方法有效(可验证且有证据)、哪些尝试过的方法没有效果、哪些方法尚未尝试以及还剩什么要做。

image.png 会话存储示例 -> github.com/affaan-m/ev…

策略性清除上下文:

一旦你的计划设定好并且上下文已清除(现在是 Claude Code 计划模式中的默认选项),你就可以从计划开始工作。当你积累了大量不再与执行相关的探索性上下文时,这很有用。对于策略性压缩,禁用自动压缩。在逻辑间隔处手动压缩,或创建一个技能为你执行此操作,或在某些定义的标准下提出建议。


Strategic Compact Skill(直接链接):

(嵌入以供快速参考)

#!/bin/bash
# Strategic Compact Suggester
# Runs on PreToolUse to suggest manual compaction at logical intervals
#
# Why manual over auto-compact:
# - Auto-compact happens at arbitrary points, often mid-task
# - Strategic compacting preserves context through logical phases
# - Compact after exploration, before execution
# - Compact after completing a milestone, before starting next

COUNTER_FILE="/tmp/claude-tool-count-$$"
THRESHOLD=${COMPACT_THRESHOLD:-50}

# Initialize or increment counter
if [ -f "$COUNTER_FILE" ]; then
  count=$(cat "$COUNTER_FILE")
  count=$((count + 1))
  echo "$count" > "$COUNTER_FILE"
else
  echo "1" > "$COUNTER_FILE"
  count=1
fi

# Suggest compact after threshold tool calls
if [ "$count" -eq "$THRESHOLD" ]; then
  echo "[StrategicCompact] $THRESHOLD tool calls reached - consider /compact if transitioning phases" >&2
fi

将它挂钩到 Edit/Write 操作的 PreToolUse 上——当你积累了足够多的上下文、压缩可能有帮助时,它会提醒你。


进阶:动态系统提示注入(Advanced: Dynamic System Prompt Injection)

我学到并正在试运行的一种模式是:不要只把所有东西放在 CLAUDE.md(用户范围)或 .claude/rules/(项目范围)中(这些每次会话都会加载),而是使用 CLI 标志来动态注入上下文。

claude --system-prompt "$(cat memory.md)"

这让你可以更精确地控制何时加载什么上下文。你可以根据当前工作内容在每个会话中注入不同的上下文。

为什么这比 @ 文件引用更重要: 当你使用 @memory.md 或将内容放在 .claude/rules/ 中时,Claude 通过 Read 工具在对话过程中读取它——它作为工具输出传入。当你使用 --system-prompt 时,内容在对话开始前被注入到实际的系统提示中。

区别在于指令层级(instruction hierarchy)。系统提示内容的权威性高于用户消息,用户消息的权威性高于工具结果。对于大多数日常工作来说,这种差异是微不足道的。但对于诸如严格的行为规则、项目特定的约束,或你绝对需要 Claude 优先考虑的上下文——系统提示注入确保它得到适当的权重。

实际配置:

一种有效的方法是将 .claude/rules/ 用于你的基线项目规则,然后为场景特定的上下文设置 CLI 别名,可以在它们之间切换:

# Daily development
alias claude-dev='claude --system-prompt "$(cat ~/.claude/contexts/dev.md)"'

# PR review mode
alias claude-review='claude --system-prompt "$(cat ~/.claude/contexts/review.md)"'

# Research/exploration mode
alias claude-research='claude --system-prompt "$(cat ~/.claude/contexts/research.md)"'

System Prompt Context Example Files(直接链接):

  • dev.md 专注于实现
  • review.md 专注于代码质量/安全
  • research.md 专注于行动前的探索

同样,对于大多数情况,使用 .claude/rules/context1.md 和直接将 context1.md 附加到系统提示之间的区别是微乎其微的。CLI 方法更快(无需工具调用)、更可靠(系统级权威)且稍微更节省 token。但这是一个小优化,对许多人来说开销大于收益。


进阶:记忆持久化钩子(Advanced: Memory Persistence Hooks)

有一些钩子(hooks)是大多数人不知道的,或者知道但没有真正利用的,它们有助于记忆管理:

SESSION 1                    SESSION 2
─────────                    ─────────
[Start]                      [Start]
   │                            │
   ▼                            ▼
┌──────────────┐          ┌──────────────┐
│ SessionStart │ ◄─── reads ─────── │ SessionStart │◄── loads previous
│    Hook      │    nothing yet     │    Hook      │    context
└──────┬───────┘          └──────┬───────┘
       │                         │
       ▼                         ▼
   [Working]                 [Working]
       │                    (informed)
       ▼                         │
┌──────────────┐                 ▼
│  PreCompact  │──► saves state [Continue...]
│    Hook      │    before summary
└──────┬───────┘
       │
       ▼
   [Compacted]
       │
       ▼
┌──────────────┐
│  Stop Hook   │──► persists to ──────────►
│ (session-end)│    ~/.claude/sessions/
└──────────────┘
  • PreCompact Hook: 在上下文压缩发生之前,将重要状态保存到文件中
  • SessionComplete Hook: 在会话结束时,将学习成果持久化到文件中
  • SessionStart Hook: 在新会话开始时,自动加载之前的上下文

Memory Persistent Hooks(直接链接):

(嵌入以供快速参考)

{
  "hooks": {
    "PreCompact": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/hooks/memory-persistence/pre-compact.sh"
      }]
    }],
    "SessionStart": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/hooks/memory-persistence/session-start.sh"
      }]
    }],
    "Stop": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/hooks/memory-persistence/session-end.sh"
      }]
    }]
  }
}

这些的作用:

  • pre-compact.sh:记录压缩事件,用压缩时间戳更新活动会话文件
  • session-start.sh:检查最近的会话文件(最近7天),通知可用的上下文和已学习的技能
  • session-end.sh:创建/更新每日会话文件模板,跟踪开始/结束时间

将这些串联起来,即可实现跨会话的持续记忆,无需手动干预。这建立在第一篇文章中的钩子类型(PreToolUse、PostToolUse、Stop)之上,但专门针对会话生命周期。


Continuous Learning / Memory

持续学习 / 记忆

我们之前讨论了以更新代码地图(codemaps)的形式进行持续记忆更新,但这也适用于其他方面,比如从错误中学习。如果你不得不多次重复提示,而 Claude 遇到了相同的问题或给了你以前听过的回答,那这部分就适用于你。很可能你需要发送第二个提示来"重新引导(resteer)"并校准 Claude 的方向。这适用于任何此类场景——这些模式必须被追加到技能中。

现在你可以通过简单地告诉 Claude 记住它或将其添加到你的规则中来自动完成此操作,或者你可以有一个技能来专门做这件事。

问题: 浪费 token,浪费上下文,浪费时间,你的皮质醇飙升,因为你沮丧地对 Claude 大喊不要做你在之前会话中已经告诉它不要做的事情。

解决方案: 当 Claude Code 发现一些非平凡的东西——调试技术、变通方法、某些项目特定的模式——它会将该知识保存为新技能。下次出现类似问题时,技能会自动加载。


Continuous Learning Skill(直接链接)

为什么我使用 Stop hook 而不是 UserPromptSubmit?UserPromptSubmit 在你发送的每条消息上都会运行——这会带来很大的开销,给每个提示增加延迟,坦率地说对这个目的来说是大材小用。Stop 在会话结束时只运行一次——轻量级,不会在会话期间拖慢你的速度,而且评估的是完整会话而非碎片化的内容。

安装:

# Clone to skills folder
git clone https://github.com/affaan-m/everything-claude-code.git ~/.claude/skills/everything-claude-code

# Or just grab the continuous-learning skill
mkdir -p ~/.claude/skills/continuous-learning
curl -sL https://raw.githubusercontent.com/affaan-m/everything-claude-code/main/skills/continuous-learning/evaluate-session.sh > ~/.claude/skills/continuous-learning/evaluate-session.sh
chmod +x ~/.claude/skills/continuous-learning/evaluate-session.sh

Hook Configuration(直接链接):

{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/skills/continuous-learning/evaluate-session.sh"
          }
        ]
      }
    ]
  }
}

这使用 Stop hook 在每个提示上运行一个激活脚本,评估会话中值得提取的知识。该技能也可以通过语义匹配激活,但钩子确保了一致的评估。

Stop hook 在你的会话结束时触发——脚本分析会话中值得提取的模式(错误解决方案、调试技术、变通方法、项目特定模式等),并将它们保存为 ~/.claude/skills/learned/ 中的可复用技能。

手动提取 - /learn:

你不必等到会话结束。仓库中还包含一个 /learn 命令,你可以在会话中途刚解决了一些非平凡的问题时运行它。它会提示你立即提取模式,起草一个技能文件,并在保存前征求确认。参见这里

会话日志模式:

该技能期望会话日志保存在 .tmp 文件中。模式为:~/.claude/sessions/YYYY-MM-DD-topic.tmp——每个会话一个文件,包含当前状态、已完成项目、阻碍因素、关键决定和下次会话的上下文。示例会话文件在仓库的 examples/sessions/ 中。


其他自我改进的记忆模式:

来自 @RLanceMartin 的一种方法涉及反思会话日志以提炼用户偏好——本质上是建立一个记录什么有效、什么无效的"日记"。每次会话后,一个反思代理(reflection agent)提取哪些做得好、哪些失败了、你做了哪些修正。这些学习成果会更新一个记忆文件,在后续会话中加载。

来自 @alexhillman 的另一种方法是让系统每15分钟主动建议改进,而不是等你注意到模式。代理审查最近的交互,提出记忆更新建议,你批准或拒绝。随着时间推移,它会从你的批准模式中学习。


Token Optimization

Token 优化

我从价格敏感的消费者那里收到了很多问题,或者那些作为重度用户经常遇到限制问题的人。在 token 优化方面,有一些技巧可以使用。

主要策略:子代理架构

主要是优化你使用的工具和子代理架构,旨在将最便宜的、足以完成任务的模型委派出去以减少浪费。你有几个选项——可以尝试试错法并随着进展调整。一旦你了解了什么是什么,你就可以区分哪些委派给 Haiku、哪些委派给 Sonnet、哪些委派给 Opus。

基准测试方法(更深入):

另一种更深入的方法是,你可以让 Claude 设置一个基准测试,在一个有明确目标、任务和明确计划的仓库中。在每个 git worktree 中,让所有子代理都使用同一个模型。在任务完成时记录日志——理想情况下在你的计划和任务中记录。你必须至少使用每个子代理一次。

一旦完成一轮完整测试并且任务已从你的 Claude 计划中勾选完毕,停下来审核进度。你可以通过比较差异(diffs)、创建在所有 worktree 中统一的单元测试、集成测试和端到端测试来做到这一点。这将给你一个基于通过/失败案例的数值基准。如果所有 worktree 都全部通过,你需要添加更多的边界测试用例或增加测试的复杂度。这可能值得也可能不值得做,取决于这对你来说到底有多重要。

模型选择快速参考:

image.png

各种常见任务上子代理的假设配置及选择背后的推理

90% 的编码任务默认使用 Sonnet。当首次尝试失败、任务跨越5个以上文件、架构决策或安全关键代码时升级到 Opus。当任务是重复性的、指令非常清晰,或在多代理设置中作为"工人(worker)"使用时降级到 Haiku。坦率地说,Sonnet 4.5 目前处于一个尴尬的位置,每百万输入 token 3,每百万输出token3,每百万输出 token 15,与 Opus 相比成本节省约66.7%,绝对值来说这是不错的节省,但相对而言对大多数人来说微不足道。Haiku 和 Opus 组合最有意义,因为 Haiku 与 Opus 有5倍的成本差异,而与 Sonnet 只有1.67倍的价格差异。

image.png

来源:platform.claude.com/docs/en/abo…

在你的代理定义中,指定模型:

---
name: quick-search
description: Fast file search
tools: Glob, Grep
model: haiku  # Cheap and fast
---

工具特定优化:

想想 Claude 最频繁调用的工具。例如,用 mgrep 替换 grep——在各种任务上,与传统 grep 或 ripgrep(Claude 默认使用的)相比,平均有效 token 减少约一半。

image.png

来源:github.com/mixedbread-…

后台进程:

适用时,如果你不需要 Claude 处理整个输出并实时流式传输,请在 Claude 之外运行后台进程。这可以通过 tmux 轻松实现(参见简明指南Tmux Commands Reference(直接链接))。获取终端输出后,要么总结它,要么只复制你需要的部分。这将节省大量输入 token,这是大部分成本的来源——Opus 4.5 每百万 token 5,输出每百万token5,输出每百万 token 25。

模块化代码库的好处:

拥有更模块化的代码库,包含可复用的工具函数、函数、钩子等——主文件在数百行而非数千行——既有助于 token 优化成本,也有助于第一次就正确完成任务,两者是相关的。如果你必须多次提示 Claude,你就在大量消耗 token,尤其是当它反复读取非常长的文件时。你会注意到它需要进行大量工具调用才能读完文件。中间过程中,它会告诉你文件非常长,它将继续读取。在这个过程中的某个地方,Claude 可能会丢失一些信息。而且,停止和重新读取会消耗额外的 token。这可以通过拥有更模块化的代码库来避免。示例如下 ->

root/
├── docs/              # Global documentation
├── scripts/           # CI/CD and build scripts
├── src/
│   ├── apps/          # Entry points (API, CLI, Workers)
│   │   ├── api-gateway/    # Routes requests to modules
│   │   └── cron-jobs/
│   │
│   ├── modules/       # The core of the system
│   │   ├── ordering/       # Self-contained "Ordering" module
│   │   │   ├── api/             # Public interface for other modules
│   │   │   ├── domain/          # Business logic & Entities (Pure)
│   │   │   ├── infrastructure/  # DB, External Clients, Repositories
│   │   │   ├── use-cases/       # Application logic (Orchestration)
│   │   │   └── tests/           # Unit and integration tests
│   │   │
│   │   ├── catalog/        # Self-contained "Catalog" module
│   │   │   ├── domain/
│   │   │   └── ...
│   │   │
│   │   └── identity/       # Self-contained "Auth/User" module
│   │       ├── domain/
│   │       └── ...
│   │
│   ├── shared/        # Code used by EVERY module
│   │   ├── kernel/         # Base classes (Entity, ValueObject)
│   │   ├── events/         # Global Event Bus definitions
│   │   └── utils/          # Deeply generic helpers
│   │
│   └── main.ts        # Application bootstrap
├── tests/             # End-to-End (E2E) global tests
├── package.json
└── README.md

精简代码库 = 更便宜的 Token:

这可能很明显,但你的代码库越精简,token 成本就越低。关键是要通过使用技能来持续清理代码库,利用技能和命令进行重构来识别死代码。另外在某些时候,我喜欢通读整个代码库,寻找那些让我觉得突出或看起来重复的东西,手动拼凑这些上下文,然后将其与重构技能和死代码技能一起输入给 Claude。

系统提示精简(进阶):

对于真正注重成本的人:Claude Code 的系统提示占用约18k token(200k 上下文的约9%)。这可以通过补丁减少到约10k token,节省约7,300 token(静态开销的41%)。如果你想走这条路,参见 YK 的 system-prompt-patches,我个人不这样做。


Verification Loops and Evals

验证循环与评估

评估和框架调优——取决于项目,你需要使用某种形式的可观测性和标准化。

可观测性方法:

一种方法是让 tmux 进程挂钩到追踪思维流和输出上,每当技能被触发时。另一种方法是使用 PostToolUse 钩子来记录 Claude 具体执行了什么以及确切的变更和输出是什么。

基准测试工作流:

将其与不使用技能地要求相同的事情进行比较,检查输出差异以进行相对性能基准测试:

           [Same Task]
                │
    ┌────────────┴────────────┐
    ▼                         ▼
┌───────────────┐     ┌───────────────┐
│  Worktree A   │     │  Worktree B   │
│  WITH skill   │     │ WITHOUT skill │
└───────┬───────┘     └───────┬───────┘
        │                     │
        ▼                     ▼
   [Output A]            [Output B]
        │                     │
        └──────────┬──────────┘
                   ▼
              [git diff]
                   │
                   ▼
          ┌────────────────┐
          │ Compare logs,  │
          │ token usage,   │
          │ output quality │
          └────────────────┘

分叉对话,在其中一个中启动一个没有技能的新 worktree,最后查看差异,看看日志记录了什么。这与持续学习和记忆部分相关联。

评估模式类型:

更高级的评估和循环协议在这里登场。分为基于检查点的评估和基于强化学习任务的持续评估。

CHECKPOINT-BASED              CONTINUOUS
─────────────────             ──────────
[Task 1]                      [Work]
    │                            │
    ▼                            ▼
┌─────────┐                  ┌─────────┐
│Checkpoint│◄── verify       │ Timer/  │
│   #1     │    criteria     │ Change  │
└────┬────┘                  └────┬────┘
     │ pass?                      │
  ┌───┴───┐                       ▼
  │       │                  ┌──────────┐
 yes   no ──► fix ──┐       │Run Tests │
  │               │  │       │ + Lint   │
  ▼           └────┘ │       └────┬─────┘
[Task 2]              │            │
    │                 │       ┌────┴────┐
    ▼                 │       │         │
┌─────────┐           │      pass     fail
│Checkpoint│          │       │         │
│   #2     │          │       ▼         ▼
└────┬────┘           │   [Continue]  [Stop & Fix]
     │                │
    ...            └────┘

Best for: Linear workflows    Best for: Long sessions
with clear milestones         exploratory refactoring

基于检查点的评估:

  • 在工作流中设置明确的检查点
  • 在每个检查点根据定义的标准进行验证
  • 如果验证失败,Claude 必须在继续之前修复
  • 适合具有明确里程碑的线性工作流

持续评估:

  • 每 N 分钟或在重大变更后运行
  • 完整测试套件、构建状态、lint 检查
  • 立即报告回归
  • 在继续之前停下来修复
  • 适合长时间运行的会话

决定因素是你工作的性质。基于检查点适用于具有明确阶段的功能实现。持续评估适用于探索性重构或维护,因为你没有明确的里程碑。

我会说,通过一些干预,验证方法足以避免大部分技术债务。让 Claude 在完成任务后通过运行技能和 PostToolUse 钩子来验证有助于此。持续更新代码地图也有帮助,因为它保持了变更日志以及代码地图如何随时间演变的记录,作为仓库本身之外的事实来源。通过严格的规则,Claude 会避免创建杂乱的随机 .md 文件、为相似代码创建重复文件,以及留下一片死代码的荒原。

评分器类型(来自 Anthropic - 直接链接)

基于代码的评分器(Code-Based Graders): 字符串匹配、二元测试、静态分析、结果验证。快速、便宜、客观,但对有效变体很脆弱。

基于模型的评分器(Model-Based Graders): 评分标准打分、自然语言断言、成对比较。灵活且能处理微妙之处,但非确定性且更昂贵。

人工评分器(Human Graders): 专家审查、众包判断、抽样检查。金标准质量,但昂贵且缓慢。

关键指标:

pass@k: At least ONE of k attempts succeeds
┌─────────────────────────────────────┐
│ k=1: 70%    k=3: 91%    k=5: 97%   │
│ Higher k = higher odds of success   │
└─────────────────────────────────────┘

pass^k: ALL k attempts must succeed
┌─────────────────────────────────────┐
│ k=1: 70%    k=3: 34%    k=5: 17%   │
│ Higher k = harder (consistency)     │
└─────────────────────────────────────┘

当你只需要它能工作且任何验证反馈就足够时,使用 pass@k。当一致性至关重要且你需要接近确定性的输出一致性(在结果/质量/风格方面)时,使用 pass^k

构建评估路线图(来自同一 Anthropic 指南):

  • 尽早开始——从真实失败中提取20-50个简单任务
  • 将用户报告的失败转化为测试用例
  • 编写明确的任务——两个专家应达成相同结论
  • 构建平衡的问题集——测试行为应该和不应该出现的情况
  • 构建健壮的测试框架——每次试验从干净环境开始
  • 评分代理产生的结果,而非它走过的路径
  • 阅读许多试验的记录
  • 监控饱和度——100%通过率意味着需要添加更多测试

Parallelization

并行化

在多 Claude 终端设置中分叉对话时,确保分叉中的操作范围和原始对话的操作范围都有明确定义。在代码变更方面尽量减少重叠。选择彼此正交的任务以防止干扰的可能性。

我的首选模式:

个人而言,我更喜欢主聊天用于处理代码变更,而我做的分叉是用于我对代码库及其当前状态的问题,或者做外部服务的研究,比如拉取文档、在 GitHub 上搜索适用的开源仓库来帮助任务,或其他有用的一般性研究。

关于任意终端数量:

Boris @bcherny(创建 Claude Code 的传奇人物)有一些关于并行化的建议,我对此有赞同也有不赞同的地方。他建议过类似在本地运行5个 Claude 实例和5个上游实例这样的事情。我建议不要设置这样的任意终端数量。增加一个终端和增加一个实例应该出于真正的必要性和目的。如果你可以用脚本来处理该任务,就用脚本。如果你可以留在主聊天中让 Claude 在 tmux 中启动一个实例并在单独的终端中流式传输,那就那样做。

引用推文:Boris Cherny @bcherny · 1月3日 回复 @bcherny 1/ 我在终端中并行运行5个 Claude。我给我的标签页编号1-5,并使用系统通知来知道何时 Claude 需要输入 code.claude.com/docs/en/ter…

image.png

你的目标真的应该是:用最小可行的并行化量完成尽可能多的工作。

对于大多数新手,我甚至建议在你熟练掌握单实例运行和管理一切之前远离并行化。我不是主张限制自己——我是说要小心。大多数时候,即使是我也只使用大约4个终端。我发现通常只需打开2或3个 Claude 实例就能完成大部分事情。

扩展实例时:

如果你要开始扩展你的实例,并且有多个 Claude 实例在相互重叠的代码上工作,使用 git worktrees 并为每个实例制定非常明确的计划是必不可少的。此外,为了在恢复会话时不会混淆或迷失哪个 git worktree 是做什么的(除了树的名称之外),使用 /rename <name here> 来命名你所有的聊天。

Git Worktrees 用于并行实例:

# Create worktrees for parallel work
git worktree add ../project-feature-a feature-a
git worktree add ../project-feature-b feature-b
git worktree add ../project-refactor refactor-branch

# Each worktree gets its own Claude instance
cd ../project-feature-a && claude

好处:

  • 实例之间没有 git 冲突
  • 每个都有干净的工作目录
  • 容易比较输出
  • 可以在不同方法之间对同一任务进行基准测试

级联方法(The Cascade Method):

当运行多个 Claude Code 实例时,使用"级联"模式来组织:

  • 在右侧的新标签页中打开新任务
  • 从左到右扫描,从最旧到最新
  • 保持一致的方向流
  • 根据需要检查特定任务
  • 一次最多关注3-4个任务——超过这个数量,心理开销的增长速度会快于生产力

Groundwork

基础工作

从零开始时,实际的基础非常重要。这应该是显而易见的,但随着代码库的复杂性和规模增加,技术债务也会增加。管理它非常重要,如果你遵循一些规则,其实并不困难。除了为当前项目有效配置你的 Claude 之外(参见简明指南)。

双实例启动模式(The Two-Instance Kickoff Pattern):

对于我自己的工作流管理(不是必须的但很有帮助),我喜欢用2个打开的 Claude 实例来启动一个空仓库。

实例 1:脚手架代理(Scaffolding Agent)

  • 负责搭建脚手架和基础工作
  • 创建项目结构
  • 配置设置(CLAUDE.md、规则、代理——简明指南中的所有内容)
  • 建立约定
  • 把骨架搭好

实例 2:深度研究代理(Deep Research Agent)

  • 连接到你所有的服务、网络搜索等
  • 创建详细的 PRD(产品需求文档)
  • 创建架构 mermaid 图
  • 用实际文档中的实际片段编译参考资料

image.png

启动配置:左侧终端用于编码,右侧终端用于提问——使用 /rename 和 /fork。

最小化启动所需的内容就够了——这比每次都用 Context7 或喂链接让它抓取或使用 Firecrawl MCP 站点要快。当你已经深入某件事情且 Claude 明显语法出错或使用过时的函数或端点时,那些方法才派上用场。

llms.txt 模式:

如果可用的话,你可以在许多文档参考上通过在到达其文档页面后访问 /llms.txt 来找到一个 llms.txt 文件。这是一个示例:www.helius.dev/docs/llms.t…

这给你一个干净的、为 LLM 优化的文档版本,你可以直接提供给 Claude。

理念:构建可复用模式(Philosophy: Build Reusable Patterns)

来自 @omarsar0 的一个我完全认同的见解:"早期,我花时间构建可复用的工作流/模式。构建起来很繁琐,但随着模型和代理框架的改进,这产生了疯狂的复合效应。"

值得投资的方面:

  • 子代理(简明指南)
  • 技能(简明指南)
  • 命令(简明指南)
  • 计划模式
  • MCP 工具(简明指南)
  • 上下文工程模式

为什么会产生复合效应(@omarsar0):"最棒的部分是所有这些工作流都可以转移到其他代理,如 Codex。"一旦构建完成,它们可以跨模型升级工作。投资于模式 > 投资于特定模型的技巧。


Best Practices for Agents & Sub-Agents

代理和子代理的最佳实践

在简明指南中,我列出了子代理结构——planner、architect、tdd-guide、code-reviewer 等。在这一部分,我们关注编排和执行层。

子代理上下文问题(The Sub-Agent Context Problem):

子代理的存在是为了通过返回摘要而非倾倒所有内容来节省上下文。但编排器(orchestrator)拥有子代理所缺乏的语义上下文。子代理只知道字面上的查询,不知道请求背后的目的/推理。摘要通常会遗漏关键细节。

来自 @PerceptualPeak 的类比:"你的老板让你去参加一个会议并要求你做个总结。你回来给他汇报情况。十次有九次,他会有后续问题。你的总结不会包含他需要的所有内容,因为你没有他拥有的隐含上下文。"

迭代检索模式(Iterative Retrieval Pattern):

┌─────────────────┐
│  ORCHESTRATOR   │
│  (has context)  │
└────────┬────────┘
         │ dispatch with query + objective
         ▼
┌─────────────────┐
│   SUB-AGENT     │
│ (lacks context) │
└────────┬────────┘
         │ returns summary
         ▼
┌─────────────────┐    ┌─────────────┐
│    EVALUATE     │─no──►│  FOLLOW-UP  │
│   Sufficient?   │    │  QUESTIONS  │
└────────┬────────┘    └──────┬──────┘
         │ yes                │ sub-agent
         ▼                    │ fetches answers
      [ACCEPT]                │
         ◄──────────────────────┘
                        (max 3 cycles)

要修复这个问题,让编排器:

  • 评估每个子代理的返回结果
  • 在接受之前询问后续问题
  • 子代理回到源头,获取答案,返回
  • 循环直到充分(最多3个循环以防止无限循环)

传递目标上下文,而不仅仅是查询。 当派遣子代理时,同时包含具体查询和更广泛的目标。这有助于子代理确定在其摘要中优先包含什么内容。

模式:带有顺序阶段的编排器(Pattern: Orchestrator with Sequential Phases)

Phase 1: RESEARCH (use Explore agent)
- Gather context
- Identify patterns
- Output: research-summary.md

Phase 2: PLAN (use planner agent)
- Read research-summary.md
- Create implementation plan
- Output: plan.md

Phase 3: IMPLEMENT (use tdd-guide agent)
- Read plan.md
- Write tests first
- Implement code
- Output: code changes

Phase 4: REVIEW (use code-reviewer agent)
- Review all changes
- Output: review-comments.md

Phase 5: VERIFY (use build-error-resolver if needed)
- Run tests
- Fix issues
- Output: done or loop back

关键规则:

  • 每个代理获得一个明确的输入并产生一个明确的输出
  • 输出成为下一阶段的输入
  • 永远不要跳过阶段——每个阶段都有价值
  • 在代理之间使用 /clear 保持上下文新鲜
  • 将中间输出存储在文件中(不仅仅是记忆中)

代理抽象层级列表(Agent Abstraction Tierlist)(来自 @menhguin):

第1层:直接增益(容易使用)

  • 子代理(Subagents) ——防止上下文腐烂和临时专业化的直接增益。只有多代理一半的用处,但复杂度低得多
  • 元提示(Metaprompting) ——"我花3分钟来提示一个20分钟的任务。"直接增益——提高稳定性并对假设进行健全性检查
  • 在开始时多问用户(Asking user more at the beginning) ——通常是增益,尽管你必须在计划模式中回答问题

第2层:高技能门槛(较难用好)

  • 长时间运行的代理(Long-running agents) ——需要理解15分钟任务 vs 1.5小时 vs 4小时任务的形态和权衡。需要一些调整,而且显然是非常长的试错过程
  • 并行多代理(Parallel multi-agent) ——方差非常高,仅在高度复杂或分段良好的任务上有用。"如果2个任务需要10分钟,而你花了任意时间在提示上,或者更糟糕的是合并变更,那就适得其反了"
  • 基于角色的多代理(Role-based multi-agent) ——"模型演进太快,硬编码的启发式规则除非套利非常高否则没意义。"难以测试
  • 计算机使用代理(Computer use agents) ——非常早期的范式,需要大量调教。"你在让模型做一年前它们绝对不应该做的事情"

要点:从第1层模式开始。只有在掌握了基础知识并有真正的需求时才升级到第2层。


Tips and Tricks

提示与技巧

一些 MCP 是可替代的,将释放你的上下文窗口

方法如下。

对于版本控制(GitHub)、数据库(Supabase)、部署(Vercel、Railway)等 MCP——这些平台中的大多数已经有健壮的 CLI,MCP 本质上只是对它们的包装。MCP 是一个不错的包装器,但它有成本。

要让 CLI 更像 MCP 一样工作,而实际上不使用 MCP(以及随之而来的上下文窗口缩减),考虑将功能捆绑到技能和命令中。剥离 MCP 暴露的使事情变得简单的工具,并将它们转化为命令。

示例:不要始终加载 GitHub MCP,而是创建一个 /gh-pr 命令来包装 gh pr create 并使用你偏好的选项。不要让 Supabase MCP 消耗上下文,而是创建直接使用 Supabase CLI 的技能。功能是一样的,便利性相似,但你的上下文窗口被释放出来用于实际工作。

这与我收到的一些其他问题相关。自从我发布原始文章以来的过去几天里,Boris 和 Claude Code 团队在记忆管理和优化方面取得了很大进展,主要是 MCP 的懒加载,使它们不再从一开始就消耗你的窗口。以前我会建议在可能的地方将 MCP 转换为技能,以两种方式之一卸载执行 MCP 的功能:在当时启用它(不太理想,因为你需要离开并恢复会话)或者拥有使用 MCP 的 CLI 类似物的技能(如果它们存在的话),让技能成为它的包装器——本质上让它充当伪 MCP。

通过懒加载,上下文窗口问题基本上已经解决。但 token 使用和成本并没有以同样的方式解决。CLI + 技能方法仍然是一种 token 优化方法,其效果可能与使用 MCP 相当或接近。此外,你可以通过 CLI 而不是在上下文中运行 MCP 操作,这显著减少了 token 使用,对于数据库查询或部署等繁重的 MCP 操作特别有用。

Everything Claude Code 速查指南

作者 王小酱
2026年2月27日 16:20

这是黑客松冠军日常使用 10 个月后的完整配置:技能(skills)、钩子(hooks)、子代理(subagents)、MCP、插件(plugins),以及真正好用的部分。

自从 2 月份实验性发布以来,我一直是 Claude Code 的深度用户,并且在 Anthropic x Forum Ventures 黑客松上与 @DRodriguezFX 一起完全使用 Claude Code 构建了 Zenith 并获胜。

cogsec @affaanmustafa · 2025年9月16日

在纽约的 @AnthropicAI x @forumventures 黑客松上拿下了冠军,感谢主办方举办这次精彩的活动(还有 15k 的 Anthropic Credits)

@DRodriguezFX 和我构建了 PMFProbe,帮助创始人从 0 到 1,在 MVP 阶段前验证你的想法,后续还有更多内容

图片1图片2图片3图片4


Skills and Commands

技能与命令

技能类似于规则,被限定在特定的作用域和工作流中。它们是执行特定工作流时的快捷提示词。

在用 Opus 4.5 长时间编码后,想清理无用代码和零散的 .md 文件?运行 /refactor-clean。需要测试?/tdd/e2e/test-coverage。技能和命令可以在一条提示词中链式调用。

链式调用命令链式调用命令

我可以创建一个在检查点更新代码地图(codemap)的技能——让 Claude 快速导航你的代码库,而不必在探索上消耗上下文。

~/.claude/skills/codemap-updater.md

命令是通过斜杠命令执行的技能。它们有重叠,但存储方式不同:

  • Skills: ~/.claude/skills - 更广泛的工作流定义
  • Commands: ~/.claude/commands - 快速可执行的提示词
# Example skill structure
~/.claude/skills/
  pmx-guidelines.md           # Project-specific patterns
  coding-standards.md          # Language best practices
  tdd-workflow/                # Multi-file skill with README.md
  security-review/             # Checklist-based skill

Hooks

钩子

钩子是基于触发器的自动化,在特定事件上触发。与技能不同,它们被限定在工具调用和生命周期事件中。

钩子类型

  • PreToolUse - 工具执行前(验证、提醒)
  • PostToolUse - 工具完成后(格式化、反馈循环)
  • UserPromptSubmit - 当你发送消息时
  • Stop - 当 Claude 完成响应时
  • PreCompact - 上下文压缩前
  • Notification - 权限请求

示例:在运行长时间命令前提醒使用 tmux

{
  "PreToolUse": [
    {
      "matcher": "tool == \"Bash\" && tool_input.command matches \"(npm|pnpm|yarn|cargo|pytest)\"",
      "hooks": [
        {
          "type": "command",
          "command": "if [ -z \"$TMUX\" ]; then echo '[Hook] Consider tmux for session persistence' >&2; fi"
        }
      ]
    }
  ]
}

image.png

在 Claude Code 中运行 PostToolUse 钩子时获得的反馈示例

专业提示: 使用 hookify 插件通过对话方式创建钩子,而不是手动编写 JSON。运行 /hookify 并描述你想要的功能。


Subagents

子代理

子代理是主编排器(主 Claude)可以委派任务的进程,具有有限的作用域。它们可以在后台或前台运行,为主代理释放上下文。

子代理与技能配合很好——一个能够执行你部分技能的子代理可以被委派任务并自主使用这些技能。它们还可以通过特定的工具权限进行沙盒化。

# Example subagent structure
~/.claude/agents/
  planner.md                   # Feature implementation planning
  architect.md                 # System design decisions
  tdd-guide.md                 # Test-driven development
  code-reviewer.md             # Quality/security review
  security-reviewer.md         # Vulnerability analysis
  build-error-resolver.md
  e2e-runner.md
  refactor-cleaner.md

为每个子代理配置允许的工具、MCP 和权限,以实现合理的作用域划分。


Rules and Memory

规则与记忆

你的 .rules 文件夹存放 Claude 应该始终遵循的最佳实践 .md 文件。两种方式:

  • 单一 CLAUDE.md - 所有内容放在一个文件中(用户级或项目级)
  • Rules 文件夹 - 按关注点分组的模块化 .md 文件
~/.claude/rules/
  security.md                  # No hardcoded secrets, validate inputs
  coding-style.md              # Immutability, file organization
  testing.md                   # TDD workflow, 80% coverage
  git-workflow.md              # Commit format, PR process
  agents.md                    # When to delegate to subagents
  performance.md               # Model selection, context management

示例规则:

  • 代码库中不使用 emoji
  • 前端避免紫色调
  • 部署前始终测试代码
  • 优先使用模块化代码而非超大文件
  • 永远不要提交 console.log

MCPs (Model Context Protocol)

MCP(模型上下文协议)

MCP 将 Claude 直接连接到外部服务。它不是 API 的替代品——而是围绕 API 的提示驱动封装,在信息导航方面提供更多灵活性。

示例: Supabase MCP 让 Claude 可以拉取特定数据、直接在上游运行 SQL,无需复制粘贴。数据库、部署平台等同理。

Supabase MCP 列出公共 schema 中的表的示例Supabase MCP 列出公共 schema 中的表的示例

Chrome in Claude: 是一个内置的插件 MCP,让 Claude 可以自主控制你的浏览器——点击查看各种功能。

关键:上下文窗口管理

对 MCP 要精挑细选。我把所有 MCP 放在用户配置中,但禁用所有不用的。导航到 /plugins 并向下滚动或运行 /mcp

你 200k 的上下文窗口在压缩前可能只有 70k,如果启用了太多工具的话。性能会显著下降。

使用 /plugins 导航到 MCP 查看当前已安装的及其状态使用 /plugins 导航到 MCP 查看当前已安装的及其状态

经验法则: 配置中放 20-30 个 MCP,但保持启用的不超过 10 个 / 活跃工具不超过 80 个。


Plugins

插件

插件将工具打包以便于安装,而不需要繁琐的手动设置。一个插件可以是技能 + MCP 的组合,或者钩子/工具的捆绑。

安装插件:

# Add a marketplace
claude plugin marketplace add https://github.com/mixedbread-ai/mgrep
# Open Claude, run /plugins, find new marketplace, install from there

显示新安装的 Mixedbread-Grep 市场显示新安装的 Mixedbread-Grep 市场

LSP 插件: 如果你经常在编辑器外运行 Claude Code,这特别有用。语言服务协议(Language Server Protocol)为 Claude 提供实时类型检查、跳转到定义和智能补全,无需打开 IDE。

# Enabled plugins example
typescript-lsp@claude-plugins-official     # TypeScript intelligence
pyright-lsp@claude-plugins-official        # Python type checking
hookify@claude-plugins-official            # Create hooks conversationally
mgrep@Mixedbread-Grep                      # Better search than ripgrep

与 MCP 同样的警告——注意你的上下文窗口。


Tips and Tricks

技巧和窍门

快捷键

  • Ctrl+U - 删除整行(比疯狂按退格快)
  • ! - 快速 bash 命令前缀
  • @ - 搜索文件
  • / - 启动斜杠命令
  • Shift+Enter - 多行输入
  • Tab - 切换思考模式显示
  • Esc Esc - 中断 Claude / 恢复代码

并行工作流

/fork - 分叉对话,在不重叠的任务上并行工作,而不是排队发送消息

Git Worktrees - 用于有重叠的并行 Claude,避免冲突。每个 worktree 是独立的检出

git worktree add ../feature-branch feature-branch
# Now run separate Claude instances in each worktree

tmux 用于长时间运行的命令: 流式查看和监控 Claude 运行的日志/bash 进程。

tmux new -s dev
# Claude runs commands here, you can detach and reattach
tmux attach -t dev

mgrep > grep: mgrep 比 ripgrep/grep 有显著提升。通过插件市场安装,然后使用 /mgrep 技能。支持本地搜索和网络搜索。

mgrep "function handleSubmit"                    # Local search
mgrep --web "Next.js 15 app router changes"      # Web search

其他有用命令

  • /rewind - 回到之前的状态
  • /statusline - 自定义显示分支、上下文百分比、待办事项
  • /checkpoints - 文件级撤销点
  • /compact - 手动触发上下文压缩

GitHub Actions CI/CD

在你的 PR 上用 GitHub Actions 设置代码审查。配置后 Claude 可以自动审查 PR。

Claude 批准一个 bug 修复 PRClaude 批准一个 bug 修复 PR

沙盒化

对有风险的操作使用沙盒模式——Claude 在受限环境中运行,不影响你的实际系统。(使用 --dangerously-skip-permissions 则相反,让 Claude 自由操作,如果不小心的话可能会造成破坏。)


On Editors

关于编辑器

虽然不需要编辑器,但它可能正面或负面地影响你的 Claude Code 工作流。虽然 Claude Code 可以在任何终端上工作,但配合一个强大的编辑器可以解锁实时文件追踪、快速导航和集成命令执行。

Zed(我的首选)

我使用 Zed——一个基于 Rust 的编辑器,轻量、快速、高度可定制。

为什么 Zed 与 Claude Code 配合良好:

  • Agent Panel Integration(代理面板集成) - Zed 的 Claude 集成让你在 Claude 编辑时实时追踪文件变更。在 Claude 引用的文件间跳转,无需离开编辑器
  • Performance(性能) - 用 Rust 编写,即时打开,处理大型代码库无延迟
  • CMD+Shift+R Command Palette(命令面板) - 在可搜索的 UI 中快速访问所有自定义斜杠命令、调试器和工具。即使你只想运行一个快速命令而不切换到终端
  • Minimal Resource Usage(最小资源占用) - 在繁重操作期间不会与 Claude 争夺系统资源
  • Vim Mode(Vim 模式) - 完整的 vim 键绑定

Zed 编辑器使用 CMD+Shift+R 的自定义命令下拉菜单。右下角靶心图标显示跟随模式。Zed 编辑器使用 CMD+Shift+R 的自定义命令下拉菜单。右下角靶心图标显示跟随模式。

  • Split your screen(分屏) - 一边终端运行 Claude Code,一边编辑器
  • Ctrl + G - 在 Zed 中快速打开 Claude 正在编辑的文件
  • Auto-save(自动保存) - 启用自动保存,确保 Claude 的文件读取始终是最新的
  • Git integration(Git 集成) - 使用编辑器的 Git 功能在提交前审查 Claude 的更改
  • File watchers(文件监视器) - 大多数编辑器自动重新加载已更改的文件,确认已启用

VSCode / Cursor

这也是一个可行的选择,与 Claude Code 配合良好。你可以以终端格式使用,通过 \ide 启用 LSP 功能实现与编辑器的自动同步(现在与插件有些冗余)。或者你可以选择扩展版本,它与编辑器集成度更高,有匹配的 UI。

来自文档来自文档,地址:code.claude.com/docs/en/vs-…


My Setup

我的配置

插件

已安装:(我通常同时只启用其中 4-5 个)

ralph-wiggum@claude-code-plugins         # Loop automation
frontend-design@claude-code-plugins      # UI/UX patterns
commit-commands@claude-code-plugins      # Git workflow
security-guidance@claude-code-plugins    # Security checks
pr-review-toolkit@claude-code-plugins    # PR automation
typescript-lsp@claude-plugins-official   # TS intelligence
hookify@claude-plugins-official          # Hook creation
code-simplifier@claude-plugins-official
feature-dev@claude-code-plugins
explanatory-output-style@claude-code-plugins
code-review@claude-code-plugins
context7@claude-plugins-official         # Live documentation
pyright-lsp@claude-plugins-official      # Python types
mgrep@Mixedbread-Grep                    # Better search

MCP 服务器

已配置(用户级):

{
  "github": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"]
  },
  "firecrawl": {
    "command": "npx",
    "args": ["-y", "firecrawl-mcp"]
  },
  "supabase": {
    "command": "npx",
    "args": ["-y", "@supabase/mcp-server-supabase@latest", "--project-ref=YOUR_REF"]
  },
  "memory": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-memory"]
  },
  "sequential-thinking": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"]
  },
  "vercel": {
    "type": "http",
    "url": "https://mcp.vercel.com"
  },
  "railway": {
    "command": "npx",
    "args": ["-y", "@railway/mcp-server"]
  },
  "cloudflare-docs": {
    "type": "http",
    "url": "https://docs.mcp.cloudflare.com/mcp"
  },
  "cloudflare-workers-bindings": {
    "type": "http",
    "url": "https://bindings.mcp.cloudflare.com/mcp"
  },
  "cloudflare-workers-builds": {
    "type": "http",
    "url": "https://builds.mcp.cloudflare.com/mcp"
  },
  "cloudflare-observability": {
    "type": "http",
    "url": "https://observability.mcp.cloudflare.com/mcp"
  },
  "clickhouse": {
    "type": "http",
    "url": "https://mcp.clickhouse.cloud/mcp"
  },
  "AbletonMCP": {
    "command": "uvx",
    "args": ["ableton-mcp"]
  },
  "magic": {
    "command": "npx",
    "args": ["-y", "@magicuidesign/mcp@latest"]
  }
}

按项目禁用(上下文窗口管理):

# In ~/.claude.json under projects.[path].disabledMcpServers
disabledMcpServers: [
  "playwright",
  "cloudflare-workers-builds",
  "cloudflare-workers-bindings",
  "cloudflare-observability",
  "cloudflare-docs",
  "clickhouse",
  "AbletonMCP",
  "context7",
  "magic"
]

这是关键——我配置了 14 个 MCP,但每个项目只启用约 5-6 个。保持上下文窗口健康。

关键钩子

{
  "PreToolUse": [
    // tmux reminder for long-running commands
    {
      "matcher": "npm|pnpm|yarn|cargo|pytest",
      "hooks": ["tmux reminder"]
    },
    // Block unnecessary .md file creation
    {
      "matcher": "Write && .md file",
      "hooks": ["block unless README/CLAUDE"]
    },
    // Review before git push
    {
      "matcher": "git push",
      "hooks": ["open editor for review"]
    }
  ],
  "PostToolUse": [
    // Auto-format JS/TS with Prettier
    {
      "matcher": "Edit && .ts/.tsx/.js/.jsx",
      "hooks": ["prettier --write"]
    },
    // TypeScript check after edits
    {
      "matcher": "Edit && .ts/.tsx",
      "hooks": ["tsc --noEmit"]
    },
    // Warn about console.log
    {
      "matcher": "Edit",
      "hooks": ["grep console.log warning"]
    }
  ],
  "Stop": [
    // Audit for console.logs before session ends
    {
      "matcher": "*",
      "hooks": ["check modified files for console.log"]
    }
  ]
}

自定义状态栏

显示用户、目录、git 分支(带脏标记)、剩余上下文百分比、模型、时间和待办事项数量:

Mac 根目录中的示例状态栏Mac 根目录中的示例状态栏

规则结构

~/.claude/rules/
  security.md          # Mandatory security checks
  coding-style.md      # Immutability, file size limits
  testing.md           # TDD, 80% coverage
  git-workflow.md      # Conventional commits
  agents.md            # Subagent delegation rules
  patterns.md          # API response formats
  performance.md       # Model selection (Haiku vs Sonnet vs Opus)
  hooks.md             # Hook documentation

子代理

~/.claude/agents/
  planner.md             # Break down features
  architect.md           # System design
  tdd-guide.md           # Write tests first
  code-reviewer.md       # Quality review
  security-reviewer.md   # Vulnerability scan
  build-error-resolver.md
  e2e-runner.md          # Playwright tests
  refactor-cleaner.md    # Dead code removal
  doc-updater.md         # Keep docs synced

Key Takeaways

关键要点

  • 不要过度复杂化 - 把配置当作微调,而非架构设计
  • 上下文窗口很宝贵 - 禁用未使用的 MCP 和插件
  • 并行执行 - 分叉对话,使用 git worktrees
  • 自动化重复工作 - 用钩子处理格式化、代码检查、提醒
  • 限定子代理的作用域 - 有限的工具 = 专注的执行

References

参考链接


注: 这只是部分细节。如果大家感兴趣,我可能会发更多关于具体内容的帖子。

【AI 编程实战】第 12 篇:从 0 到 1 的回顾 - 项目总结与 AI 协作心得

作者 HashTang
2026年2月27日 13:25

12 篇文章、一个完整的小程序、从需求分析到性能优化,这是我和 AI 协作开发心动恋聊的全过程。这篇文章作为系列收官,分享项目的完整回顾、AI 协作的心得体会、以及对未来开发模式的思考。

系列专栏【AI 编程实战】专栏目录

本篇主题:项目总结与 AI 协作心得

实战项目:心动恋聊 - AI 恋爱聊天助手

一、项目回顾:从 0 到 1 的历程

1.1 项目概览

心动恋聊是一个 AI 恋爱聊天助手小程序,帮助用户在社交场景中获得更好的聊天回复建议。

📊 项目规模:

技术栈:
- 前端:UniApp + Vue 3 + TypeScript + Pinia
- 后端:Next.js + Prisma + AI API
- UI:UnoCSS + UView Pro

代码量:
- 前端代码:~15,000 行
- 组件数量:30+ 个
- 页面数量:15+ 个
- Hooks:10+ 个

开发周期:
- 总耗时:约 4 周
- 迭代次数:3 个大版本

1.2 系列文章脉络

📚 12 篇文章的完整脉络:

【基础搭建】(第 1-5 篇)
├── 第 1 篇:项目启动 - 需求分析、技术选型、AI 配置
├── 第 2 篇:创建项目 - UniApp 项目初始化与配置
├── 第 3 篇:页面结构 - 首页布局与 TabBar 配置
├── 第 4 篇:样式系统 - UnoCSS 原子化 CSS 实战
└── 第 5 篇:状态管理 - Pinia 持久化与用户状态

【核心功能】(第 6-9 篇)
├── 第 6 篇:网络请求 - HTTP 封装与拦截器设计
├── 第 7 篇:登录流程 - 微信授权与多步骤弹窗
├── 第 8 篇:组件封装 - 可复用组件设计方法
└── 第 9 篇:Hooks 封装 - 逻辑复用的最佳实践

【质量保障】(第 10-12 篇)
├── 第 10 篇:错误处理 - 防御性编程与边界情况
├── 第 11 篇:性能优化 - 分包、懒加载、缓存策略
└── 第 12 篇:项目总结 - 回顾与 AI 协作心得(本篇)

1.3 每篇文章的核心产出

篇章 主题 核心产出
第 1 篇 项目启动 需求文档、技术架构图
第 2 篇 创建项目 项目脚手架、目录结构
第 3 篇 页面结构 首页布局、TabBar 配置
第 4 篇 样式系统 UnoCSS 配置、主题色方案
第 5 篇 状态管理 userStore、持久化方案
第 6 篇 网络请求 HTTP 封装、拦截器
第 7 篇 登录流程 LoginModal、多步骤流程
第 8 篇 组件封装 XButton、Modal、VipCard
第 9 篇 Hooks useRequest、useUpload
第 10 篇 错误处理 Toast 封装、防御性编程
第 11 篇 性能优化 分包、懒加载、缓存
第 12 篇 项目总结 方法论、心得体会

二、AI 协作的正确姿势

2.1 什么样的对话最有效

通过 12 篇文章的实践,我总结出和 AI 对话的几个关键点:

❌ 低效的对话方式:

我:帮我写一个登录功能

这样的提问太宽泛,AI 不知道:

  • 是什么平台?小程序/H5/App?
  • 用什么登录方式?微信/手机号/密码?
  • 登录后跳哪里?需要什么回调?
  • UI 是弹窗还是页面?

✅ 高效的对话方式:

我:需要设计一套登录系统,要求:
    1. 微信小程序环境
    2. 支持微信登录 + 手机号授权
    3. 新用户要引导填性别和年龄
    4. 任意页面都能触发登录弹窗
    5. 登录成功后能执行回调

这样 AI 能给出精准的方案,因为:

  • 明确了环境(小程序)
  • 明确了功能(微信登录 + 手机号)
  • 明确了流程(新用户引导)
  • 明确了交互(弹窗 + 回调)

2.2 对话的层次感

我发现最有效的对话是分层推进的:

第一轮:说清楚要做什么
我:需要封装一个通用的请求 Hook

第二轮:AI 询问细节,我补充
AI:需要支持哪些功能?immediate?初始数据?
我:需要立即执行选项,需要初始数据

第三轮:AI 给出设计,我确认
AI:我来设计接口结构...
我:开始吧

第四轮:AI 生成代码,我追问
AI:(生成代码)
我:为什么用 Promise 链式而不是 try-catch?

第五轮:AI 解释原理,我学到了
AI:两种写法对比...

这种层层递进的对话,比一次性给出"完美 Prompt"更有效,因为:

  1. 渐进明确:需求是在对话中逐步清晰的
  2. 双向确认:AI 的设计决策需要你确认
  3. 深度学习:追问"为什么"让你真正理解

2.3 AI 不是银弹

在实践中,我也发现了 AI 的局限:

📊 AI 擅长的事:

✅ 生成样板代码(CRUD、配置)
✅ 解释技术概念
✅ 分析代码问题
✅ 提供多种方案对比
✅ 重构和优化建议

📊 AI 不擅长的事:

❌ 理解业务上下文(需要你提供)
❌ 做产品决策(需要你判断)
❌ 处理边界情况(需要你验证)
❌ 了解项目历史(需要你说明)

核心认知:AI 是工具,不是替代品。你需要:

  • 清晰地描述需求
  • 评估 AI 给出的方案
  • 验证生成的代码
  • 理解背后的原理

三、效率提升的真实数据

3.1 开发效率对比

📊 单项任务耗时对比:

| 任务 | 传统方式 | AI 辅助 | 提升倍数 |
|------|---------|---------|----------|
| HTTP 封装 | 4 小时 | 1 小时 | 4x |
| 登录弹窗 | 8 小时 | 3 小时 | 2.7x |
| 组件封装 | 6 小时 | 2 小时 | 3x |
| Hooks 设计 | 4 小时 | 1.5 小时 | 2.7x |
| 错误处理 | 3 小时 | 1 小时 | 3x |
| 性能优化 | 6 小时 | 2 小时 | 3x |

3.2 效率提升的来源

📊 效率提升分析:

1. 减少"从零开始"的时间
   - 传统:Google 搜索 → 看文档 → 试错
   - AI:描述需求 → 获得可用代码 → 微调

2. 减少"踩坑"的时间
   - 传统:遇到问题 → Stack Overflow → 找答案
   - AI:描述问题 → 获得解决方案 → 理解原因

3. 减少"重复劳动"
   - 传统:复制粘贴 → 手动修改
   - AI:描述模式 → 批量生成

4. 加速"学习理解"
   - 传统:看源码 → 猜测用法
   - AI:问"为什么" → 获得解释

3.3 质量提升的体现

📊 代码质量对比:

【代码规范性】
- 一致的命名风格
- 完整的类型定义
- 合理的代码注释

【架构合理性】
- 清晰的分层设计
- 合理的职责划分
- 可扩展的接口设计

【可维护性】
- 抽象复用的组件
- 封装良好的 Hooks
- 统一的错误处理

四、最佳实践总结

4.1 项目结构最佳实践

📂 推荐的项目结构:

src/
├── api/              # API 接口定义
│   ├── user.ts
│   └── chat.ts
├── components/       # 通用组件
│   ├── XButton.vue
│   ├── Modal.vue
│   └── LoadingIndicator.vue
├── composables/      # 组合式函数
│   ├── useLoginFlow.ts
│   └── useSystemInfo.ts
├── hooks/            # 通用 Hooks
│   ├── useRequest.ts
│   └── useUpload.ts
├── http/             # HTTP 封装
│   ├── http.ts
│   ├── interceptor.ts
│   └── types.ts
├── pages/            # 页面
│   ├── index/
│   └── my/
├── store/            # 状态管理
│   ├── user.ts
│   └── loginModal.ts
├── subPackages/      # 分包
│   ├── vip/
│   └── agreement/
└── utils/            # 工具函数
    ├── toast.ts
    └── platform.ts

4.2 代码规范最佳实践

// ✅ 推荐的代码风格

// 1. 类型定义清晰
interface Props {
  text?: string;
  loading?: boolean;
  disabled?: boolean;
}

// 2. 默认值合理
const props = withDefaults(defineProps<Props>(), {
  text: '',
  loading: false,
  disabled: false,
});

// 3. 事件定义明确
const emit = defineEmits<{
  click: [];
  success: [data: any];
  error: [error: Error];
}>();

// 4. 计算属性缓存
const formattedData = computed(() =>
  rawData.value.map(item => ({
    ...item,
    displayName: formatName(item.name),
  }))
);

// 5. 错误处理完整
const handleSubmit = async () => {
  if (loading.value) return;

  loading.value = true;
  try {
    const res = await doSubmit();
    emit('success', res.data);
  } catch (error) {
    console.error('提交失败:', error);
    toast.error('提交失败,请重试');
  } finally {
    loading.value = false;
  }
};

4.3 AI 协作最佳实践

📋 AI 协作清单:

【开始前】
□ 明确功能需求
□ 了解技术约束
□ 准备上下文信息

【对话中】
□ 分层描述需求
□ 确认设计方案
□ 追问实现原理
□ 要求代码解释

【生成后】
□ 阅读理解代码
□ 验证功能正确
□ 检查边界情况
□ 优化代码细节

五、踩过的坑与解决方案

5.1 常见问题汇总

📊 开发中遇到的典型问题:

1. Token 获取位置
   ❌ 从 Store 获取(拦截器执行时 Store 未初始化)
   ✅ 从 Storage 获取

2. 响应式数据依赖
   ❌ 静态对象引用 store 数据
   ✅ 使用 computed 保持响应式

3. 枚举类型存储
   ❌ 字符串存储('男'/'女')
   ✅ 数字枚举(1/2)

4. 条件编译位置
   ❌ 运行时判断平台
   ✅ 使用 #ifdef 编译时判断

5. 组件职责边界
   ❌ 组件内处理业务逻辑
   ✅ 组件只负责 UI,业务逻辑在 Store/Service

5.2 避坑指南

// 1. Token 获取
// ❌ 错误
const { token } = userStore.userInfo;

// ✅ 正确
const token = uni.getStorageSync('token');

// 2. 响应式依赖
// ❌ 错误
const menuItems = {
  label: userStore.genderDisplay
};

// ✅ 正确
const menuItems = computed(() => ({
  label: userStore.genderDisplay
}));

// 3. 平台判断
// ❌ 错误
if (process.env.UNI_PLATFORM === 'mp-weixin') {
  // ...
}

// ✅ 正确
// #ifdef MP-WEIXIN
// 小程序专用代码
// #endif

六、对未来的思考

6.1 AI 辅助开发的趋势

📊 我的观察:

【现在】
- AI 生成代码片段
- 人工整合和调试
- 需要理解才能用

【未来可能】
- AI 理解整个项目上下文
- 自动化测试和修复
- 更智能的代码审查

【不变的是】
- 需求分析能力
- 架构设计能力
- 问题诊断能力

6.2 开发者的核心竞争力

📊 AI 时代的开发者能力:

1. 问题定义能力
   - 把模糊需求转化为清晰描述
   - 识别真正要解决的问题

2. 方案评估能力
   - 评估 AI 给出的多种方案
   - 选择最适合当前场景的

3. 架构设计能力
   - 理解系统整体结构
   - 做出合理的技术决策

4. 持续学习能力
   - 通过 AI 加速学习新技术
   - 保持技术敏感度

七、系列总结

7.1 本系列的价值

📚 这个系列想传达的:

1. AI 辅助开发是可行的
   - 不是概念,是实践
   - 有真实的效率提升

2. 对话比 Prompt 更重要
   - 不是"写好 Prompt 就行"
   - 而是"多轮对话逐步明确"

3. 理解比复制更重要
   - 不是"复制代码就完了"
   - 而是"理解原理才能用好"

4. 人的判断不可替代
   - AI 是工具,不是替代
   - 技术决策需要人来做

7.2 给读者的建议

📋 如果你想开始 AI 辅助开发:

1. 从小项目开始
   - 不要一开始就用于生产项目
   - 先在 Side Project 中积累经验

2. 保持学习心态
   - 每次对话都是学习机会
   - 追问"为什么"比"给我代码"更重要

3. 建立验证习惯
   - AI 生成的代码要验证
   - 边界情况要自己考虑

4. 积累对话模式
   - 总结有效的对话方式
   - 建立自己的"提问模板"

7.3 最后的话

心动恋聊从一个想法,到一个完整的小程序,
再到这 12 篇文章,是我和 AI 协作的一次深度实践。

我最大的感受是:
AI 没有让编程变得"不需要思考",
反而让我更清晰地思考"该怎么做"。

因为你需要:
- 清晰地描述需求
- 评估多种方案
- 理解生成的代码
- 验证实际效果

这些,都需要思考。

希望这个系列对你有帮助。
如果有问题,欢迎评论区交流!

系列完结!

12 篇文章,完整记录了心动恋聊小程序从 0 到 1 的开发过程。

这不是教你"如何写 Prompt",而是展示如何和 AI 协作解决实际问题

如果这个系列对你有帮助,请点赞、收藏、转发!

AI辅助开发实战:会问问题比会写代码更重要

作者 牛奶
2026年2月26日 21:22

AI辅助开发实战:会问问题比会写代码更重要

系列第二篇。我想聊聊怎么用好 AI 这个工具。不是教你怎么敲代码,而是教你,怎么真正用好AI辅助开发工具。


原文地址

墨渊书肆/AI辅助开发实战:会问问题比会写代码更重要


你有没有过这样的经历?

打开Cursor(或者TraeCopilot),对着空白编辑器发了半天呆,不知道该让AI帮你干什么。

或者你问了一句「帮我写个登录功能」,AI 噼里啪啦写了一大堆代码,你看都看不懂,最后只能硬着头皮复制粘贴。

再或者,你问 AI:「这个报错是什么意思?」它回了一堆你看不懂的术语,你更迷茫了。

如果你有以上任何一种经历,这篇文章就是写给你的。


会问问题,比会写代码更重要

这是我最近一年用 AI 辅助开发最大的感悟。

以前我觉得,AI 嘛,就是个更聪明的搜索引擎。我不会的代码问它,它告诉我怎么写呗。

后来发现不是这么回事。

同样一个问题,不同的问法,AI 给出的答案质量可以差十倍。

AI 不会读心术。你得把自己的需求翻译成 AI 能理解的语言。

举两个例子感受一下:

第一种问法:「帮我写个登录功能。」

AI给你一个标准答案:用户名密码输入框、提交按钮、后端接口、数据库查询。看起来很全,但放到你的项目里可能完全不适用。你要改吧,改到猴年马月。不要吧,扔掉又可惜。

第二种问法:「我的项目用Next.jsPrisma,用户表字段是 email 和 passwordHash。请帮我写一个登录API,要支持邮箱密码登录,密码用 bcrypt 加密,返回 JWT token,7天有效期。」

AI给你的代码,直接就能用。稍微调一下就能跑。

这就是差距。好的 Prompt 不是更长的Prompt,而是更精确的 Prompt。


几个基本概念

在开始讲技巧之前,先简单说几个你经常会遇到的术语:

LLM:Large Language Model,大语言模型。你可以把LLM理解为"大脑",GPT、Claude、DeepSeek 都是 LLM。ChatGPT、Cursor背后的 AI 都是LLM在驱动。

Prompt:提示词,你给AI说的话。「帮我写个登录功能」就是一个Prompt。

Agent:你可以理解为"能自己干活"的AI。传统AI是你问一句它答一句,Agent 是你告诉它一个目标,它自己规划步骤去执行。Cursor 的 Agent 模式就是这个原理。

MCP:Model Context Protocol,模型上下文协议。这是 2024 年出来的一个标准,让 AI 能统一地访问外部工具和数据。比如 AI 可以通过 MCP 直接读取你电脑上的文件、查询你的数据库、控制浏览器。2026年的 Cursor 已经支持 MCP,用起来很方便。

Token:你可以理解为 AI 处理文字的"计量单位"。英文约4个字符=1个 Token,中文约1-2个汉字=1个 Token。

为什么要注意 Token?因为 AI API 是按 Token 收费的。你输入的文字要花钱,AI输出的文字也要花钱。知道这些就够了,继续往下看。


我的AI辅助开发经验

2026年了,AI辅助开发工具已经成为程序员的标配。CursorTraeCopilotOpenCode……不管你用哪个,核心技巧都是互通的。

我用了一年多,从一开始的「这有啥」到现在的「真香」,总结了一些真正有用的经验。

1. 搞清楚什么时候用什么模式

Cursor 有两个核心模式:AgentChat。用对了,效率翻倍;用错了,就是折磨。

Chat模式:你问一句,它答一句。像跟人聊天一样。

我一般用来:

  • 问具体问题:「这个报错是什么意思?」
  • 查知识点:「PostgreSQL的索引类型有哪些?」
  • 解释代码:「这个函数做了什么?」
  • 帮我想名字:「帮我给这个函数起个名字」

Agent模式:你描述一个任务,它自己去分析和改代码。威力更大,但需要把需求说清楚。

我一般用来:

  • 帮我重构整个模块:「把这个登录从JWT改成Session」
  • 帮我修bug:「登录一直返回401,帮我看看是什么原因」
  • 帮我转换代码:「把这个JavaScript文件改成TypeScript」
  • 帮我实现一个功能:「帮我实现用户注册功能,包含表单验证、数据库存储、发送欢迎邮件」

简单说:小问题用Chat,大任务用Agent。

2. 喂上下文是有技巧的

Cursor 最强的地方是它能理解你的整个项目。你打开一个文件,问它这个组件是做什么的,它能根据文件名、代码内容、项目结构给你答案。

但有时候它也会犯傻——给你一些牛头不对马嘴的回答。

这时候,你得学会喂上下文

我犯过的错误:

「怎么优化这个查询?」

AI回了半天,什么加索引、分页、缓存讲了一套,我根本不知道它说的是什么,因为我连我的表结构都没告诉它。

后来我学乖了:

「我的Prisma查询是这样的:prisma const users = await prisma.user.findMany() 数据量大概10万条,现在查询要3秒,请问怎么优化?」

这次AI直接告诉我:1. 加索引 2. 用select只查需要的字段 3. 考虑分页。

我的习惯是:至少告诉AI三件事

  1. 我用的技术栈是什么(Next.js + Prisma + PostgreSQL)
  2. 当前代码长什么样(贴上代码)
  3. 遇到了什么问题(查询慢、报错、不知道怎么做)

3. Tab键补全真的好用

大部分 AI 辅助开发工具都有代码补全功能,会预测你下一行要写什么。按 Tab 键直接采纳预测。

刚开始我还不太信这个功能,觉得 AI 哪有那么聪明。后来真香了。

我经常这样用:

  • 写TypeScript类型定义,AI能猜到我要的类型
  • 写React组件props,AI能帮我补全大部分
  • 写数据库schema,AI知道我想要什么字段
  • 写import语句,AI知道我要导入什么

10次有8次是准的,能省很多打字的时间。

4. 选中代码让AI帮你改(核心技巧)

选中一段代码,让AI帮你修改。这是一个通用技巧,大部分工具都支持,只是快捷键不太一样。

这是我最常用的功能,没有之一。

比如我选中一个函数,这样用:

「请帮我添加错误处理和类型定义」

AI直接在原代码基础上帮我改好了,我只需要确认一下就行。

比让它生成一段新代码然后我再替换,效率高很多。

再举几个我常用的场景:

  • 选中一段面条式代码:「请帮我重构这段代码」
  • 选中一个API接口:「请帮我添加参数校验」
  • 选中一个组件:「请把这个组件改成响应式」

5. 打开对话窗口做复杂任务

有时候你想让AI帮你做比较复杂的任务,比如生成一个完整的组件。

选中代码后打开对话窗口,可以详细描述你的需求。

我经常这样用:

  1. 选中一段代码
  2. 打开对话窗口
  3. 详细描述我要做什么
  4. AI生成代码,我可以逐行确认

这个功能特别适合:

  • 生成一个新组件
  • 实现一个复杂功能
  • 写测试用例

6. @符号引用文件

@符号引用特定内容。

  • @File :引用当前打开的文件
  • @components/UserCard.tsx :直接引用某个文件
  • @Folder :引用整个文件夹
  • @Docs :引用官方文档
  • @Search :搜索项目内的代码

最常用的场景:

@components/UserCard.tsx 请帮我在这个组件里添加一个编辑用户信息的功能

AI直接读取文件内容,在正确的位置帮我添加代码。

@Docs 请帮我查一下Next.js的metadata怎么用来做SEO

AI直接读官方文档,给我准确的答案。

7. 设置好项目规范

我在每个项目都会设置Rules。这是Cursor的一个特色功能,其他工具类似功能还在发展中。

在项目根目录创建.cursor/rules/目录,放.mdc文件:

---
name: 项目规范
description: Next.js 15 App Router 项目规范
---

# 技术栈
- 框架:Next.js 15 App Router
- 语言:TypeScript strict
- 样式:Tailwind CSS
- 数据库:PostgreSQL + Prisma

# 目录结构
- app/:页面
- components/:组件
- lib/:工具函数
- prisma/:数据库schema

# 代码规范
1. 默认使用 Server Components
2. 客户端用 'use client' 标记
3. API错误格式:{ success: boolean, error?: string }

设置好之后,Cursor每次生成代码都会自动遵循这些规范。

举个例子:我不用每次都说「API错误要返回success和error字段」,Cursor自己就知道。

而且Rules是可以复用的。我做了几个模板:

  • Next.js项目规范
  • NestJS项目规范
  • React组件规范

每次建新项目,直接复制过来改一下就行。

8. 用好Skills,让AI更专业

如果说Rules是「项目规范」,那Skills就是「专业能力」。

你可以在.cursor/skills/目录放一些专业技能文件:

# .cursor/skills/database.md

你是一个数据库专家,精通 PostgreSQL 和 Prisma。

在回答数据库相关问题时:
1. 优先考虑查询性能,避免 N+1 问题
2. 合理使用索引,解释为什么加这个索引
3. 更新用 update,删除用 delete,别用 updateMany

回答时先解释原理,再给代码示例。

用的时候告诉AI:「请用数据库专家的角度,帮我审查以下Prisma查询...」

它回答的专业度明显比普通模式高。

我目前积累了几个Skills:

  • database.md:数据库专家
  • security.md:安全专家
  • performance.md:性能优化专家
  • typescript.md:TypeScript专家

9. MCP让AI更强大

前面提到了MCP,这是2026年特别值得关注的特性。

简单说,MCP 让 AI 能从"只懂训练数据"变成"能操作真实世界":

  • MCP + 文件系统:AI 可以直接读取、修改你本地项目的代码
  • MCP + 数据库:AI 可以直接查询你的数据库
  • MCP + 浏览器:AI 可以控制浏览器,帮你填表单、截图
  • MCP + 搜索:AI 可以帮你搜Google、搜文档

Cursor、Trae 等新一代工具已经开始支持 MCP,装好对应的插件就能用。

装好 MCP 插件后,我可以直接问AI:「帮我查询数据库里最近注册的10个用户」

AI真的会去查数据库,然后给我结果。

这个功能还在快速进化中,未来能做的事情会越来越多。

10. 节省Token是有技巧的

前面提到了 Token 的概念。Token 是 AI 处理文字的计量单位,AI API 是按 Token 收费的。

这是我总结的节省 Token 经验:

  1. 别一上来就贴全栈代码:只贴和问题相关的代码片段,AI不需要看你的整个项目才能回答问题。

  2. 问完一个问题可以开新会话:如果新问题和上一个问题不相关,别在同一个会话里继续聊。AI需要记住之前对话的内容,这些也会算Token。

  3. 让AI一次性完成:比如你要写一个组件,别分开问「先帮我写HTML」「再帮我写样式」「再加个交互」。直接说「帮我写一个登录组件,包含表单验证、错误提示、暗色模式支持」,一次搞定。

  4. 精简你的Prompt:Prompt不是越长越好,是越精确越好。把无关的废话去掉,AI能更专注,Token也花得值。

  5. 用@引用代替复制粘贴:用@File引用文件,AI会自己去读,比你复制粘贴一长串代码省Token。


这些场景我天天用AI

1. 读报错信息

以前遇到报错,我要把错误信息复制到Google搜半天。

现在直接问Cursor:「这个报错是什么意思?TypeError: Cannot read properties of undefined (reading 'map')」

它会告诉我:错误原因是什么、最可能出在哪个地方、怎么修复。

80%的情况下,它能帮我省掉搜索的时间。

有时候我甚至直接截图给它看,它也能分析个大概。

2. 代码Review

以前代码Review都是同事做。现在我先让AI Review一遍,发现低级问题,再交给同事。

效率高很多,而且有些话AI说得,我作为开发者反而不好开口。

「请审查以下代码,指出:1. 潜在安全问题 2. 性能问题 3. 代码规范问题」

它会从安全性、性能、代码规范等角度帮我分析一遍。

3. 重构代码

觉得某段代码写得烂,但不知道怎么改?

问AI:「请帮我重构以下代码,要求:1. 使用TypeScript类型 2. 提取可复用逻辑 3. 增加错误处理」

AI会给一个全新的版本,我可以参考它的思路自己改,也能学到东西。

有时候我还会让它用不同的方式重构,让我对比学习。

4. 帮我想名字

我经常让AI帮我给变量、函数起名字。

「我有一个函数,接受用户ID,返回用户名、邮箱、头像、最后登录时间。请帮我想一个合适的函数名」

AI会给三四个建议:

  • getUserById
  • fetchUserDetails
  • getUserProfile

我会选一个最合适的。

比自己想半天强多了。而且AI起的名字通常都比较规范,符合命名习惯。

5. 写测试

写测试很枯燥,但很重要。

我会让AI帮我:

「请为以下函数编写单元测试,覆盖:正常情况、空输入、错误输入」

AI生成测试代码,我再根据需要调整。能省不少时间。

有时候我还会让它帮我补充边界情况的测试。

6. 查文档

以前遇到问题,我先去 Google 搜,然后看 Stack Overflow,最后实在不行才去翻文档。

现在我直接问 Cursor:

`@Docs 请帮我查一下Next.js 15怎么做密码重置」

或者

`@Docs Vercel AI SDK怎么实现流式响应」

AI直接从文档里给我准确的答案,比我自己搜快多了。

7. 帮我写SQL

有时候我需要写一些复杂的SQL查询,直接问AI:

「帮我写一个SQL,查询过去7天每天的新增用户数,按日期排序」

AI会给我SQL,我稍微改改就能用。

8. 帮我理解别人的代码

接手别人的项目,看不懂代码怎么办?

问AI:

`@components/OldCode.tsx 请帮我解释这个组件做了什么」

AI会把代码逻辑梳理一遍,比我自己看快多了。


积累自己的Prompt模板库

这是我想聊的最后一个话题。

有些 Prompt 我会反复使用,慢慢就积累了一套模板:

// 解释代码
请用三句话解释以下代码做了什么

// 解释报错
这个报错是什么意思?{报错信息}

// 生成类型
请为以下接口生成 TypeScript 类型定义

// 代码审查
请审查以下代码,指出:1. 潜在问题 2. 性能优化点 3. 代码规范问题

// 重构
请重构以下代码,要求:{你的要求}

// 写测试
请为以下函数编写测试用例,覆盖:{场景1}、{场景2}、{场景3}

// 查文档
@Docs {你的问题}

我保存在一个markdown文件里,用的时候直接复制粘贴,稍微改改就能用。

我的经验总结

用多了,你会发现有些规律:

  • 1. 模板要简单通用

    我的模板都很简单,就是一个开头。比如「请用三句话解释」,这个模板可以用在任何代码上。

    不要把模板写得太具体,比如「帮我写一个登录表单要包含用户名、密码、验证码」。这样反而不好复用。

  • 2. 遇到好的Prompt就保存下来

    有时候你会发现,同样的问题,不同的问法,AI回答的质量差很多。

    遇到好的Prompt,就把它保存下来。下次遇到类似的问题,直接用或者改改再用。

  • 3. Rules模板可以复用

    Rules模板也是一样的道理。

    我做了几个模板:

    • Next.js项目规范
    • NestJS项目规范
    • React组件规范

    每次建新项目,直接复制过来改一下就能用。做到第三个项目,你会发现很多规范是可以复用的。

  • 4. 定期整理和迭代

    我的模板库每个月会整理一次。把不用的删掉,好的留下来。

    有时候会发现之前写的模板不够好,就改改。

    这是一个持续迭代的过程,不用急。


写在最后

回到开头的问题:会问问题,为什么比会写代码更重要?

因为 AI 时代,写代码的门槛会越来越低。但提问的能力——把模糊的需求翻译成精确的描述——这个能力反而越来越值钱。

你能不能清楚地描述你想要什么?能不能给 AI 足够的上下文?能不能判断 AI 给出的答案对不对?

这些才是 AI 时代真正的核心竞争力。

AI 辅助开发工具会越来越好用,Cursor、Trae、Copilot、OpenCode……不管你用哪个,核心技巧都是互通的。用好工具的人,永远是那些懂得思考的人。

下一篇文章,我们会开始真正的技术内容:《全栈开发环境搭建:Git + monorepo + 开发工具链》。

感兴趣的话,下一篇见。

为什么2026年还要学全栈?

作者 牛奶
2026年2月26日 21:17

为什么2026年还要学全栈?

系列开篇,写给想要真正做事的人。


原文地址

墨渊书肆/为什么2026年还要学全栈


你有没有过这样的经历?

做了一套很酷的前端界面,发到群里求赞。朋友问:「能线上访问吗?」你愣了一下:「还在本地跑着呢。」

搭建了一个API接口,测试数据跑得好好的。放到线上就开始报错,你对着日志看了半天,不知道是数据库连接问题还是CORS没配好。

买了个云服务器,SSH连上后对着黑屏发呆——接下来该干什么?域名怎么绑定?HTTPS怎么配置?

如果你有过类似的经历,说明你和我一样,曾经被困在某个技术边界里。

前端会一点,后端也懂一点,但真的要把一个想法变成线上能用的东西,总是差了那么一口气。

我想聊聊这件事。


全栈这件事,被误解了很多年

一提到「全栈工程师」,很多人脑海里浮现的是这样一个形象:什么框架都会,什么语言都能写,数据库也能碰,服务器也能捣鼓。

换句话说,「什么都会一点」。

这种理解,在五年前或许还能成立。那时候做Web开发,确实需要前后端都懂一点才能混得下去。

但2026年了,这种理解该过时了。

真正的全栈,不是「什么都会一点」,而是「能独立交付一个完整的、可运行的互联网产品」。

这两个定义有本质区别。

「什么都会一点」说的是技术广度,你掌握了ABCDE各种技术。 「能交付完整产品」说的是能力深度,你能够从0到1,把想法变成现实。

前者是堆砌,后者是整合。

这十年,全栈经历了什么

让我简单回顾一下这段历史,你可能会更有感触。

  • 2010-2015年:全栈的黄金时代

那时候,一个创业者想要做个网站,真的需要一个人搞定所有事情。PHP就是最典型的全栈语言——一个文件,从数据库到HTML全写了。

没有选择,只能全栈。

  • 2015-2020年:前后端分离,全栈「衰落」

前端技术越来越复杂,React、Vue、Angular各自一套生态。后端技术也在深化,微服务、容器化、云原生,一个领域比一个领域深。

很多人开始专注于一个方向。全栈这个词渐渐变成了「什么都会一点,什么都不精」的代名词。

我见过很多前端工程师,后端代码一行都不敢改。也见过很多后端工程师,写个表单样式就头皮发麻。

技术栈在变宽,人在变窄。

  • 2020年至今:AI时代,全栈复兴

转折来自两个力量:

一是Serverless和全栈框架的成熟。Next.jsSupabase让一个人能覆盖的场景越来越广。

二是AI的爆发。代码可以自动生成了,一个人能做的事情边界再次扩大。

但这次不一样。

这次的全栈,不是回到过去那种「什么都会一点」的状态,而是有了AI的辅助,你可以更专注于「整合」而非「实现」

你不需要记住每个API的用法,AI可以帮你查。但你需要知道一个系统需要哪些模块、它们怎么配合。

这才是2026年「全栈」的真正含义。


我见过太多「会技术」但「做不出东西」的人

我自己也是这么走过来的。

刚学编程的那几年,我痴迷于学新东西。React出来了,学React。Vue火了,学Vue。Node.js流行,学Node。Docker热门,学Docker。

感觉自己越来越厉害,简历上技术栈越来越长。

但有一次,我做一个个人博客系统,前前后后做了俩个月。

不是技术难,而是我在每个环节都卡住:

  • 前端写到一半,发现后端API设计不合理,推倒重来
  • 数据库表结构改了三版,每次都要改前端对应的字段
  • 好不容易做完了,部署上线又折腾了一周
  • 刚上线就被别人注册了一堆垃圾数据,才发现自己没做接口限流

一个看似简单的博客系统,真正从零做到上线,才发现之前学的那些技术都是散的,根本连不起来。

后来我反思:不是我技术不够,而是我从来没有站在「完整产品」的角度去规划一个系统。

这就是问题所在。

但现在,在春节前,我使用 AI 辅助开发和腾讯云的轻量服务器,3天就成功上线了我的个人博客站。

————墨渊书肆

后面,也会根据这个博客站,和我在开发的另一个出海产品,分享我的实战经验。


全栈到底学什么?

说了这么多,你可能想问:所以全栈到底要学什么?

我的回答是:不是学更多技术,而是理解技术之间的关系。

举两个例子。

第一个例子。

你想实现「用户登录后可以评论」这个功能。你需要懂:

  • 前端表单验证
  • 后端接口设计
  • 数据库表结构
  • 密码怎么加密存储
  • Token怎么验证
  • HTTPS怎么配
  • Rate Limiting怎么加

每一项单拎出来都不难。但如果你不懂它们之间的关系,就会出现:前端验证了后端没验证、密码存明文了、Token没过期时间、接口被人刷爆等各种问题。

第二个例子。

你做一个博客系统。要发文章、要看文章、要评论、要搜索、要做SEO、要做推荐。

每个功能你都能找到对应的技术方案。但关键问题是:

  • 先做哪个后做哪个?
  • 数据库表之间怎么关联?
  • 哪些数据要缓存哪些不用?
  • 搜索要做全文检索还是简单like查询?

这些问题没有标准答案,需要你根据实际需求去权衡去决策。

全栈的核心能力,就是理解这些技术怎么配合,然后做出合理的决策。


2026年的全栈技术图谱

既然说到全栈,我把一个现代 Web 应用涉及到的技术领域整理一下。不用全部记住,但需要知道大概有哪些东西,以及每个部分是干嘛的。


前端部分 —— 用户能看到的一切

前端就是用户打开浏览器能看到的所有东西。按钮能不能点、页面好不好看、表单能不能提交,这些都归前端管。

框架:用来构建用户界面。React是现在最主流的选择,Vue在国内用得也比较多,Next.js比较特殊,它既是前端框架,又自带后端能力,属于「全栈框架」。

样式:让界面好看。Tailwind CSS是现在的主流,因为它不用写单独的CSS文件,直接在HTML里写样式,很方便。

状态管理:管理页面数据。比如用户登录了,他的信息存在哪里?购物车有几件商品?这些数据的变化需要统一管理,Zustandmobx是轻量级的选择,Redux功能更全但也 更重。

UI组件:现成的界面零件。shadcn/ui现在特别火,它不是传统意义上的组件库,而是提供代码让你自己修改,这样你可以完全控制样式。


后端部分 —— 用户看不到但每天在用的

后端是服务器上运行的代码,你看不见它,但它在默默处理各种请求。用户登录、提交订单、查询数据——这些都需要后端来处理。

运行时:JS 可以在服务器上运行了,这就是Node.js,目前最成熟。Bun更快,Deno更现代(Node.js的原作者重新写的)。

框架:写后端代码的工具。Next.js API Routes是前后端一起写的方式,适合小项目。Hono非常轻量,而且天然支持 Edge 部署(边缘计算,后面会讲)。NestJS是企业级的,结构更严谨,适合大项目。

数据库:存数据的地方。PostgreSQL是目前最强悍的关系型数据库,MySQL是老牌稳定选手。简单理解:重要数据放数据库。

ORM:数据库和代码之间的翻译官。Prisma用起来很舒服,Drizzle更快且更轻, typeORM 功能更全。它们让你用 JS 的语法去操作数据库,不用写原始SQL。


基础设施 —— 让你的应用能跑起来

这部分是很多前端开发者最头疼的——代码写完了,怎么让它能被所有人访问?这就是基础设施要解决的问题。

服务器:一台24小时开机的电脑。国内的阿里云腾讯云,国外的VercelNetlify,都是提供服务器的服务商。

容器:把应用和它依赖的所有东西打包,这样在任何环境下都能跑。Docker是标配,Docker Compose用来在本机编排多个服务。

CDN:让用户访问更快。CDN就是一堆分布在世界各地的服务器,用户访问时从最近的服务器拿资源,速度会快很多。国际首选Cloudflare,国内用阿里云CDN

域名和SSL:域名是网站的地址,SSL是让访问变成https://的那个加密协议。Let's Encrypt提供免费SSL,Cloudflare可以自动帮你处理HTTPS。


运维监控 —— 保障服务稳定运行

应用上线了,怎么知道用户访问快不快?出错了怎么知道?这些就是运维监控要做的。

日志:记录系统发生了什么。ELK(Elasticsearch + Logstash + Kibana)是经典方案,Loki更轻量。现在很多云服务也自带日志功能。

监控:看系统健康不健康。Sentry专门追踪错误,谁的代码出错了第一时间知道。Prometheus + Grafana是看指标的,比如服务器CPU用了多少、数据库响应多快。

CI/CD:自动化部署。代码提交后自动测试、自动部署到服务器。GitHub Actions最常用,国内有阿里云效腾讯云CODING


安全 —— 保护你的应用

不做安全防护的应用,就像没装门的房子,谁都能进来。

前端安全:XSS是别人在你的页面里注入恶意脚本,CSRF是别人伪造你的身份发请求,CSP是限制页面能加载哪些资源。

后端安全:SQL注入是通过输入框往数据库里塞恶意代码,参数校验是确保用户传的数据是你期望的,Rate Limiting是限制一个人1分钟内只能发10次请求,防止被刷。

数据安全:HTTPS加密传输是最基本的,敏感数据(比如密码)要加密存储,密钥不要写在代码里。


AI能力 —— 新时代的必备技能

2026年了,如果你说自己是全栈但不懂 AI 用法,就像做前端不会用Git一样说不过去。

集成框架Vercel AI SDK是最流行的AI功能集成框架,支持流式响应(就是 ChatGPT 那种一个字一个字蹦出来的效果),对接各种模型很方便。

模型提供商:国外用OpenAI(GPT)、Anthropic(Claude),国内用硅基流动DeepSeek。国内外使用体验和成本差异很大,后面实战会分别讲。

向量数据库:AI场景专用。传统数据库存文字,向量数据库存「意思」。比如你搜「苹果」,它不仅能匹配到「苹果」,还能匹配到「iPhone」、「水果」,因为它理解「苹果」的含义。PineconeMilvus是代表。


这就是现代全栈的完整图谱。你不需要每样都精通,但需要知道它们各自负责什么,以及什么时候该用什么。


AI时代,全栈反而更重要了

我知道你可能会有疑问:现在AI这么强,Cursor敲几下代码就出来了,我还需要学全栈吗?

我的答案是:恰恰相反。

AI可以帮你写一个登录API,但它不知道:

  • 你的产品需不需要短信验证码登录
  • 你的用户数据存储在哪里
  • 你要不要支持微信登录
  • 登录失败几次要锁号
  • Token过期时间设多长

AI可以帮你写一个数据库查询,但它不知道:

  • 你的数据量级需要什么索引
  • 哪些查询需要加缓存
  • 读写分离怎么做

AI可以帮你部署上线,但它不知道:

  • 选择Vercel还是阿里云
  • 国内用户访问慢怎么办
  • 怎么做成本优化

AI擅长的是「点」,你需要的是「面」。

你告诉AI「帮我写个用户登录」,它会给你写一个标准答案。但具体怎么设计,这是你需要决策的事情。

而且,只有当你真正理解一个系统是怎么运转的,你才能:

  • 准确描述你想要什么(而不是永远在改需求)
  • 发现AI写的代码哪里有问题(而不是全盘接受)
  • 把不同模块组合在一起(而不是拼都拼不起来)

这才是整合能力的价值。

AI不是取代你,而是放大你。你原本只能做前端,AI帮你写了后端,你就能做全栈。但前提是,你本来就具备全栈思维,知道一个完整的产品需要什么。


怎么学?T型发展

说了这么多,到底怎么学?我的建议是「T型发展」:

先广度,后深度。

首先,对全栈技术有个整体认知。前端、后端、数据库、运维、安全……每个领域都了解一下,知道它们各自负责什么、解决什么问题。

这个阶段不需要深入,掌握概念就够了。

然后,选择一个方向深挖。

如果你对前端感兴趣,就深入React/Next.js。如果你对后端感兴趣,就深入Node.js/PostgreSQL。深入到能独立完成一个完整项目的程度。

最后,按需补充。

在实际项目中遇到什么问题,就去学什么。需要做支付,就去学Stripe。需要做搜索,就去学Elasticsearch。需要做 AI 功能,就去学Vercel AI SDK

这种「实战驱动」的学习方式,效率最高。


这个系列想带你做什么

市面上不缺技术教程。React入门、Node.js实战、Docker部署——这种内容一搜一大把。

但我发现很多人学完这些教程,还是做不出东西。

因为技术是散的,需要一条线把它们串起来。

这个系列我想带你做的事情很简单:从零开始,构建一个真正能上线的产品。

不是demo,不是练习,而是真实的、可访问的、能在生产环境跑的系统。

我会分成这几个阶段:

  • 第一阶段:认知重建

先理解全栈到底要学什么,怎么学(就是这篇)。

  • 第二阶段:基础设施

服务器、域名、CDN、Docker、日志、监控——那些「不太技术」但非常重要的东西。

  • 第三阶段:前端开发

React、Next.js、TypeScript、UI体系。

  • 第四阶段:后端开发

API设计、数据库、认证、缓存。

  • 第五阶段:AI集成

Vercel AI SDK、流式响应。

  • 第六阶段:部署上线

国内(阿里云)和国外(Vercel)两套方案。

  • 第七阶段:安全与性能

生产环境必须注意的那些事。

  • 第八阶段:实战

两个完整项目,从0到上线的全过程。

在这个过程中,你会看到我踩过的坑、做过的错误决策、总结出的经验。我不是为了告诉你「这个技术怎么用」,而是告诉你「这个系统该怎么搭」。


写在最后

回到开头的问题。

你是不是经常感觉学了很多技术,但真正要用的时候还是不知道从哪里开始?

这很正常。

技术本身不是目的,产品才是。

2026年了,AI 可以帮你写代码,但不能帮你交付产品。能做到这一点的人,永远有市场。

而这,就是我们这个系列要一起做的事情。

下一篇文章,我会讲讲AI辅助开发这件事——怎么用好CursorTraeOpenCode,以及一个更重要的道理:会问问题比会写代码更重要。

感兴趣的话,下一篇见。

昨天以前首页

AI 写代码总是半途而废?试试这个免费的工作流工具

作者 仿生狮子
2026年2月26日 17:04

作为一个用过多种 IDE 的开发者,我想分享一个让我效率 up up 的小工具。

你有没有遇到过这种情况?

  • 跟 AI 聊了半天需求,代码写了一半,上下文满了,AI "失忆"了
  • 项目做到一半搁置,一周后回来完全忘了做到哪了
  • 想加一个功能,结果 AI 把之前的代码改坏了

这些问题都有一个共同原因:上下文衰减(Context Rot)

简单来说,AI 的"记忆"是有限的。当对话太长时,它会慢慢忘掉之前说过的话,导致代码质量下降。

GSD 是什么?

GSD = Get Shit Done(把事做完)

它是一个开源的 AI 编程工作流框架,核心思路很简单:

把项目信息存到文件里,而不是全部塞给 AI。

就像你写代码会用 Git 做版本控制一样,GSD 帮你做"AI 对话的版本控制"。

GSD for Trae

原版 GSD 是为 Claude Code 设计的。因为我日常用 Trae,所以做了这个适配版本。

安装只需一行命令:

npx gsd-trae

或者:

bash <(curl -s https://raw.githubusercontent.com/Lionad-Morotar/get-shit-done-trae/main/install.sh)

它能帮你做什么?

1. 新项目规划

输入 /gsd:new-project,它会:

  • 问你一系列问题,搞清楚你要做什么
  • 自动研究技术方案(可选)
  • 生成项目路线图

2. 阶段式开发

大项目拆成小阶段:

  • /gsd:plan-phase 1 - 规划第一阶段
  • /gsd:execute-phase 1 - 执行第一阶段
  • /gsd:verify-work - 验证做得对不对

每完成一个阶段,进度都会被记录,随时可以接着做。

3. “断点续传”

关掉电脑、明天再来,输入 /gsd:progress,AI 马上知道:

  • 项目做到哪了
  • 接下来该做什么
  • 之前的决策是什么

实际使用感受

我用了一个月,相比 Trae 的 Plan Build 模式最明显的变化:

以前:一个功能聊到一半,AI 开始"胡言乱语",只能新开对话重来

现在:每个阶段都有清晰的目标和验收标准,AI 一直保持在正确的方向上

以前:同时开多个功能,代码互相冲突

现在:按阶段来,做完一个再做下一个,井井有条(进阶用户也可以选择 Worktree 模式)

以前:Plan 文档随意仍在 .trae 的文档目录,没有管理,很难查找

现在:结构化的目录,GSD 和开发者都能轻松阅读

适合谁用?

  • 用 Trae/Gemini/Claude 写代码的开发者
  • 做独立项目、 side project 的人
  • 觉得 AI 编程"聊不动"的新手

相比其他工具的优势

市面上有不少 AI 编程工作流工具,比如 GitHub 的 Spec Kit、OpenSpec、BMAD 等。GSD 的定位不太一样:

工具 特点 GSD 的区别
Spec Kit 企业级、严格阶段门控、30分钟启动 GSD 更轻量,5分钟上手,没有繁琐的仪式
OpenSpec 灵活快速、Node.js 运行 GSD 额外解决了 Context Rot 问题,支持断点续传
BMAD 21个 AI Agent、完整敏捷流程 GSD 不模拟团队,而是聚焦"让开发者高效完成项目"

简单说:如果你期待快速而结构化的流程,又不想被复杂的企业开发规范束缚的同时,确保 AI 编程能稳定输出,GSD 可能是目前最合适的选择。

它是免费的

开源项目,GitHub 地址: github.com/Lionad-Moro…

MIT 协议,可以随便用、随便改。

最后说一句

AI 编程工具越来越强大,但工具只是工具。

好的工作流能让你事半功倍,而 GSD 就是这样一套经过验证的工作流。

不需要改变你现有的开发习惯,安装后输入 /gsd:new-project 试试看。


如果你试过觉得好用,欢迎点个 Star ⭐

如果发现问题,也欢迎提 Issue

微信小程序自动化的 AI 新时代:wechat-devtools-mcp 智能方案

作者 FliPPeDround
2026年2月26日 15:43

FliPPeDround

前端工程师 · 开源爱好者 · 正在找工作

对我的项目感兴趣?查看我的简历 · resume

如果你曾尝试配合 AI 代理(如 Claude、Cursor 编写微信小程序,你大概率会遇到这样的困境:测试工具与 AI 代理集成困难、缺乏统一的自动化框架支持、无法充分利用 AI 的智能分析能力。更糟糕的是,当你想要使用 Claude、Cursor 等 AI 辅助工具来提升测试效率时,却发现没有合适的微信小程序自动化集成方案。

为了解决这些痛点,wechat-devtools-mcp 应运而生。作为一款基于 MCP(Model Context Protocol)协议的微信开发者工具自动化服务,它让小程序的自动化测试与 AI 智能分析变得前所未有的简单和高效。

📖 介绍

📚 官方文档:更多详细的安装和配置说明,请参考 GitHub 仓库

wechat-devtools-mcp 是一个专门为微信小程序设计的 MCP 服务,通过 MCP 协议与 AI 代理(如 Claude、Cursor)深度集成。它基于微信官方的 miniprogram-automator 库,提供了完整的小程序自动化能力,让你能够在 AI 的辅助下,高效地完成小程序的自动化测试和调试。

这个工具的出现,填补了小程序自动化测试与 AI 智能分析之间的空白。它不仅保持了与微信开发者工具的完全兼容性,还充分发挥了 MCP 协议的标准化优势,为开发者提供了一个更智能、更高效的自动化测试解决方案。

🚀 核心功能与技术优势

1. 无缝集成 MCP 协议生态

wechat-devtools-mcp 完全兼容 MCP 协议,可以轻松集成到支持 MCP 的 AI 代理中:

{
  "mcpServers": {
    "wechat-devtools": {
      "command": "npx",
      "args": [
        "-y",
        "wechat-devtools-mcp",
        "--projectPath=/path/to/your/miniprogram"
      ]
    }
  }
}

2. 全面的页面导航支持

工具提供了丰富的 API 来操作小程序页面导航:

  • 保留当前页面跳转:通过 navigateTo 跳转到非 tabBar 页面
  • 关闭当前页面跳转:通过 redirectTo 关闭当前页面并跳转
  • 返回上一页:通过 navigateBack 返回上一页面或多级页面
  • 重新加载页面:通过 reLaunch 关闭所有页面并重新打开
  • 切换 TabBar:通过 switchTab 跳转到 tabBar 页面
  • 获取页面栈:通过 pageStack 获取当前页面栈列表

3. 强大的元素操作能力

工具提供了完整的元素操作 API,支持各种用户交互模拟:

  • 元素获取:通过 getElementgetElements 获取页面元素
  • 用户交互:支持点击、长按、触摸、输入等操作
  • 元素信息:获取元素尺寸、位置、文本、属性、样式等信息
  • 组件方法:调用自定义组件的方法和数据操作

4. 智能日志和异常监听

工具内部实现了智能的日志和异常监听机制:

  • 自动监听控制台日志(console.log、console.info、console.warn、console.error)
  • 自动捕获运行时异常,包括错误名称、堆栈跟踪和发生时间
  • 支持日志和异常的查询和过滤
  • 内置日志数量限制,避免内存溢出

5. 灵活的配置选项

支持丰富的配置选项,满足不同测试场景的需求:

  • 自定义小程序项目路径
  • 微信开发者工具 CLI 路径配置
  • 连接超时时间设置
  • WebSocket 端口配置
  • 用户账号和登录票据支持
  • 项目配置文件覆盖

6. 微信 API 模拟与调用

工具提供了强大的微信 API 操作能力:

  • 调用微信 API:通过 callWxMethod 调用 wx 对象上的任意方法
  • Mock 微信 API:通过 mockWxMethod 模拟 API 返回值,便于测试
  • 恢复微信 API:通过 restoreWxMethod 恢复被 Mock 的方法

🧪 为什么 E2E 测试如此重要

在软件开发中,单元测试固然重要,但 E2E(End-to-End)测试在构建高质量代码过程中扮演着不可替代的角色。

提升代码可靠性

E2E 测试模拟真实用户的使用场景,从用户界面到后端服务的完整流程进行验证。与单元测试不同,E2E 测试能够发现:

  • 页面间的导航和状态传递问题
  • 用户交互与业务逻辑的集成异常
  • 微信 API 调用的错误处理
  • 不同设备和系统版本的兼容性问题

对于微信小程序这种运行在特殊环境中的应用,E2E 测试尤为重要。它能够确保你的小程序在不同设备、不同微信版本、不同网络环境下都能正常运行,避免出现"在开发环境正常,上线后出问题"的尴尬情况。

降低维护成本

虽然编写 E2E 测试 需要投入一定的时间成本,但从长远来看,它能显著降低维护成本:

  • 减少回归测试时间:自动化测试可以在几分钟内完成原本需要数小时的手动测试
  • 快速定位问题:当出现 bug 时,E2E 测试能够快速定位问题所在
  • 增强重构信心:有了完善的测试覆盖,你可以放心地进行代码重构,而不必担心破坏现有功能
  • 文档化业务逻辑:测试代码本身就是最好的业务逻辑文档

提升团队协作效率

E2E 测试作为项目质量的"守门员",能够:

  • 统一团队对功能实现的理解
  • 减少 code review 时的争议
  • 让新成员快速理解项目功能
  • 建立持续集成的质量保障体系

📦 快速上手

安装依赖

wechat-devtools-mcp 是一个 npm 包,可以直接通过 npx 运行,无需额外安装:

npx -y wechat-devtools-mcp --projectPath=/path/to/your/miniprogram

配置 MCP 服务器

在你的 AI 代理(如 Claude、Cursor)的配置文件中添加 MCP 服务器配置:

{
  "mcpServers": {
    "wechat-devtools": {
      "command": "npx",
      "args": [
        "-y",
        "wechat-devtools-mcp",
        "--projectPath=/path/to/your/miniprogram"
      ]
    }
  }
}

命令行参数说明

参数 类型 说明
--projectPath string 小程序项目路径(必填)
--cliPath string 微信开发者工具 CLI 路径
--timeout number 连接超时时间(毫秒),默认 30000
--port number WebSocket 端口号,默认 9420
--account string 用户 openid
--ticket string 开发者工具登录票据
--projectConfig string 覆盖 project.config.json 中的配置

使用示例

配置完成后,你就可以在 AI 代理中使用 wechat-devtools-mcp 提供的工具了。以下是一些典型的使用场景:

1. 启动小程序

// 使用 launch 工具启动微信开发者工具并连接小程序
await launch()

2. 页面导航测试

// 跳转到指定页面
await navigateTo({ url: '/pages/detail/detail?id=123' })

// 获取当前页面信息
const currentPage = await currentPage()

// 返回上一页
await navigateBack({ delta: 1 })

3. 元素操作测试

// 获取页面元素
const element = await getElement({ selector: '.submit-button' })

// 点击元素
await tapElement({ selector: '.submit-button' })

// 输入文本
await inputElement({ selector: '#username', value: 'testuser' })

// 获取元素文本
const text = await getElementText({ selector: '.welcome-text' })

4. 页面数据操作

// 获取页面数据
const pageData = await getPageData({ path: 'userInfo.name' })

// 设置页面数据
await setPageData({ data: { count: 10, status: 'active' } })

5. 日志和异常监听

// 获取控制台日志
const logs = await getlogs({ type: 'error', limit: 10 })

// 获取异常信息
const exceptions = await getexceptions({ limit: 5 })

6. 微信 API 调用和 Mock

// 调用微信登录 API
const loginResult = await callWxMethod({ method: 'login', args: [] })

// Mock 用户信息 API
await mockWxMethod({ method: 'getUserInfo', result: { nickName: '测试用户' } })

// 恢复 API
await restoreWxMethod({ method: 'getUserInfo' })

🎯 高级功能详解

截图功能

工具支持对小程序当前页面进行截图,返回 Base64 编码的图片数据:

const screenshot = await screenshot()

系统信息获取

获取小程序运行所在的系统信息,包括手机品牌、型号、屏幕尺寸、操作系统版本、微信版本等:

const systemInfo = await systemInfo()

体验评分

支持微信小程序体验评分(Audits)功能,可以获取性能最佳实践、Accessibility 可访问性等方面的评分和建议:

// 停止体验评分并获取报告
const auditResult = await stopAudits({ path: './audits.json' })

测试账号管理

支持获取微信开发者工具多账号调试中添加的测试用户列表,可用于模拟不同用户登录场景的测试:

const accounts = await testAccounts()

🔧 技术实现细节

wechat-devtools-mcp 的实现基于 MCP 协议和微信官方的 miniprogram-automator 库,核心架构包括以下几个部分:

  1. Automator 类:负责微信开发者工具的启动、连接和生命周期管理
  2. MiniProgram 工具类:提供小程序级别的操作,如页面导航、API 调用、系统信息获取等
  3. Page 工具类:提供页面级别的操作,如元素获取、数据操作、方法调用等
  4. Element 工具类:提供元素级别的操作,如点击、输入、属性获取等

工具内部还实现了智能的状态管理和错误处理机制,确保自动化测试的稳定性和可靠性。

🌟 总结

wechat-devtools-mcp 为微信小程序开发者提供了一个现代化、智能化的自动化测试解决方案。它不仅解决了传统测试工具与 AI 代理集成困难的问题,还充分发挥了 MCP 协议和 miniprogram-automator 的技术优势。

通过完善的 E2E 测试和 AI 智能分析,你可以:

  • 提升代码质量和可靠性
  • 降低长期维护成本
  • 增强团队协作效率
  • 建立持续集成的质量保障体系
  • 充分利用 AI 的智能分析能力

如果你正在开发微信小程序项目,并且想要建立完善的自动化测试体系,wechat-devtools-mcp 绝对值得一试。它会让你的测试工作变得前所未有的简单和高效。

📚 相关资源


最后

wechat-devtools-mcp 是一个免费的开源软件,遵循MIT协议,社区的赞助使其能够有更好的发展。

你的赞助会帮助我更好的维护项目,如果对你有帮助,请考虑赞助一下😊

你的star🌟也是对我的很大鼓励,Github

欢迎反馈问题和提pr共建

2025前端技术趋势:从智能到沉浸的新时代

2026年2月25日 10:27

引言

前端技术正处于一个前所未有的快速发展阶段。从早期的静态网页到如今的复杂单页应用,前端开发已经经历了多次重大变革。随着WebAssembly、人工智能、虚拟现实等技术的不断成熟,2025年的前端技术将进入一个全新的时代。在这个新时代,前端开发者需要掌握的技能不再局限于HTML、CSS和JavaScript。他们需要了解WebAssembly、机器学习、3D渲染等跨领域技术,并能够将这些技术有机地结合,创造出前所未有的用户体验。

1. WebAssembly 3.0:突破Web性能极限

WebAssembly(Wasm)自2017年推出以来,已经成为前端性能优化的重要工具。2025年,WebAssembly 3.0将带来一系列重大改进,进一步突破Web应用的性能极限。

1.1 架构演进

image.png

1.2 WebAssembly 3.0 核心特性

WebAssembly 3.0将引入以下核心特性:

  • 增强的垃圾回收支持:更高效的内存管理,减少内存泄漏和性能问题

  • 原生DOM访问:直接操作DOM,消除JavaScript桥接开销

  • 多线程与并行计算:更强大的并行处理能力,支持复杂计算任务

  • WASI 2.0:更完善的WebAssembly系统接口,支持更多系统级功能

1.3 性能对比

WebAssembly 3.0在性能方面将有显著提升:

image.png

1.4 应用场景

WebAssembly 3.0将在以下场景中发挥重要作用:

  1. 复杂数据可视化:处理大规模数据并实时渲染

  2. 游戏开发:创建接近原生性能的Web游戏

  3. 音视频处理:实时编解码和特效处理

  4. 科学计算:在浏览器中运行复杂算法

  5. 3D建模 与渲染:支持复杂的3D场景和模型

2. AI原生前端框架:智能交互的新范式

2025年,前端框架将深度集成人工智能技术,形成AI原生前端框架。这些框架将能够理解用户意图,自动优化性能,并提供更加智能的用户体验。

2.1 AI原生框架核心特性

AI原生前端框架将具备以下核心特性:

  • 智能组件:能够根据用户行为自动调整UI和功能

  • 预测渲染:预测用户下一步操作并提前渲染

  • 自适应布局:根据设备、用户偏好和上下文自动调整布局

  • 智能性能优化:自动识别和优化性能瓶颈

  • 自然语言交互:支持语音和文本的自然语言界面

2.2 架构设计

image.png

2.3 智能组件示例

以下是一个AI原生前端框架中的智能组件示例:

// AI原生组件示例
import { SmartComponent } from 'ai-frontend-framework';

class AdaptiveButton extends SmartComponent {
  constructor(props) {
    super(props);
    this.state = {
      variant: 'primary',
      size: 'medium'
    };
  }
  
  // AI驱动的自适应逻辑
  async adaptToUserContext() {
    const userBehavior = await this.aiContext.getUserBehavior();
    const deviceInfo = await this.aiContext.getDeviceInfo();
    
    // 根据用户行为调整按钮样式
    if (userBehavior.clickSpeed > 5) {
      this.setState({ size: 'large' });
    }
    
    // 根据设备类型调整按钮变体
    if (deviceInfo.type === 'mobile') {
      this.setState({ variant: 'secondary' });
    }
    
    // 预测用户操作并提前加载资源
    if (userBehavior.predictedAction === 'submit') {
      this.aiContext.preloadResource('/api/submit');
    }
  }
  
  render() {
    return (
      <button 
        variant={this.state.variant}
        size={this.state.size}
        onClick={this.props.onClick}
        onMouseEnter={this.adaptToUserContext}
      >
        {this.props.children}
      </button>
    );
  }
}

2.4 应用场景

AI原生前端框架将在以下场景中得到广泛应用:

  1. 个性化用户体验:根据用户行为和偏好自动调整界面

  2. 智能表单:自动填充、验证和优化表单流程

  3. 预测性加载:预测用户下一步操作并提前加载内容

  4. 无障碍设计:自动适配不同用户的无障碍需求

  5. 实时数据可视化:智能分析和展示数据趋势

3. 沉浸式Web体验:从2D到3D的转变

2025年,Web将从平面的2D体验向沉浸式的3D体验转变。随着WebXR、WebGL 2.0和WebGPU的不断成熟,浏览器将能够提供接近原生的3D和虚拟现实体验。

3.1 核心技术栈

沉浸式Web体验的核心技术栈包括:

  • WebXRAPI:支持虚拟现实(VR)和增强现实(AR)体验

  • WebGL 2.0:高性能3D图形渲染

  • WebGPU:下一代图形API,提供更好的性能和功能

  • Three.js/Rapier.js:成熟的3D库和物理引擎

3.2 架构设计

image.png

3.3 应用场景

沉浸式Web体验将在以下场景中得到广泛应用:

  1. 虚拟会议与协作:创建沉浸式的远程会议空间

  2. 在线教育:提供交互式的3D学习体验

  3. 虚拟购物:允许用户在虚拟环境中试穿和体验产品

  4. 虚拟旅游:提供沉浸式的虚拟旅游体验

  5. 工业设计与原型:在浏览器中进行3D设计和原型开发

4. 去中心化前端架构:Web3的新范式

随着Web3技术的不断发展,2025年将出现去中心化前端架构,彻底改变前端应用的部署和运行方式。

4.1 核心概念

去中心化前端架构的核心概念包括:

  • 去中心化存储:使用IPFS、Arweave等去中心化存储协议

  • 智能合约集成:直接与区块链上的智能合约交互

  • 去中心化身份(DID) :用户控制自己的身份和数据

  • 零信任安全:基于区块链的安全模型

4.2 架构设计

image.png

4.3 开发示例

以下是一个基于去中心化前端架构的应用示例:

// 去中心化前端应用示例
import { createDApp } from 'decentralized-frontend-framework';
import { ethers } from 'ethers';
import { create } from 'ipfs-http-client';

// 连接到IPFS
const ipfs = create('https://ipfs.infura.io:5001/api/v0');

// 连接到以太坊区块链
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

// 初始化DApp
const dapp = createDApp({
  ipfs,
  provider,
  signer,
  contracts: {
    myContract: {
      address: '0x1234567890abcdef1234567890abcdef12345678',
      abi: [...]
    }
  }
});

// 从IPFS加载内容
async function loadContent(cid) {
  const content = await dapp.ipfs.get(cid);
  return content;
}

// 与智能合约交互
async function interactWithContract() {
  const contract = dapp.contracts.myContract;
  const result = await contract.functions.myFunction();
  return result;
}

// 保存数据到IPFS并记录到区块链
async function saveData(data) {
  const { cid } = await dapp.ipfs.add(data);
  const transaction = await dapp.contracts.myContract.functions.saveData(cid.toString());
  await transaction.wait();
  return cid;
}

// 渲染应用
function renderApp() {
  return (
    <DAppProvider dapp={dapp}>
      <App />
    </DAppProvider>
  );
}

4.4 应用场景

去中心化前端架构将在以下场景中得到广泛应用:

  1. 去中心化金融(DeFi) :构建安全、透明的金融应用

  2. 数字身份管理:用户控制自己的身份和数据

  3. 内容创作与分发:消除中间商,直接连接创作者和用户

  4. 供应链管理:构建透明、可追溯的供应链系统

  5. 去中心化社交网络:用户控制自己的社交数据和关系

5. 量子计算在前端的初步应用

随着量子计算技术的不断发展,2025年量子计算将开始在前端领域得到初步应用,为前端开发带来新的可能性。

5.1 核心概念

量子计算在前端的应用基于以下核心概念:

  • 量子算法:利用量子力学特性解决复杂问题

  • 量子机器学习:结合量子计算和机器学习

  • 量子安全:基于量子力学的安全加密

  • 量子云服务:通过云服务访问量子计算机

5.2 应用示例

以下是一个使用量子计算的前端应用示例:

// 量子计算前端应用示例
import { QuantumSDK } from 'quantum-frontend-sdk';
import { QuantumMachineLearning } from 'quantum-ml';

// 初始化量子SDK
const quantumSDK = new QuantumSDK({
  apiKey: 'your-quantum-cloud-api-key',
  provider: 'ibm-quantum'
});

// 创建量子机器学习模型
const qml = new QuantumMachineLearning(quantumSDK);

// 训练量子模型
async function trainQuantumModel(data) {
  // 准备量子数据
  const quantumData = await qml.prepareData(data);
  
  // 选择量子算法
  const algorithm = qml.selectAlgorithm('quantum-kernel');
  
  // 训练模型
  const model = await qml.train(quantumData, algorithm);
  
  return model;
}

// 使用量子模型进行预测
async function predictWithQuantumModel(model, input) {
  // 准备输入数据
  const quantumInput = await qml.prepareData(input);
  
  // 进行量子预测
  const result = await qml.predict(model, quantumInput);
  
  // 转换结果为经典数据
  const classicResult = qml.toClassic(result);
  
  return classicResult;
}

// 量子安全加密
async function encryptDataWithQuantumSecurity(data, publicKey) {
  // 使用量子密钥分发生成安全密钥
  const secureKey = await quantumSDK.generateQuantumKey();
  
  // 使用安全密钥加密数据
  const encryptedData = quantumSDK.encrypt(data, secureKey);
  
  // 使用公钥加密安全密钥
  const encryptedKey = quantumSDK.encrypt(secureKey, publicKey);
  
  return { encryptedData, encryptedKey };
}

// 渲染应用
function renderApp() {
  return (
    <QuantumProvider sdk={quantumSDK}>
      <App 
        trainModel={trainQuantumModel}
        predict={predictWithQuantumModel}
        encryptData={encryptDataWithQuantumSecurity}
      />
    </QuantumProvider>
  );
}

5.3 应用场景

量子计算在前端的初步应用将包括:

  1. 量子机器学习:处理复杂的机器学习任务,如图像识别、自然语言处理

  2. 量子安全:提供更安全的加密和认证机制

  3. 优化问题:解决复杂的优化问题,如路径规划、资源分配

  4. 模拟与仿真:进行复杂的物理、化学模拟

  5. 数据分析:处理大规模数据集

6. 总结

2025年的前端技术进入一个全新的时代,从智能到沉浸,从中心化到去中心化,从经典计算到量子计算。这些技术的发展将彻底改变前端开发的方式和前端应用的能力。作为前端开发者,我们需要不断学习和适应这些变化,掌握新的技术和工具,以创造出更加智能、沉浸、安全和高效的用户体验。只有这样,我们才能在未来的前端开发领域保持竞争力,为用户创造出真正有价值的产品和服务。

未来的前端世界充满了无限可能,让我们一起拥抱这个新时代,共同创造更加美好的数字未来!

7. 参考文献

  1. WebAssembly 3.0 官方文档
  2. AI原生前端框架白皮书
  3. WebXR API 规范
  4. IPFS 官方文档
  5. 量子计算在前端的应用
  6. Web3 前端开发指南
  7. Three.js 文档
  8. 前端性能优化最佳实践

8. 团队介绍

智慧家技术平台-应用软件框架开发」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

Claude Code 构建完全指南:十大核心功能深度解析

作者 王小酱
2026年2月25日 21:47

一、创建自定义子代理(Subagents)

1. 是什么

子代理是运行在独立上下文窗口中的专用 AI 助手。每个子代理拥有自己的系统提示、工具访问权限和权限设置。当 Claude 遇到与某个子代理描述匹配的任务时,会自动将任务委派给该子代理,子代理独立工作并返回结果。Claude Code 内置了 Explore(只读代码搜索,使用 Haiku 模型)、Plan(计划模式研究代理)和 general-purpose(全工具通用代理)等子代理,用户也可以创建自定义子代理。

2. 怎么用

创建子代理最简单的方式是在 Claude Code 中运行 /agents 命令,进入交互式界面选择"Create new agent",然后选择作用域(用户级或项目级),输入描述后由 Claude 自动生成配置。也可以手动创建 Markdown 文件,放在 .claude/agents/(项目级)或 ~/.claude/agents/(用户级)目录中:

---
name: code-reviewer
description: Reviews code for quality and best practices
tools: Read, Glob, Grep
model: sonnet
---
You are a code reviewer. Analyze the code and provide actionable feedback.

还可以通过 CLI 标志以 JSON 传递临时子代理:

claude --agents '{ "code-reviewer": { "description": "Expert code reviewer.", "prompt": "Focus on code quality and security.", "tools": ["Read","Grep","Glob","Bash"], "model": "sonnet" } }'

3. 使用场景与痛点

子代理解决的核心痛点是主对话上下文膨胀和工具权限失控。典型场景包括:运行测试套件时大量输出占用上下文(委派给子代理后只返回摘要)、需要对代码库进行并行研究(多个子代理同时探索不同模块)、需要强制只读权限(如代码审查只允许读不允许写)、多步骤工作流的链式委派(先审查再优化),以及通过路由到 Haiku 模型来控制 Token 成本。

4. 使用示例

入门示例——只读代码审查器:

---
name: code-reviewer
description: Expert code review specialist. Use immediately after writing or modifying code.
tools: Read, Grep, Glob, Bash
model: inherit
---
You are a senior code reviewer. Run git diff, focus on modified files, check for readability, error handling, security, and test coverage. Organize feedback by priority: Critical, Warnings, Suggestions.

使用时只需说:"Use the code-reviewer subagent to look at my recent changes."

进阶示例——带持久化记忆的调试器:

---
name: debugger
description: Debugging specialist for errors, test failures, and unexpected behavior. Use proactively when encountering any issues.
tools: Read, Edit, Bash, Grep, Glob
memory: user
---
You are an expert debugger. Capture error messages, identify reproduction steps, isolate failure location, implement minimal fix, verify solution. Before starting, consult your memory for patterns you've seen before. After fixing, save what you learned.

记忆功能使调试器跨会话积累知识——它会记住之前遇到的 bug 模式和修复策略,随着使用越来越高效。

高级最佳实践——带 Hook 验证的数据库查询代理:

---
name: db-reader
description: Execute read-only database queries. Use when analyzing data or generating reports.
tools: Bash
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate-readonly-query.sh"
---
You are a database analyst with read-only access. Execute SELECT queries only.

配合验证脚本,通过 Hook 在每个 Bash 命令执行前检查是否包含 INSERT/UPDATE/DELETE 等写操作,退出码 2 阻止执行。这是"工具级允许、操作级限制"的精细控制模式。其他高级实践包括:使用 skills 字段预加载团队编码规范、用 isolation: worktree 在独立 git worktree 中运行子代理避免影响主分支、用 Task(worker, researcher) 语法限制主代理只能生成特定子代理。

5. 适用角色

个人开发者: 创建用户级子代理(~/.claude/agents/)用于个人常用任务,如代码审查、调试、日志分析。推荐从 /agents 交互式界面入手,用 Haiku 模型的 Explore 子代理快速搜索代码库以节省成本。

团队开发者: 创建项目级子代理(.claude/agents/)并提交到版本控制。统一团队的代码审查标准、API 开发规范。使用 skills 字段预加载团队编码规范,确保每个成员使用的子代理行为一致。

Tech Lead / 架构师: 设计子代理体系——为不同任务领域(前端、后端、数据库、安全)创建专门的子代理,使用权限模式和工具限制确保安全边界。利用 memory: project 让子代理积累项目架构知识。

DevOps / CI 工程师: 通过 --agents CLI 标志在自动化脚本和 CI/CD 流水线中动态定义子代理,用于自动化测试、代码扫描、构建验证等。结合 bypassPermissions 模式实现全自动化执行。


二、运行代理团队(Agent Teams)

1. 是什么

代理团队是一项实验性功能,允许多个独立的 Claude Code 实例协同工作。一个会话充当"团队负责人"(Lead),协调工作、分配任务和综合结果;其他"团队成员"(Teammates)各自拥有独立的上下文窗口,可以直接相互通信、挑战彼此的发现。与子代理的关键区别是:子代理只能向主代理报告结果,而代理团队成员之间可以直接发送消息和协作。

2. 怎么用

首先需要启用实验性功能,在 settings.json 中添加:

{ "env": { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" } }

然后用自然语言告诉 Claude 创建团队:

Create an agent team to review PR #142. Spawn three reviewers:
- One focused on security implications
- One checking performance impact
- One validating test coverage

Claude 会创建团队、生成共享任务列表、生成成员并协调工作。在进程内模式中用 Shift+Down 切换成员;在分屏模式中(需 tmux 或 iTerm2)每个成员有独立窗格。用 Ctrl+T 切换任务列表视图。

3. 使用场景与痛点

代理团队解决的核心痛点是单一会话的线性工作模式无法高效处理需要并行探索的复杂任务。最强场景包括:并行代码审查(安全、性能、测试覆盖各一个审查员,避免单个审查员的视角偏见)、竞争假设调试(多个成员调查不同理论并相互辩论反驳,避免锚定效应)、新功能多模块开发(每个成员负责一个独立模块不会冲突)以及跨层协调(前端、后端、测试各由不同成员负责)。

不适合的场景:顺序任务、同文件编辑、依赖关系多的工作——这些情况下单会话或子代理更高效。

4. 使用示例

入门示例——并行代码审查:

Create an agent team to review PR #142. Spawn three reviewers:
- One focused on security implications
- One checking performance impact
- One validating test coverage
Have them each review and report findings.

每个审查员从不同角度审查同一个 PR,Lead 综合所有发现。

进阶示例——竞争假设调试:

Users report the app exits after one message instead of staying connected.
Spawn 5 agent teammates to investigate different hypotheses.
Have them talk to each other to try to disprove each other's theories,
like a scientific debate. Update the findings doc with whatever consensus emerges.

辩论式结构是关键——多个独立调查者主动尝试推翻对方的理论,幸存的理论更可能是真正的根因。

高级最佳实践:

要求成员在实施前提交计划审批:

Spawn an architect teammate to refactor the authentication module.
Require plan approval before they make any changes.

成员以只读计划模式工作,提交计划后由 Lead 审批或驳回。可以通过提示影响 Lead 的判断标准,如"只批准包含测试覆盖的计划"。其他高级实践包括:使用 TeammateIdle 和 TaskCompleted Hook 实施质量门禁、保持 3-5 个成员和每人 5-6 个任务的最佳比例、给成员充足的上下文(spawn 提示中包含具体的任务细节而非依赖继承)。

5. 适用角色

个人开发者: 从研究和审查任务开始尝试——审查一个 PR、研究一个库、调查一个 bug。这些任务展示并行探索的价值,又没有并行实施的协调挑战。

团队 Lead / 项目经理: 用代理团队进行全方位的代码审查、架构评估和技术方案论证。通过设定审批流程(require plan approval)控制实施质量。

高级工程师: 用于复杂调试(竞争假设模式)、跨模块重构(每个成员负责一个模块)和大规模代码迁移(并行处理不同组件)。注意 Token 消耗——代理团队的成本随成员数线性增长。

注意: 代理团队目前是实验性功能,不建议在关键生产流程中使用。已知限制包括不支持进程内成员的会话恢复、任务状态可能滞后、每个会话只能管理一个团队。


三、创建插件(Plugins)

1. 是什么

插件是 Claude Code 的打包扩展机制,将技能、代理、钩子、MCP 服务器和 LSP 服务器封装成可分发的单元。每个插件有一个 .claude-plugin/plugin.json 清单文件定义元数据,以及按功能组织的目录结构。插件的技能使用命名空间(/plugin-name:skill-name)来避免多插件之间的冲突。

与独立配置(.claude/ 目录中的文件)相比,插件更适合需要跨项目、跨团队共享的场景——独立配置适合个人实验和项目专属定制,插件适合版本化发布和市场分发。

2. 怎么用

创建插件的步骤:

# 1. 创建插件目录和清单
mkdir -p my-plugin/.claude-plugin
# 2. 编写 plugin.json
cat > my-plugin/.claude-plugin/plugin.json << 'EOF'
{ "name": "my-plugin", "description": "My extension", "version": "1.0.0" }
EOF
# 3. 添加技能
mkdir -p my-plugin/skills/hello
cat > my-plugin/skills/hello/SKILL.md << 'EOF'
---
description: Greet the user with a friendly message
---
Greet the user warmly and ask how you can help.
EOF
# 4. 本地测试
claude --plugin-dir ./my-plugin

在 Claude Code 中运行 /my-plugin:hello 即可使用。可以用 --plugin-dir 标志同时加载多个插件进行测试。

3. 使用场景与痛点

插件解决的核心痛点是扩展功能的碎片化和不可共享。典型场景包括:团队统一工具链(所有成员通过安装同一插件获得相同的代码审查代理、提交工作流和格式化钩子)、社区分享(将自己开发的有用扩展发布到市场让其他人安装)、跨项目复用(一套检查规则不需要在每个项目中重新配置)、以及版本化管理(通过语义化版本号追踪发布和更新)。

4. 使用示例

入门示例——包含一个技能的插件:

创建一个 greeting 插件,包含一个 hello 技能。目录结构:greeting/.claude-plugin/plugin.json + greeting/skills/hello/SKILL.md。使用 claude --plugin-dir ./greeting 加载,然后运行 /greeting:hello Alex 测试。

进阶示例——包含代理、钩子和 LSP 的完整插件:

my-team-plugin/
├── .claude-plugin/
│   └── plugin.json
├── agents/
│   └── security-reviewer.md      # 安全审查代理
├── skills/
│   └── code-review/
│       └── SKILL.md               # 代码审查技能
├── hooks/
│   └── hooks.json                 # PostToolUse 自动格式化
├── .mcp.json                      # 集成 GitHub MCP 服务器
├── .lsp.json                      # TypeScript LSP 配置
└── settings.json                  # 默认设置 {"agent": "security-reviewer"}

设置 settings.json 中的 agent 字段可以让插件在启用时自动激活指定代理作为默认行为。

高级最佳实践:

从独立配置迁移到插件:将 .claude/commands/.claude/agents/.claude/skills/ 的文件复制到插件目录,将 settings.json 中的 hooks 迁移到 hooks/hooks.json,然后用 --plugin-dir 验证一切正常。发布前为插件添加 README.md,使用语义化版本号。注意:不要把 commands/agents/ 等目录放在 .claude-plugin/ 里面——只有 plugin.json 放在 .claude-plugin/ 中,其他目录在插件根目录。

5. 适用角色

个人开发者: 先用 .claude/ 目录中的独立配置快速实验,等稳定后再转为插件以跨项目复用。适合创建个人的提交工作流、代码模板等。

团队 Lead / 平台工程师: 为团队创建统一的插件,包含编码规范技能、代码审查代理和自动格式化钩子。通过项目的 .claude/settings.json 配置团队市场,让新成员加入时自动获取团队插件。

开源 / 社区贡献者: 创建通用插件(如 commit-commands、pr-review-toolkit)并发布到市场,供社区使用。遵循清晰的目录结构和版本号规范。

企业 IT 管理员: 通过托管设置(managed settings)部署组织级插件,确保合规性审查工具和安全检查在所有项目中强制启用。


四、发现和安装预构建插件

1. 是什么

插件市场(Marketplace)是帮助用户发现和安装 Claude Code 扩展的目录。市场本质上是一个包含 .claude-plugin/marketplace.json 的仓库或文件,列出了可供安装的插件集合。Anthropic 官方市场(claude-plugins-official)自动可用,包含代码智能(LSP)、外部集成(GitHub/Slack/Jira 等)、开发工作流和输出风格等类别的插件。用户也可以添加第三方市场或创建团队私有市场。

2. 怎么用

# 浏览官方市场
/plugin                             # 打开插件管理器,进入 Discover 标签

# 安装插件
/plugin install typescript-lsp@claude-plugins-official

# 添加第三方市场
/plugin marketplace add anthropics/claude-code        # GitHub 仓库
/plugin marketplace add https://gitlab.com/company/plugins.git  # Git URL
/plugin marketplace add ./my-marketplace              # 本地路径

# 管理插件
/plugin disable plugin-name@marketplace-name          # 禁用
/plugin enable plugin-name@marketplace-name           # 启用
/plugin uninstall plugin-name@marketplace-name        # 卸载

# 管理市场
/plugin marketplace list                              # 列出所有市场
/plugin marketplace update marketplace-name           # 刷新
/plugin marketplace remove marketplace-name           # 移除(会卸载其中的插件)

插件安装有三种作用域:用户(跨所有项目)、项目(通过 .claude/settings.json 共享给协作者)和本地(仅当前用户当前仓库)。

3. 使用场景与痛点

插件市场解决的核心痛点是手动配置外部工具的复杂性和团队配置不一致。安装代码智能插件后,Claude 在每次编辑后自动获得类型错误、缺失导入等诊断反馈,无需手动运行编译器。安装外部集成插件(GitHub、Jira、Slack)后,Claude 可以直接与这些服务交互,无需手动配置 MCP 服务器。团队管理员可以在 .claude/settings.json 中配置 extraKnownMarketplacesenabledPlugins,让团队成员信任仓库后自动安装指定市场和插件。

4. 使用示例

入门示例——安装代码智能插件:

/plugin install typescript-lsp@claude-plugins-official

安装后,Claude 在编辑 TypeScript 文件时自动获得类型检查——如果引入错误会立即发现并在同一轮修复。按 Ctrl+O 可以内联查看诊断信息。前提:系统需安装对应的语言服务器二进制文件(如 typescript-language-server)。

进阶示例——添加外部集成和工作流插件:

# 安装 GitHub 集成
/plugin install github@claude-plugins-official
# 安装提交工作流
/plugin install commit-commands@claude-plugins-official

之后可以直接说"Review PR #456 and suggest improvements"或使用 /commit-commands:commit 一键暂存、生成消息、创建提交。

高级最佳实践:

配置团队市场自动安装:在项目的 .claude/settings.json 中添加 extraKnownMarketplacesenabledPlugins,让团队成员加入项目时自动获取。启用自动更新以保持插件版本最新(官方市场默认启用,第三方默认禁用)。如果需要禁用 Claude Code 自动更新但保持插件更新,可设置 DISABLE_AUTOUPDATER=trueFORCE_AUTOUPDATE_PLUGINS=true

5. 适用角色

所有开发者: 安装代码智能插件是最基础的增强——给 Claude 实时的类型错误和语法问题反馈,减少编辑后需要手动编译验证的次数。

全栈开发者: 安装外部集成插件(GitHub、Slack、Figma、数据库),让 Claude 能直接从 issue 描述实现功能、集成设计稿、查询数据库。

团队管理员: 配置团队市场,确保所有成员使用相同的插件和版本。通过项目级安装(--scope project)将插件配置提交到版本控制。

初学者: 从 Discover 标签浏览可用插件,安装 learning-output-style 或 explanatory-output-style 获得更好的学习体验。


五、使用技能扩展 Claude(Skills)

1. 是什么

技能(Skills)是通过 SKILL.md 文件为 Claude 添加的指令和能力扩展。技能遵循 Agent Skills 开放标准,可以是参考知识(编码规范、API 文档),也可以是具体任务(部署流程、提交规范)。Claude 可以根据任务上下文自动加载相关技能,用户也可以通过 /skill-name 直接调用。

技能与子代理的区别:技能默认在主对话上下文中运行(共享上下文),子代理在隔离的上下文中运行。技能适合需要共享对话历史的场景,子代理适合需要隔离的场景。

2. 怎么用

创建技能目录和 SKILL.md 文件:

mkdir -p ~/.claude/skills/explain-code

编写 ~/.claude/skills/explain-code/SKILL.md

---
name: explain-code
description: Explains code with visual diagrams and analogies. Use when explaining how code works.
---
When explaining code, always include:
1. **Start with an analogy**: Compare the code to something from everyday life
2. **Draw a diagram**: Use ASCII art to show the flow
3. **Walk through the code**: Explain step-by-step
4. **Highlight a gotcha**: What's a common mistake?

两种使用方式:让 Claude 自动识别("How does this code work?")或直接调用(/explain-code src/auth/login.ts)。

3. 使用场景与痛点

技能解决的核心痛点是重复的指令输入和行为不一致。没有技能时,每次需要 Claude 按特定方式工作都要重新描述需求;有了技能,一次编写,反复使用。典型场景包括:团队编码规范强制执行(API 设计模式、错误处理模式作为参考技能自动加载)、标准化操作流程(部署、提交、PR 创建作为任务技能由用户触发)、自定义代码生成模板(组件创建、测试编写遵循固定结构),以及跨项目知识复用(个人级技能在所有项目中可用)。

4. 使用示例

入门示例——参考型技能(API 规范):

---
name: api-conventions
description: API design patterns for this codebase
---
When writing API endpoints:
- Use RESTful naming conventions
- Return consistent error formats: { "error": { "code": "...", "message": "..." } }
- Include request validation with Zod schemas
- Always add rate limiting middleware

Claude 在实现 API 时会自动加载这个技能,确保遵循团队约定。

进阶示例——带参数和动态上下文的任务技能:

---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---
Fix GitHub issue $ARGUMENTS following our coding standards.

## Issue context
- Issue details: !`gh issue view $0 --json title,body,labels`
- Recent commits: !`git log --oneline -5`

## Steps
1. Read the issue description
2. Implement the fix
3. Write tests
4. Create a commit

使用 /fix-issue 123 时,!command 语法预先执行 shell 命令获取实时数据,$0 替换为第一个参数。disable-model-invocation: true 确保只有用户手动触发。

高级最佳实践——在子代理中运行技能 + 可视化输出:

---
name: deep-research
description: Research a topic thoroughly
context: fork
agent: Explore
---
Research $ARGUMENTS thoroughly:
1. Find relevant files using Glob and Grep
2. Read and analyze the code
3. Summarize findings with specific file references

context: fork 使技能在隔离的 Explore 子代理中运行,不影响主对话。另一个强大模式是生成可视化输出——技能中捆绑 Python 脚本生成交互式 HTML 文件用于数据探索。其他高级实践:使用 allowed-tools 限制技能中 Claude 可用的工具、用 Skill(commit) / Skill(deploy *) 权限规则精细控制哪些技能 Claude 可以自动调用、SKILL.md 控制在 500 行内并将详细参考材料拆分到辅助文件中。

5. 适用角色

个人开发者: 创建用户级技能(~/.claude/skills/)封装个人工作习惯——自定义提交格式、代码解释风格、调试流程。用 disable-model-invocation: true 保护有副作用的操作(如部署)。

团队开发者: 创建项目级技能(.claude/skills/)并提交到版本控制,统一团队的 API 设计模式、错误处理方式、测试编写规范。新成员无需阅读长篇文档,Claude 自动遵循。

技术培训师 / 导师: 创建教学型技能(如 explain-code),让 Claude 始终用类比、图表和分步讲解的方式解释代码,帮助初级开发者学习。

平台工程师: 通过插件分发技能、通过企业托管设置强制加载组织级技能,确保合规性检查和安全扫描在所有项目中自动执行。


六、输出风格(Output Styles)

1. 是什么

输出风格直接修改 Claude Code 的系统提示,改变 Claude 的响应方式——包括格式、语调和结构。与 CLAUDE.md(作为附加的用户消息)和 --append-system-prompt(追加到系统提示末尾)不同,输出风格会关闭 Claude Code 默认系统提示中与软件工程相关的部分,用自定义指令替代。Claude Code 内置三种风格:默认(高效完成软件工程任务)、解释性(在工作中穿插教育性"洞察")、学习(协作式学做模式,用 TODO(human) 标记让用户自己写代码)。

2. 怎么用

# 交互式选择
/output-style

# 直接切换到指定风格
/output-style explanatory
/output-style learning

创建自定义输出风格,保存为 Markdown 文件到 ~/.claude/output-styles/(用户级)或 .claude/output-styles/(项目级):

---
name: My Custom Style
description: A brief description
keep-coding-instructions: false
---
# Custom Style Instructions
You are an interactive CLI tool that helps users with software engineering tasks.
[Your custom instructions...]

keep-coding-instructions: true 可以保留 Claude Code 默认的编码相关系统提示(如"用测试验证代码")。

3. 使用场景与痛点

输出风格解决的核心痛点是 Claude 的响应方式与用户需求不匹配。有些人需要简洁的执行结果,有些人需要详细的解释以学习。典型场景包括:初学者学习模式(learning 风格让 Claude 不仅写代码还要求你自己实践)、教学演示(explanatory 风格在每个实现决策后插入背景知识)、非软件工程任务(关闭默认的编码指令,让 Claude 专注于写作、分析等)、团队统一输出格式(通过项目级输出风格确保所有人看到相同格式的响应)。

4. 使用示例

入门示例——切换到学习模式:

/output-style learning

切换后,Claude 在帮你完成任务时会添加 TODO(human) 标记,要求你自己实现关键部分,同时分享"洞察"帮助你理解为什么这样做。

进阶示例——创建自定义架构师风格:

---
name: architect
description: Respond with architecture-first thinking. Always discuss trade-offs before implementing.
keep-coding-instructions: true
---
# Architect Mode
Before writing any code:
1. Identify the architectural implications
2. List alternative approaches with trade-offs
3. Recommend an approach with justification
4. Only implement after the user approves

Always think about: scalability, maintainability, testability, and security.
Use diagrams (ASCII art) to illustrate architecture decisions.

高级最佳实践:

理解输出风格与其他扩展机制的区别非常重要:输出风格修改系统提示、始终生效;技能是按需加载的任务指令;CLAUDE.md 是始终存在的上下文补充。最佳实践是用输出风格控制"Claude 如何说",用技能控制"Claude 做什么",用 CLAUDE.md 提供"Claude 需要知道什么"。变更保存在 .claude/settings.local.json,也可直接编辑其他级别设置文件中的 outputStyle 字段。

5. 适用角色

初学者 / 学生: 使用 learning 风格,让 Claude 变成交互式编程导师——不仅帮你写代码,还要求你自己实践关键部分,加速学习。

经验开发者: 使用默认风格或创建极简自定义风格,获得简洁高效的输出,减少冗余解释。

技术 Lead / 导师: 使用 explanatory 风格或创建架构师风格,在代码审查和设计讨论时获得详细的决策背景和权衡分析。

非工程人员(产品、设计): 创建自定义输出风格关闭编码指令,让 Claude 专注于文档编写、数据分析或项目管理任务。


七、使用钩子自动化工作流(Hooks)

1. 是什么

钩子(Hooks)是在 Claude Code 生命周期特定节点执行的用户定义 shell 命令,提供确定性的行为控制——确保某些操作始终发生,而非依赖 LLM 的判断。钩子支持丰富的事件类型:SessionStart(会话开始/恢复/压缩后)、PreToolUse(工具执行前,可阻止)、PostToolUse(工具执行后)、Notification(通知时)、Stop(Claude 完成响应时)、ConfigChange(配置变更时)等。除了命令型钩子外,还有提示型(type: "prompt",使用 Claude 模型判断)和代理型(type: "agent",生成子代理验证条件)。

2. 怎么用

最快的方式是运行 /hooks 进入交互式菜单,选择事件、配置匹配器和命令。也可以直接在配置文件中编写:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

钩子通过 stdin 接收 JSON 事件数据,通过退出码控制行为:0 = 继续,2 = 阻止(stderr 内容反馈给 Claude),其他 = 继续但记录。配置文件位置:~/.claude/settings.json(所有项目)、.claude/settings.json(单项目共享)、.claude/settings.local.json(单项目私有)。

3. 使用场景与痛点

钩子解决的核心痛点是手动重复操作和依赖 LLM 判断的不可靠性。典型场景包括:每次编辑后自动格式化(PostToolUse + Edit|Write 匹配器 + Prettier)、保护敏感文件不被修改(PreToolUse 检查文件路径拒绝 .env、package-lock.json)、桌面通知(Notification 事件触发 osascript/notify-send)、压缩后重新注入关键上下文(SessionStart + compact 匹配器)、审计配置变更(ConfigChange 事件记录到日志文件)。

4. 使用示例

入门示例——桌面通知:

macOS:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

保存到 ~/.claude/settings.json,之后每次 Claude 等待输入时都会收到系统通知。

进阶示例——保护敏感文件 + 自动格式化:

创建 .claude/hooks/protect-files.sh 脚本,从 stdin 读取 JSON,用 jq 提取 tool_input.file_path,检查是否匹配 .envpackage-lock.json.git/ 等受保护模式,匹配则 exit 2 阻止。同时配置 PostToolUse 钩子在每次 Edit/Write 后自动运行 Prettier。这样实现了"阻止 + 后处理"的双层自动化。

高级最佳实践——提示型和代理型钩子:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains\"}."
          }

Skill Seekers 全面指南:让 AI 真正"读懂"你的技术文档

作者 王小酱
2026年2月25日 19:59

从零基础入门到架构原理深度剖析——一份写给所有 AI 从业者的渐进式技术博客


写在最前面:这篇文章适合谁?

这篇文章按照由浅入深的结构组织,不同读者可以选择适合自己的起始章节。

如果你是对 AI 工具好奇的普通开发者,从第一章开始读,你将在 15 分钟内理解 Skill Seekers 是什么,并完成第一个实际操作。如果你是正在搭建 RAG 管线的 AI 工程师,可以直接跳到第四章和第五章,那里有针对 LangChain、LlamaIndex、向量数据库等场景的最佳实践。如果你是架构师或技术负责人,第六、七、八章从源码层面深入分析了项目的设计哲学、模块架构和工程体系。如果你是想参与开源贡献的开发者,第九章介绍了项目的工程规范与贡献流程。

开源项目地址:https://github.com/yusufkaraaslan/Skill_Seekers/tree/development


第一章:三分钟理解 Skill Seekers

1.1 一个故事开始

假设你是一名前端开发者,刚加入团队,团队技术栈基于 React。你希望让 Claude Code 成为你的"React 专家搭档"——不是那种只知道泛泛基础知识的通用 AI,而是真正了解 React 最新 API、Hooks 最佳实践、Server Components 细节的领域专家。

你面临一个尴尬的处境:React 官方文档有数百个页面,散布在 react.dev 网站上。你总不能一页一页复制粘贴到 Claude 的对话框里吧?即使你这么做了,Claude 的上下文窗口也装不下这么多内容。

Skill Seekers 做的事情,就是帮你把这几百页文档,在 15 分钟之内变成一个 Claude 能直接加载和使用的"技能包"。

打开终端,三条命令:

# 第 1 步:安装
pip install skill-seekers

# 第 2 步:一键抓取 React 文档并生成知识资产
skill-seekers create https://docs.react.dev/

# 第 3 步:打包为 Claude 能用的格式
skill-seekers package output/react --target claude

完成后你的 output/ 目录里会出现一个 react-claude.zip,上传到 Claude 就行了。从此 Claude 就是你的 React 领域专家。

但这只是冰山一角——同一份知识资产还可以导出为 Gemini、OpenAI、LangChain、Cursor 等十多个平台的格式,做一次就够了。

1.2 用一句话定义 Skill Seekers

Skill Seekers 是 AI 系统的"数据层"(Data Layer)——它将散落在文档网站、GitHub 仓库、PDF 文件中的非结构化技术知识,自动转化为各类 AI 系统可以直接消费的结构化知识资产。

你可以把它理解成一个"AI 的翻译官":一边读懂人类的文档,一边把知识翻译成 AI 能高效理解的格式。

1.3 它支持哪些输入和输出?

输入端,Skill Seekers 能从三种来源获取知识。第一种是文档网站——任意在线技术文档,如 React、Django、Godot 等官网文档。第二种是 GitHub 仓库——通过 owner/repo 格式指定,系统会分析代码结构、README、Issues 等。第三种是 PDF 文件——技术手册、API 文档、论文等。

输出端则覆盖了当前 AI 生态中几乎所有主流的消费方。包括 Claude AI(ZIP + YAML)、Google Gemini(tar.gz)、OpenAI / Custom GPT(ZIP)、LangChain Documents(JSON)、LlamaIndex TextNodes(JSON)、Haystack Documents、Pinecone / ChromaDB / FAISS / Qdrant 等向量数据库就绪格式,以及 Cursor / Windsurf / Cline / Continue.dev 等 IDE AI 助手的规则文件。

一次预处理,十六个目标平台,这是 Skill Seekers 最核心的价值主张。


第二章:手把手入门——从安装到创建第一个 Skill

这一章面向完全没有用过 Skill Seekers 的读者,按照实际操作步骤逐一展开。

2.1 环境准备

你需要准备的东西非常少:Python 3.10 或更高版本,Git,以及一台能联网的电脑(macOS、Linux 或 Windows 均可)。

检查 Python 版本:

python3 --version
# 看到 Python 3.10.x 或更高即可

检查 Git:

git --version
# 看到 git version 2.x.x 即可

如果 Python 未安装,macOS 用户可以用 brew install python3,Ubuntu/Debian 用户用 sudo apt install python3 python3-pip,Windows 用户从 python.org 下载安装器(注意勾选"Add Python to PATH")。

2.2 安装 Skill Seekers

最简单的安装方式只需一行命令:

pip install skill-seekers

这会安装核心功能:文档抓取、GitHub 分析、PDF 处理和所有平台的打包能力。如果你需要额外能力,可以按需安装可选组件:

# 如果你需要 Google Gemini 支持
pip install skill-seekers[gemini]

# 如果你需要 OpenAI 支持
pip install skill-seekers[openai]

# 如果你需要 MCP 服务器(与 Claude Code 集成)
pip install skill-seekers[mcp]

# 全部安装
pip install skill-seekers[all]

安装完成后,验证一下:

skill-seekers --help

看到帮助信息就说明安装成功了。

2.3 你的第一个 Skill:5 分钟搞定

我们用一个小型示例开始,避免第一次就等待太长时间。来抓取 Tailwind CSS 的文档,限制为 5 个页面:

skill-seekers scrape \
  --name tailwind-test \
  --url https://tailwindcss.com/docs/installation \
  --description "Tailwind CSS quick reference" \
  --max-pages 5

大约 30 秒后,你会看到类似这样的输出:

Scraping: https://tailwindcss.com/docs/installation
Page 1/5: Installation
Page 2/5: Editor Setup
...
 Skill created at: output/tailwind-test/

看看生成了什么:

ls output/tailwind-test/
# SKILL.md  references/  scripts/  assets/

其中 SKILL.md 是核心知识文件,references/ 目录下是按主题分类的参考文档。

2.4 打包与上传

# 打包为 Claude 格式
skill-seekers package output/tailwind-test/
# ✅ Created: output/tailwind-test.zip

# 或者打包为其他平台格式
skill-seekers package output/tailwind-test/ --target gemini
skill-seekers package output/tailwind-test/ --target langchain

如果你配置了 Anthropic API Key,还可以一步到位自动上传:

export ANTHROPIC_API_KEY=sk-ant-...
skill-seekers package output/tailwind-test/ --upload

没有 API Key 也没关系——拿着生成的 .zip 文件去 claude.ai 的 Skills 页面手动上传即可。

2.5 使用预设配置:更省心的方式

Skill Seekers 内置了 24+ 个框架的预设配置,覆盖了 React、Vue、Angular、Django、FastAPI、Godot 等主流框架。用预设配置更加省心:

# 查看所有可用预设
skill-seekers list-configs

# 直接用预设抓取
skill-seekers scrape --config configs/godot.json

你也可以用交互式模式,系统会引导你一步步完成配置:

skill-seekers scrape --interactive

2.6 create 命令:最智能的入口

skill-seekers create 是项目提供的最便捷命令——它会自动识别你给的是什么来源,并选择对应的处理方式:

# 给一个 URL,自动走文档抓取
skill-seekers create https://docs.django.com/

# 给一个 owner/repo,自动走 GitHub 分析
skill-seekers create facebook/react

# 给一个本地路径,自动分析本地项目
skill-seekers create ./my-project

# 给一个 PDF 文件,自动走 PDF 提取
skill-seekers create manual.pdf

这种"零配置"体验大幅降低了上手门槛——你不需要记住不同的子命令,一个 create 就够了。


第三章:Skill Seekers 解决了什么问题?谁需要它?

理解了基本用法之后,我们退后一步,从更宏观的视角审视这个工具为什么存在。

3.1 AI 时代的"知识注入"难题

大语言模型的能力已经毋庸置疑,但模型本身有一个固有限制:训练数据的时效性。无论是 Claude、GPT-4 还是 Gemini,它们的知识都有一个截止日期。对于快速迭代的技术框架来说,官方文档可能每周都在更新,而模型的训练数据可能已经是半年前的了。

解决这个问题的主流方案有两种。第一种是 AI Skills / Knowledge:将结构化知识直接注入 AI 的上下文(如 Claude Skills、Custom GPTs),让 AI 在回答时能参考这些外挂知识。第二种是 RAG(检索增强生成):将知识向量化存储在数据库中,用户提问时检索最相关的文档片段,拼入上下文后让模型回答。

无论哪种方案,数据预处理都是第一步,也是最脏最累的一步。你需要从各种来源抓取内容、清洗 HTML、提取代码块、识别语言、分类组织、生成元数据……而且每换一个目标平台,格式要求就不一样。

Skill Seekers 将这整个预处理流程自动化了,并且做到了"一次处理、多目标导出"。

3.2 四类核心用户群体

经过对项目功能和文档的深入分析,Skill Seekers 的用户群体可以清晰地分为四类。

第一类:AI Skill 构建者。 这是使用 Claude Skills、Gemini Extensions、Custom GPTs 的开发者或技术写作者。他们的核心诉求是把特定领域的知识"教"给 AI,让 AI 成为该领域的专家助手。痛点在于手动整理文档耗时巨大,且不同 AI 平台要求的格式各异。

第二类:RAG 工程师。 这些是搭建企业级知识问答系统、智能客服、文档检索等 RAG 应用的工程师。他们的核心诉求是获得高质量、带元数据、分块合理的文档数据。痛点在于数据预处理流程繁琐,分块策略难以兼顾精度和上下文。

第三类:AI 编程助手用户。 这些是使用 Cursor、Windsurf、Cline 等 AI 辅助编程工具的开发者。他们的核心诉求是让 IDE 中的 AI 助手深度理解特定框架的最新用法。痛点在于 AI 助手的通用知识不够深入,需要手动维护上下文规则文件。

第四类:技术团队和企业。 这些团队需要将内部文档、私有 API 文档、跨项目知识等统一管理,构建团队级别的 AI 知识资产。痛点在于知识散落在多个系统中,且缺乏统一的预处理和分发管道。

3.3 适用场景全景

根据用户群体,Skill Seekers 的典型使用场景包括以下几类:

框架学习加速: 新入职开发者快速将团队使用的技术栈文档转化为 AI Skill,让 AI 成为"老员工"一样的带教导师。

文档智能检索: 将公司内部文档库转化为 RAG 数据集,搭建内部知识问答系统。

代码助手增强: 为 Cursor/Windsurf 生成精确的框架规则文件,让代码建议更准确。

文档质量审计: 利用冲突检测功能,发现文档与实际代码实现之间的不一致之处。

多源知识融合: 将文档网站 + GitHub 代码 + PDF 手册合并为一个统一的知识资产,消除信息孤岛。


第四章:面向不同用户的最佳实践与示例

这一章按用户群体分别给出详细的最佳实践方案和操作示例。

4.1 AI Skill 构建者的最佳实践

场景:为 Claude 创建一个 Django 专家技能

这是最基础也最常见的使用场景。完整工作流如下:

# 步骤 1:从文档创建知识资产
skill-seekers create https://docs.djangoproject.com/

# 步骤 2:AI 增强——将基础文档升级为专家级技能文件
skill-seekers enhance output/django/ --mode local
# 如果有 API Key,也可以用 API 模式:
# skill-seekers enhance output/django/ --mode api

# 步骤 3:打包并上传
skill-seekers package output/django/ --upload

增强步骤是关键——不经过增强的 SKILL.md 只是文档的简单组织,增强之后的 SKILL.md 会包含 500+ 行的内容,涵盖代码示例、最佳实践模式、快速参考指南和错误排查建议。

进阶:使用工作流预设做专项增强

如果你的 Django 项目对安全性有特殊要求,可以叠加安全增强工作流:

skill-seekers create https://docs.djangoproject.com/ \
  --enhance-workflow security-focus \
  --enhance-workflow api-documentation

这条命令会先执行安全聚焦的增强(审查 OWASP Top 10、认证授权模式等),然后再执行 API 文档增强。两个工作流链式执行,后续工作流会引用前序工作流的分析结果。

项目内置了 64 个工作流预设,覆盖了从 defaultminimalkubernetes-deploymentgraphql-schemacompliance-gdprmlops-pipeline 等极为广泛的领域。你还可以创建自定义预设放到 ~/.config/skill-seekers/workflows/ 目录下。

进阶:多平台批量导出

一次处理,导出到所有平台:

# 批量导出到 Claude、Gemini、OpenAI、Markdown 四个平台
for platform in claude gemini openai markdown; do
  skill-seekers package output/django --target $platform
done

4.2 RAG 工程师的最佳实践

场景:搭建一个基于 LangChain 的框架文档问答系统

RAG 工程师最关心的是数据质量——分块是否合理、元数据是否丰富、代码块是否保持完整。

# 步骤 1:抓取文档
skill-seekers create https://docs.react.dev/

# 步骤 2:导出为 LangChain Documents 格式
skill-seekers package output/react --target langchain
# 生成:output/react-langchain.json

导出的 JSON 文件中,每个 Document 都包含 page_content(文档内容)和 metadata(元数据,包括来源 URL、分类、内容类型等)。你可以直接将其加载到 LangChain 的检索链中。

项目的 examples/langchain-rag-pipeline/ 目录提供了完整的端到端示例。类似地,examples/llama-index-query-engine/ 提供了 LlamaIndex 的集成示例,examples/pinecone-upsert/ 提供了 Pinecone 向量数据库的写入示例。

进阶:使用 Docker Compose 搭建完整 RAG 基础设施

Skill Seekers 的 docker-compose.yml 已经预置了一个完整的 RAG 基础设施:

# 一键启动:CLI 工具 + MCP 服务器 + Weaviate + Qdrant + ChromaDB
docker-compose up -d

这会启动五个容器化服务。skill-seekers 容器是主 CLI 工具。mcp-server 在 8765 端口提供 MCP HTTP 服务。weaviate 在 8080 端口提供 Weaviate 向量数据库。qdrant 在 6333/6334 端口提供 Qdrant 向量数据库。chroma 在 8000 端口提供 ChromaDB。

所有服务通过内部 bridge 网络互联,向量数据库配置了持久化卷。你可以把文档抓取、增强、向量化入库的全流程在容器环境中完成。

进阶:处理超大型文档(10K-40K+ 页面)

对于 Godot、Unity 这类超大型文档,直接抓取会产生巨大的单一文件。Skill Seekers 提供了文档拆分和路由机制:

# 先评估文档规模
skill-seekers estimate --config configs/godot.json
# 📊 Estimated pages: 40,000
# ⚠️ Large documentation detected!

# 使用 router 策略拆分
skill-seekers split --config configs/godot.json --strategy router --target-pages 5000
# 会生成多个子配置:godot-scripting.json, godot-2d.json, godot-3d.json 等

# 并行抓取所有子技能
# (每个子技能独立抓取,可以并行执行)

# 最后生成路由器技能
skill-seekers generate-router --config-pattern "configs/godot-*.json"

路由器技能的 SKILL.md 包含智能路由逻辑——当用户提问时,路由器会根据关键词将问题导向合适的子技能。比如问到"physics"就路由到 godot-physics,问到"shader"就路由到 godot-shaders。

4.3 AI 编程助手用户的最佳实践

场景:让 Cursor IDE 的 AI 深度理解 React

Cursor、Windsurf 等 IDE 的 AI 助手支持加载上下文规则文件,让 AI 在生成代码时参考特定框架的最佳实践。

# 创建 React 技能
skill-seekers create https://docs.react.dev/

# 打包为 Claude 格式(Cursor 使用相同格式)
skill-seekers package output/react --target claude

# 复制到你的项目中
cp output/react-claude/SKILL.md my-react-project/.cursorrules

对于 Windsurf:

cp output/react-claude/SKILL.md my-project/.windsurf/rules/react.md

对于 Cline(VS Code 扩展):

cp output/react-claude/SKILL.md my-project/.clinerules

项目的 examples/ 目录提供了多个真实示例:cursor-react-skill/ 展示了 Cursor + React 的集成方式,windsurf-fastapi-context/ 展示了 Windsurf + FastAPI 的场景,cline-django-assistant/ 展示了 Cline + Django 的用法。

进阶:使用 install-agent 命令一键安装到所有 IDE

# 一键安装到 Cursor
skill-seekers install-agent output/react/ --agent cursor

# 或者安装到所有支持的 AI 编程助手
skill-seekers install-agent output/react/ --agent all

# 预览安装效果但不实际执行
skill-seekers install-agent output/react/ --agent cursor --dry-run

这条命令会自动将 Skill 文件复制到对应 IDE 的配置目录。支持的 Agent 包括 Claude Code(~/.claude/skills/)、Cursor(.cursor/skills/)、VS Code / Copilot(.github/skills/)、Amp(~/.amp/skills/)、Goose、OpenCode、Windsurf 等。

4.4 团队协作者的最佳实践

场景:在 5 人团队中共享内部 API 文档的 AI 技能

Skill Seekers 支持从私有 Git 仓库获取配置文件,实现团队级别的技能共享:

# 注册团队的私有配置仓库
# (通过 MCP 工具,在 Claude Code 中以自然语言操作更为便利)
skill-seekers config --add-source \
  --name team \
  --git-url https://github.com/mycompany/skill-configs.git

# 从团队仓库获取配置
skill-seekers config --fetch --source team --config internal-api

# 正常使用
skill-seekers scrape --config internal-api.json

支持 GitHub、GitLab、Gitea、Bitbucket 四种 Git 托管平台,通过对应的环境变量(GITHUB_TOKENGITLAB_TOKEN 等)进行认证。

场景:多源知识融合——将文档 + 代码 + PDF 合并为单一知识资产

这是企业场景中最有价值的能力之一。以 Godot 引擎为例,其预设配置展示了如何融合文档和代码两个来源:

配置中定义了 merge_mode: "claude-enhanced",然后在 sources 数组中分别配置了两个来源。第一个来源类型为 documentation,指向 Godot 官方文档网站,配置了 CSS 选择器、URL 过滤规则和内容分类。第二个来源类型为 github,指向 godotengine/godot 仓库,启用了深度代码分析、Issue 获取、Changelog 和 Release 信息抓取,以及特定的文件匹配模式(core/**/*.hscene/**/*.cpp 等)。

运行统一抓取后,系统会自动执行冲突检测——发现文档中描述但代码中不存在的 API,或者代码中实现但文档中未记录的功能,并在最终输出中以醒目标注呈现。


第五章:核心功能全景透视

在理解了"谁在用"和"怎么用"之后,让我们系统性地审视 Skill Seekers 的功能全景。

5.1 llms.txt 优先检测:10 倍速度提升的秘密

llms.txt 是一个新兴的约定标准——越来越多的技术文档网站开始提供专门为 LLM 消费优化的纯文本文件。Skill Seekers 在正式爬取之前,会依次检查目标域名下是否存在 llms-full.txtllms.txtllms-small.txt

如果检测到这些文件,系统直接下载解析即可——完全跳过了逐页爬取、HTML 解析、内容提取等耗时步骤。这在实际效果上意味着:原本需要 15 分钟的抓取任务,可能 1-2 分钟就完成了。

这个功能由三个模块协同实现:llms_txt_detector.py 负责探测文件是否存在,llms_txt_downloader.py 负责高效下载,llms_txt_parser.py 负责解析内容结构。

5.2 异步模式:2-3 倍的爬取加速

对于不支持 llms.txt 的网站,Skill Seekers 提供了基于 httpx 异步引擎的加速模式:

skill-seekers scrape --config configs/react.json --async --workers 8

--async 标志启用异步爬取(底层使用 httpx 的 async/await),--workers 指定并发工作者数量。在实际测试中,同步模式需要 15-45 分钟的任务,异步模式只需 5-15 分钟。

5.3 AI 增强工作流:从 75 行到 500+ 行的质变

基础的文档抓取只能生成结构化的参考文件。AI 增强步骤是将"数据"转化为"知识"的关键。

增强过程由 LLM 驱动——系统将抓取到的文档内容作为上下文,让 LLM 分析后生成一份综合性的 SKILL.md 文件。这份文件不是简单的摘要或合并,而是包含了以下几个维度的内容:核心概念和设计理念的阐述、带注释的代码示例和最佳实践、常见错误和解决方案、快速参考索引、以及从基础到进阶的导航建议。

系统支持三个 LLM 平台执行增强——Claude Sonnet 4(通过 Anthropic API 或 LOCAL 模式)、Gemini 2.0 Flash(通过 Google API)、GPT-4o(通过 OpenAI API)。LOCAL 模式的独特之处在于它利用 Claude Code Max 的本地执行能力,无需 API Key 也无需额外费用。

此外,通过 ANTHROPIC_BASE_URL 环境变量,中国大陆用户可以配置 GLM-4.7 等兼容 Claude 协议的国产 API 端点来完成增强。

5.4 冲突检测:文档与代码的一致性审计

当同时从文档和代码两个来源获取信息时,Skill Seekers 的冲突检测引擎会自动识别四类不一致。

红色:Missing in code(高优先级)。 文档中描述了某个 API 或功能,但在代码中找不到对应实现。这通常意味着文档描述了尚未实现的特性,或者该功能已被移除但文档未更新。

黄色:Missing in docs(中优先级)。 代码中实现了某个功能,但文档中完全没有提及。这是最常见的文档欠缺类型。

橙色:Signature mismatch(警告级别)。 同一个函数在文档和代码中有不同的参数列表、类型定义或返回值。

灰色:Description mismatch(信息级别)。 文档描述与代码注释对同一功能给出了不同的解释文字。

这个能力不仅提升了生成技能的可靠性,本身也是一个独立的文档质量审计工具。

5.5 MCP 集成:用自然语言驱动整个工作流

MCP(Model Context Protocol)是 Anthropic 推出的协议标准,用于让 AI 助手与外部工具交互。Skill Seekers 的 MCP 服务器暴露了 26 个工具,分为四类。

核心工具(9 个):list_configs, generate_config, validate_config, estimate_pages, scrape_docs, package_skill, upload_skill, enhance_skill, install_skill。

扩展工具(10 个):scrape_github, scrape_pdf, unified_scrape, merge_sources, detect_conflicts, add_config_source, fetch_config, list_config_sources, remove_config_source, split_config。

向量数据库工具(4 个):export_to_chroma, export_to_weaviate, export_to_faiss, export_to_qdrant。

云存储工具(3 个):cloud_upload, cloud_download, cloud_list。

配置好 MCP 后,你可以在 Claude Code 中直接用自然语言完成一切:

用户:帮我抓取 Svelte 文档并打包为 Claude Skill
Claude Code[调用 generate_config][调用 scrape_docs][调用 enhance_skill][调用 package_skill]
✅ 已创建 output/svelte.zip,可以上传到 Claude

MCP 服务器支持两种传输模式——stdio 模式(用于 Claude Code、VS Code + Cline 的本地集成)和 HTTP 模式(用于 Cursor、Windsurf、IntelliJ 的网络集成,默认端口 8765)。

5.6 断点续传:永不丢失进度

考虑到大型文档的抓取可能需要数十分钟甚至数小时,Skill Seekers 实现了作业恢复机制。系统以可配置的间隔(默认 60 秒)自动保存进度。如果中途意外中断,可以查看并恢复:

# 查看所有可恢复的作业
skill-seekers resume --list

# 从中断处继续
skill-seekers resume github_react_20260117_143022

旧作业会在 7 天后自动清理,不会无限占用磁盘。


第六章:架构原理深度剖析

从这一章开始,我们深入到项目的内部设计中。

6.1 项目代码结构解析

Skill Seekers v3.1.3 的 development 分支采用 src 布局(即源码位于 src/skill_seekers/ 而非项目根目录),这是 Python 社区推荐的现代项目结构,通过 pyproject.toml[tool.setuptools] package-dir = {"" = "src"} 配置实现。

核心源码分为六个子包。cli/ 是最庞大的模块,包含约 75 个 Python 文件和 5 个子目录(adaptors、arguments、parsers、presets、storage),承载了几乎所有的业务逻辑——从爬虫到分析到增强到打包。mcp/ 实现 MCP 协议服务器,将 CLI 能力暴露为 26 个可通过自然语言调用的工具。embedding/ 包含嵌入向量相关的模型(models.py)、生成器(generator.py)、缓存(cache.py)和服务器(server.py)四个模块。workflows/ 存放 64 个 YAML 格式的增强工作流预设。sync/ 处理同步监控逻辑,基于 schedule 库实现定时更新。benchmark/ 提供性能基准测试工具。

6.2 五阶段数据处理管线

Skill Seekers 的数据流遵循一条清晰的五阶段管线:Ingest → Analyze → Structure → Enhance → Export

Ingest(摄取)阶段: 这是整个管线的入口,由三个专用爬取器负责。doc_scraper.py 处理文档网站,内部通过 llms_txt_detector.py 优先检测 llms.txt 快速通道,未命中时退回到基于 BeautifulSoup4 的 HTML 解析模式,markdown_cleaner.py 负责将 HTML 转化为干净的 Markdown。github_scraper.py 处理 GitHub 仓库,采用三流架构(Code / Docs / Insights),其中 Code 流使用 unified_codebase_analyzer.py 进行 AST 级别的深度代码分析,Docs 流提取 README 和文档文件,Insights 流通过 PyGithub 调用 GitHub API 获取 Issues、Labels、Stars 等社区数据。pdf_scraper.py 处理 PDF 文件,底层基于 PyMuPDF 引擎,叠加了三种互补的代码检测方法(字体特征、缩进模式、模式匹配)和支持 19+ 种语言的识别能力。

Analyze(分析)阶段: 这一阶段对原始内容进行深度语义分析。code_analyzer.py 执行 AST 解析(支持 Python、JavaScript、TypeScript、Java、C++、Go),提取函数签名、类结构、方法参数和类型信息。architectural_pattern_detector.py 识别工厂模式、单例模式、观察者模式等设计模式。dependency_analyzer.py 基于 networkx 构建依赖关系图谱。conflict_detector.py 执行前文描述的四级冲突检测。signal_flow_analyzer.py 分析代码中的信号和事件流。config_extractor.py

让 AI 学会"组队打怪"——聊聊微软的 AutoGen 框架

作者 王小酱
2026年2月25日 19:15

你有没有想过,一个 AI 助手再聪明,终究也是一个人在战斗。它写完代码没人 review,它做完分析没人挑刺,它回答问题也没人帮忙查漏补缺。

但如果让好几个 AI 坐在一起,各管一摊,一个写、一个审、一个改,会怎样?

这就是微软开源的 AutoGen 在做的事情。

一句话说清楚它是什么

AutoGen 是一个用 Python 搭建多智能体应用的框架。说白了,它让你可以创建多个各有分工的 AI 角色,让它们自己聊着把活儿干了。

比如你可以安排一个"写手"负责起草文案,再安排一个"编辑"负责提修改意见,写手改完再让编辑看,来回几轮直到编辑满意为止。整个过程不需要你盯着,它们自己就能协商完成。

这不是什么遥远的概念,装两个包就能跑起来。

怎么装?怎么用?

环境要求很简单:Python 3.10 以上就行。打开终端敲一行命令:

pip install -U "autogen-agentchat" "autogen-ext[openai]"

装好之后,最基础的用法是创建一个带工具的 AI 助手。举个例子,做一个能查天气的智能体:

import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient

async def get_weather(city: str) -> str:
    return f"{city}今天 25°C,晴。"

model_client = OpenAIChatCompletionClient(model="gpt-4o")
agent = AssistantAgent(
    name="weather_agent",
    model_client=model_client,
    tools=[get_weather],
)

async def main():
    result = await agent.run(task="北京今天天气怎么样?")
    print(result.messages[-1].content)

asyncio.run(main())

你定义一个普通的 Python 函数,把它扔进 tools 参数里,AI 就自动知道什么时候该调用它。函数签名和注释会被自动转成工具描述,不需要你手动写 JSON schema。

真正有意思的部分:多个 AI 协作

单个智能体只是开胃菜。AutoGen 真正的看家本领是让多个智能体组队工作。

框架内置了几种编排模式,最常用的是 RoundRobinGroupChat——大家轮流发言。你可以设一个终止条件,比如当某个角色说出"通过"这个词就停下来:

from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination

team = RoundRobinGroupChat(
    [writer_agent, reviewer_agent],
    termination_condition=TextMentionTermination("通过"),
)
result = await team.run(task="写一段产品介绍文案")

除了轮流发言,还有 SelectorGroupChat,由一个 LLM 来判断下一个该谁说话,适合角色更多、分工更复杂的场景。还有 Swarm 模式,智能体之间可以主动"转交"任务,像客服系统的分级处理一样。

它的架构长什么样

AutoGen 分三层。最底下的 Core 层负责消息传递和运行时调度,属于基础设施。中间的 AgentChat 层是大多数人打交道的地方,预设智能体、团队编排、终止条件都在这一层。最外面的 Extensions 层负责对接各种外部服务,比如 OpenAI 的模型、Docker 代码执行器,以及通过 MCP 协议接入 Jira、Slack 等工具。

这种分层的好处是,你可以只用上层 API 快速出原型,也可以深入底层做精细控制。

还有个不用写代码的选项

如果你不想写代码,AutoGen 还提供了一个叫 AutoGen Studio 的可视化工具。装好之后一行命令启动:

pip install -U autogenstudio
autogenstudio ui --port 8080

打开浏览器就能拖拖拽拽搭建多智能体工作流,适合做快速验证和演示。

适合什么场景

说实话,不是所有任务都需要多智能体。一个简单的问答,一个 AI 就够了,上多智能体反而是大炮打蚊子。

但有些场景确实适合:需要反复打磨的内容创作、多角度的分析研判、有明确流程的任务处理(比如先分类再路由再执行),以及需要调用多种外部工具协同完成的复杂工作流。

核心判断标准就一个:如果你发现自己在不断地把一个 AI 的输出复制粘贴给另一个 AI 让它接着处理,那多半可以用 AutoGen 把这个链路自动化掉。


AutoGen 当前稳定版本为 v0.7.5,项目地址:github.com/microsoft/autogen

OpenClaw Memory 模块完整分析

作者 AngelPP
2026年2月25日 11:24

OpenClaw Memory 模块完整分析

一、项目背景

OpenClaw 是一个本地优先的个人 AI 助手,支持多种消息通道(WhatsApp、Telegram、Slack 等)。Memory 模块为 AI Agent 提供语义记忆搜索能力——Agent 可以在 Markdown 记忆文件和历史会话中进行向量 + 关键词的混合检索。

二、整体架构

┌─────────────────────────────────────────────────┐
│                  入口层 (index.ts)               │
│  getMemorySearchManager() → 选择后端策略          │
├────────────┬────────────────────────┬───────────┤
│  QMD 后端   │  FallbackManager      │ Builtin 后端│
│ (外部CLI)   │  (主备自动切换)        │ (核心实现)   │
├─────────────┴───────────────────────┴───────────┤
│               MemoryIndexManager                │
│   ┌──────────┬──────────┬──────────┐            │
│   │ SyncOps  │Embedding │ Search   │            │
│   │(文件监听) │ Ops(索引) │(混合搜索) │             │
│   └──────────┴──────────┴──────────┘            │
├─────────────────────────────────────────────────┤
│            存储层: SQLite + FTS5 + sqlite-vec    │
├─────────────────────────────────────────────────┤
│  Embedding Providers: OpenAI|Gemini|Voyage|     │
│  Mistral|Local(node-llama-cpp)                  │
└──────────────────────────────────────────────────┘

三、核心设计详解

1. 统一接口 (MemorySearchManager)

export type MemorySource = "memory" | "sessions";

export type MemorySearchResult = {
  path: string;
  startLine: number;
  endLine: number;
  score: number;
  snippet: string;
  source: MemorySource;
  citation?: string;
};
// ...
export interface MemorySearchManager {
  search(query, opts?): Promise<MemorySearchResult[]>;
  readFile(params): Promise<{ text: string; path: string }>;
  status(): MemoryProviderStatus;
  sync?(params?): Promise<void>;
  probeEmbeddingAvailability(): Promise<MemoryEmbeddingProbeResult>;
  probeVectorAvailability(): Promise<boolean>;
  close?(): Promise<void>;
}

这是整个模块的核心抽象——无论底层用什么后端(builtin SQLite 还是外部 QMD CLI),对上层暴露统一接口。

2. 后端策略选择 (search-manager.ts)

export async function getMemorySearchManager(params: {
  cfg: OpenClawConfig;
  agentId: string;
  purpose?: "default" | "status";
}): Promise<MemorySearchManagerResult> {
  const resolved = resolveMemoryBackendConfig(params);
  if (resolved.backend === "qmd" && resolved.qmd) {
    // ... 尝试 QMD 后端,失败则 fallback 到 builtin
    const wrapper = new FallbackMemoryManager({
      primary,
      fallbackFactory: async () => {
        const { MemoryIndexManager } = await import("./manager.js");
        return await MemoryIndexManager.get(params);
      },
    }, () => QMD_MANAGER_CACHE.delete(cacheKey));
    // ...
  }
  // 默认使用 builtin
  const manager = await MemoryIndexManager.get(params);
  return { manager };
}

设计亮点:

  • 策略模式 + 懒加载:通过 dynamic import 延迟加载后端实现
  • FallbackMemoryManager:代理模式,主后端失败自动切换到备用后端,对上层透明
  • 缓存驱逐:失败时自动从缓存中移除,下次请求可以重试

3. 混合搜索引擎 (hybrid.ts)

export async function mergeHybridResults(params: {
  vector: HybridVectorResult[];
  keyword: HybridKeywordResult[];
  vectorWeight: number;
  textWeight: number;
  mmr?: Partial<MMRConfig>;
  temporalDecay?: Partial<TemporalDecayConfig>;
}): Promise<Array<{ path; startLine; endLine; score; snippet; source }>> {
  // 1. 按 ID 合并向量和关键词结果
  // 2. 加权融合分数: score = vectorWeight * vectorScore + textWeight * textScore
  // 3. 时间衰减: 旧记忆分数降低
  // 4. MMR 重排序: 增加结果多样性
}

这是搜索的核心——三层融合管线:

阶段 算法 作用
加权融合 score = w_v × vectorScore + w_t × textScore 平衡语义相似度和关键词匹配
时间衰减 指数衰减 e^(-λ × age) 让近期记忆权重更高
MMR 重排序 λ × relevance - (1-λ) × max_similarity 增加结果多样性,避免重复

4. 时间衰减机制 (temporal-decay.ts)

export function toDecayLambda(halfLifeDays: number): number {
  return Math.LN2 / halfLifeDays;
}

export function calculateTemporalDecayMultiplier(params: {
  ageInDays: number;
  halfLifeDays: number;
}): number {
  const lambda = toDecayLambda(params.halfLifeDays);
  return Math.exp(-lambda * clampedAge);
}

核心设计:

  • 日期命名文件 memory/2024-01-15.md 自动从文件名提取时间
  • 常青文件 MEMORY.md 和非日期命名的 memory/*.md 不衰减(核心知识)
  • fallback 到 mtime:无法从文件名解析日期时,使用文件修改时间

5. MMR 多样性重排序 (mmr.ts)

export function computeMMRScore(relevance: number, maxSimilarity: number, lambda: number): number {
  return lambda * relevance - (1 - lambda) * maxSimilarity;
}

使用 Jaccard 相似度(基于 token 集合的交集/并集)来衡量结果间的相似程度,避免返回大量重复内容。比起用向量余弦相似度做 MMR,Jaccard 更轻量且无需额外嵌入计算。

6. Markdown 分块策略 (internal.ts)

export function chunkMarkdown(
  content: string,
  chunking: { tokens: number; overlap: number },
): MemoryChunk[] {
  const maxChars = Math.max(32, chunking.tokens * 4);
  const overlapChars = Math.max(0, chunking.overlap * 4);
  // 按行扫描,达到 maxChars 时 flush
  // flush 后保留尾部 overlapChars 作为重叠区
}

设计要点:

  • token 估算tokens × 4 转为字符数(粗略但高效)
  • 滑动窗口重叠:chunk 之间有 overlap,避免语义在边界处被截断
  • 超长行切割:单行超过 maxChars 时自动分段
  • 每个 chunk 记录 startLine/endLine,支持精确引用

7. Embedding Provider 工厂 (embeddings.ts)

export async function createEmbeddingProvider(
  options: EmbeddingProviderOptions,
): Promise<EmbeddingProviderResult> {
  // auto 模式: local → openai → gemini → voyage → mistral
  // 指定模式: primary → fallback
  // 所有 API key 缺失: 返回 null provider (FTS-only mode)
}

三层降级策略:

  1. auto 模式:依次尝试 local → openai → gemini → voyage → mistral
  2. 指定 + fallback:用户指定的 provider 失败时切换到 fallback
  3. 全部失败 → FTS-only:纯关键词搜索,仍可用但质量降低

8. 数据库 Schema (memory-schema.ts)

// meta: 索引元信息 (model/provider/版本)
// files: 文件记录 (path, hash, mtime, source)
// chunks: 文本块 (id, path, text, embedding, model)
// embedding_cache: 嵌入缓存 (provider+model+hash → embedding)
// chunks_fts: FTS5 全文搜索虚拟表
// chunks_vec: sqlite-vec 向量搜索虚拟表

9. 同步机制 (manager-sync-ops.ts)

同步有多种触发方式:

触发方式 场景
watch chokidar 文件监听,debounce 后触发
session-start 新会话开始时预热
session-delta 会话文件增长超过阈值(字节/消息数)
search 搜索时如果 dirty 则先同步
interval 定时同步(可配置分钟数)

重建索引采用安全替换策略:先写入临时 DB,完成后原子交换,失败则回滚。

10. 实例缓存 + 单例

const INDEX_CACHE = new Map<string, MemoryIndexManager>();
static async get(params): Promise<MemoryIndexManager | null> {
  const key = `${agentId}:${workspaceDir}:${JSON.stringify(settings)}`;
  const existing = INDEX_CACHE.get(key);
  if (existing) return existing;
  // ... 创建新实例
  INDEX_CACHE.set(key, manager);
  return manager;
}

agentId + workspaceDir + settings 作为缓存 key,保证同一配置只有一个 Manager 实例,避免重复打开数据库和文件监听器。

四、参考价值总结

如果你想在自己的项目中实现类似的记忆/知识检索系统,这个模块有以下核心参考价值:

维度 设计模式 参考价值
接口抽象 MemorySearchManager 接口 将搜索、同步、状态查询统一抽象,后端可替换
混合搜索 Vector + BM25 加权融合 兼顾语义理解和精确匹配,比单纯向量搜索更鲁棒
结果优化 MMR + 时间衰减 解决结果重复和旧信息权重过高的问题
降级策略 Provider 三层 fallback + FTS-only 无 API key 也能用,极大提升了可用性
主备切换 FallbackMemoryManager 代理模式 QMD 后端失败自动切到 builtin,对上层透明
增量同步 hash 对比 + 文件监听 + delta 阈值 只重新索引变化的文件,会话增量基于字节/消息数阈值
安全重建 临时 DB → 原子交换 → 失败回滚 全量重建索引时不影响在线查询
嵌入缓存 SQLite embedding_cache 表 避免重复调用 API,重建索引时可复用历史嵌入
分块策略 行级滑动窗口 + overlap 保留行号信息,支持精确引用,overlap 防止语义断裂
实例管理 缓存 Map + 复合 key 避免重复实例,正确处理 close 和缓存驱逐

这套架构特别适合以下场景复用:

  1. RAG 系统——需要在本地文档上做语义搜索
  2. 知识库检索——混合搜索 + 时间衰减适合持续更新的知识
  3. Agent 工具——作为 AI Agent 的长期记忆组件
  4. 离线优先应用——SQLite 本地存储 + 可选远程 Embedding 的架构

Speckit、OpenSpec、Superpowers 和 everything-claude-code AI辅助编程工具对比分析

作者 王小酱
2026年2月24日 13:35

1. 概述

随着AI编码能力(如 Claude Code、Cursor 等)的普及,软件开发领域正从“Vibe Coding”(随心灵感编码)向更工程化的方向演进。为了应对AI生成代码的不确定性、上下文丢失以及协作一致性等问题,社区涌现了多种规范驱动开发(Spec-Driven Development, SDD)框架和工作流方法论

选取了目前最具代表性的四个项目进行对比:

  • Speckit:GitHub官方出品的结构化规范驱动工具包

  • OpenSpec:专注于增量变更和棕地项目的轻量级框架

  • Superpowers:强调强制流程和TDD的代理技能框架

  • everything-claude-code:黑客松冠军开源的综合性 Claude Code 方法论

2. 核心属性速览

维度 Speckit OpenSpec Superpowers everything-claude-code
核心理念 规范即代码,通过严格的结构化流程实现工业级控制 增量即真相,通过Delta机制管理现有项目的演进 强制方法论,通过不可跳过的“技能”约束AI行为 CLI+Skills替代MCP,通过编排与并行化榨干模型性能
目标场景 绿地项目 (0→1) ,复杂且需要严格文档的团队协作 棕地项目 (1→n) ,在已有代码库上进行频繁修改和功能迭代 从0到1的需求探索,对代码质量、测试覆盖有极高要求的项目 重度Claude Code用户,追求极致Token效率和复杂任务并行处理的场景
工作流程 五阶段线性流程:Constitution → Specify → Plan → Tasks → Implement 四阶段循环流程:Draft Proposal → Review → Apply → Archive 三阶段严格流程:Brainstorm → Write Plan → Execute Plan (含TDD) 五阶段Agent编排:Research → Plan → Implement → Review → Verify
关键机制 9条不可变架构原则、7层LLM输出约束、ADR决策记录 Specs/与Changes/隔离、Delta增量存储、Fail-Fast冲突检测 强制技能调用、Red-Green-Refactor TDD、子代理审查 多Agent编排、并行化(Git Worktrees)、动态系统提示、记忆钩子

3. 详细维度对比

3.1 核心理念与哲学

  • Speckit:秉持“规范优先”的哲学。它假设需求在编码前可以被完全定义,通过类似于法律的“宪章”来约束AI的每一次产出。它将开发视为一个严谨的、逐层分解的工程过程,试图通过结构的确定性来对抗AI的随机性

  • OpenSpec:秉持“演进优先”的哲学。它承认需求是动态变化的,特别是在维护老项目时。其核心理念是将“当前稳定状态”(Specs)与“提议变更”(Changes)分离,每次只处理增量,最终将验证后的增量合并回主干,实现系统的平滑演进

  • Superpowers:秉持“流程即法律”的哲学。它不信任AI的自由发挥,通过一套不可跳过的“技能”(Skills)来强制AI遵循人类软件工程的最佳实践(如必须先写测试)。它像一名“教官”,强制AI按照TDD、Code Review等严谨流程行动

  • everything-claude-code:秉持“效率与编排”的哲学。它将AI视为一个可编排的智能体集群,通过精细化的工程手段(如用CLI替代MCP省Token、多实例并行)来最大化模型性能,降低成本,实现复杂的、多步骤的研发任务

3.2 目标用户与适用项目

  • Speckit:适合中大型团队,特别是需要严格合规、文档齐全的企业级项目。它明确了产品经理、技术负责人、开发者之间的交接点(如Spec、Plan),适合角色分工明确的团队

  • OpenSpec:适合全栈开发者或小型团队,尤其是在维护复杂老系统的团队。对于经常需要跨多个服务或模块进行小范围修改的场景,它的轻量级和高效增量特性极具吸引力

  • Superpowers:适合任何追求代码质量的开发者,尤其是从0到1启动项目时。它的头脑风暴模式对需求不明确的项目非常友好,强制TDD则确保了代码的健壮性

  • everything-claude-code:适合Claude Code的重度用户和技术极客。适合需要处理复杂、多步骤、多文件任务的场景,或是希望在API调用成本上精打细算的开发者

3.3 工作流与落地实践

  • Speckit:流程线性且严格

    • Constitution:定义开发原则和不可变规则

    • Specify:详细描述需求(What & Why)

    • Plan:基于技术栈制定架构方案

    • Tasks:分解为可执行的任务列表

    • Implement:逐一执行任务

      实践发现,若前期需求或设计有误,后期返工成本极高。在动态变化的企业环境中,这种线性流程常面临“理想很丰满,现实很骨感”的挑战

  • OpenSpec:流程循环且隔离

    • Proposal:在 changes/ 目录下创建变更提案、任务和Spec增量

    • Review:人与AI审查、对齐提案,可利用 openspec validate 进行冲突检测

    • Apply:AI严格根据 tasks.md 和增量Spec实施编码

    • Archive:将验证通过的变更合并(归档)到 specs/ 目录,更新“真相源”

      这种模式确保了主分支的Spec始终反映最新状态,且归档动作实现了知识的持续沉淀

  • Superpowers:流程强制且循环

    • Brainstorm:AI通过多轮问答帮助用户精炼需求,探索方案,输出设计文档

    • Write Plan:将设计拆解为极小的任务(2-5分钟),每个任务包含精确的文件路径、代码片段和测试命令

    • Execute Plan:通过子代理或批量模式执行计划,强制遵循 TDD(Red-Green-Refactor) ,并进行代码审查

      任何跳过步骤(如先写代码)的行为都会被AI视为违规

  • everything-claude-code:流程编排且并行

    • 编排:通过 Research AgentPlanner AgentTDD-guide AgentReviewer AgentResolver Agent 的有序协作完成任务。每个Agent输入输出清晰,中间用 /clear 清理上下文

    • 并行:利用 Git Worktrees 创建多个工作目录,同时运行多个Claude实例处理不同任务,互不干扰。采用 Two-Instance Kickoff(一个搭骨架,一个做调研)启动新项目

3.4 优缺点分析

工具/方法论 优点 缺点/挑战
Speckit 结构最严谨,文档最完善,适合大型项目治理;通过ADR记录决策,可追溯性强 。 太重、太理想化。对动态需求适应性差,流程僵化;生成文档冗长(相较OpenSpec多出数倍),上下文窗口易爆,返工成本高 。
OpenSpec 轻量、Token效率高。增量模式对老项目友好;archive机制能反向构建知识库;学习曲线平缓 。 对命名敏感,Delta机制依赖稳定的命名进行匹配;冲突解决依赖人工介入,对认知负担有一定挑战 ;不适合需要顶层宏观设计的0→1项目 。
Superpowers 质量保障最强。通过强制TDD和Code Review,产出代码可靠性高;头脑风暴功能极佳,能深度挖掘模糊需求 。 流程强制带来的“笨重感” 。即使是修个小Bug,也可能触发全套流程(TDD),对于追求快速验证的场景可能显得繁琐 。
everything-claude-code 极致的技术效率。Token优化策略显著降本;并行化和Agent编排极大提升复杂任务吞吐量;开源且模块化,扩展性强 。 上手门槛较高。需要理解其Agent编排、Hooks、Worktrees等整套哲学,对新手不够友好;部分技巧(如记忆钩子)需要手动配置 。

4. 选型建议

根据不同的团队类型和项目阶段,可以遵循以下建议进行选型

  • 如果你是维护复杂老系统的“单人开发者”或“小型团队”

    • 首选 OpenSpec。它的增量哲学能让你在不扰乱现有架构的前提下,安全地嵌入新功能。归档机制能帮你逐步梳理出混乱系统的“隐形文档”
  • 如果你正在启动一个全新的、复杂度较高的项目,且有明确的架构要求

    • 如果你是严谨派,追求工业级的代码质量:可以尝试 Speckit,但要做好前期投入大量时间编写规范的准备

    • 如果你是敏捷派,需求尚在演变中:强烈推荐 Superpowers。先利用其 Brainstorm 功能理清思路,再利用强制TDD构建稳固的核心,体验会非常流畅

  • 如果你是重度AI用户(特别是 Claude Code),追求极致的开发效率和成本控制

    • everything-claude-code 是你的不二之选。学习并采用它的CLI+Skills思想、Agent编排和并行化策略,你将能驾驭AI完成以往需要一个小团队才能完成的复杂任务
  • 如果你在大型团队中协作,需要明确的产品与开发交接流程

    • Speckit 的结构化文档(Constitution, Spec, Plan)可以作为团队协作的契约,明确各方职责,减少沟通误差。但需确保项目需求相对稳定,以避免因变更导致的巨大返工成本

5. 总结

AI编程正从“无序的 vibe coding”走向“有序的工程化”。这四种工具代表了不同的工程化路径

  • Speckit 走的是“计划经济的道路”,通过周密的计划来控制生产

  • OpenSpec 走的是“改革的道路”,在保持系统稳定的前提下,通过小步快跑实现演进

  • Superpowers 走的是“素质教育的道路”,通过严格的训练(流程)让AI养成良好的编码习惯

  • everything-claude-code 走的是“科技强军的道路”,通过先进的装备(编排、并行)和战术配合来发挥AI的最大战斗力

最终的选择没有绝对的对错,关键在于你的项目痛点、团队文化以及对AI协作的期望。希望这份报告能帮助你在这个快速发展的领域中找到最适合自己的方向

别让 AI 骗了:这些状态管理工具真的适合你吗?

作者 yuki_uix
2026年2月22日 10:16

某天,你让 Claude 帮你写个购物车功能,它给你生成了一套完整的 Redux。你看着满屏的 action、reducer、selector,心想:真的需要这么复杂吗?

AI 工具确实能快速生成状态管理代码,但它生成的方案,真的适合你的项目吗?这篇文章是我在 AI 辅助开发中,重新思考"状态管理选择"的过程。我想搞清楚:哪些工具是 AI 擅长的,哪些是我真正需要的。

从一个计数器开始:状态管理的起点

最简单的需求

让我们从最基础的开始。

// Environment: React
// Scenario: A simple counter

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

这里的"状态"是什么?

  • count 这个数字
  • 它会随着用户点击而变化
  • 只在这个组件内部使用

AI 友好度:⭐⭐⭐⭐⭐

为什么 AI 在这里表现完美?

  • useState 是最基础的模式,训练数据充足
  • 模式简单统一,不容易出错
  • 生成的代码几乎不需要修改

结论:如果状态只在单个组件内使用,useState 就够了,不需要其他工具。

需求升级:父子组件通信

当状态需要在多个组件间共享时,事情开始变复杂。

// Environment: React
// Scenario: State lifting to parent component

function Parent() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <Display count={count} />
      <Controls count={count} setCount={setCount} />
    </div>
  );
}

function Display({ count }) {
  return <h1>{count}</h1>;
}

function Controls({ count, setCount }) {
  return (
    <>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(count - 1)}>-1</button>
    </>
  );
}

思考点

  • 状态"提升"到父组件
  • 通过 props 传递给子组件
  • 这样做的问题是什么?

AI 友好度:⭐⭐⭐⭐

AI 能正确生成状态提升的代码,Props 传递逻辑清晰。但如果层级更深,AI 可能生成冗长的代码——它会"老实地"逐层传递,不会主动建议更好的方案。

第一层复杂度:Props Drilling 让人崩溃

问题场景:深层嵌套的组件树

想象一下存在这样的组件结构:

// Scenario: User info needed in multiple deeply nested components

<App>
  <Layout>
    <Header>
      <Navigation>
        <UserMenu />  {/* needs user info */}
      </Navigation>
    </Header>
    <Sidebar>
      <UserProfile />  {/* needs user info */}
    </Sidebar>
    <Main>
      <Content>
        <Article>
          <AuthorInfo />  {/* needs user info */}
        </Article>
      </Content>
    </Main>
  </Layout>
</App>

Props Drilling 的痛苦

// Environment: React
// Scenario: Props drilling problem

// Every layer must pass user prop
function App() {
  const [user, setUser] = useState(null);
  return <Layout user={user} />;
}

function Layout({ user }) {
  return (
    <>
      <Header user={user} />
      <Sidebar user={user} />
      <Main user={user} />
    </>
  );
}

function Header({ user }) {
  return <Navigation user={user} />;
}

function Navigation({ user }) {
  return <UserMenu user={user} />;
}

function UserMenu({ user }) {
  // Finally used here!
  return <div>{user.name}</div>;
}

问题分析

  • Layout、Header、Navigation 都不需要 user
  • 但为了传递给深层组件,它们都要接收这个 prop
  • 代码冗余,维护困难

AI 生成这种代码时的特点

  • ⚠️ AI 会"老实地"逐层传递 props
  • ⚠️ 不会主动建议使用 Context 或状态管理
  • ⚠️ 生成的代码"能用",但不优雅

解决方案1:Context API

// Environment: React
// Scenario: Use Context to avoid props drilling

// Create Context
const UserContext = createContext();

// Wrap root with Provider
function App() {
  const [user, setUser] = useState(null);
  
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Layout />
    </UserContext.Provider>
  );
}

// Deep component directly consumes
function UserMenu() {
  const { user } = useContext(UserContext);
  return <div>{user?.name}</div>;
}

// Middle components don't need to know about user
function Layout() {
  return (
    <>
      <Header />
      <Sidebar />
      <Main />
    </>
  );
}

Context 的优势

  • ✅ 解决了 Props Drilling
  • ✅ 中间组件不需要关心数据传递
  • ✅ React 原生 API,无需额外依赖

Context 的问题

// Environment: React
// Scenario: Performance issue with Context

function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');
  
  // ❌ Every time user or theme changes, all consumers re-render
  return (
    <UserContext.Provider value={{ user, setUser, theme, setTheme }}>
      {children}
    </UserContext.Provider>
  );
}

// Even if component only needs theme, it re-renders when user changes
function ThemeToggle() {
  const { theme, setTheme } = useContext(UserContext);
  // Re-renders when user changes!
}

AI 友好度:⭐⭐⭐

AI 生成 Context 代码的特点

  • ✅ AI 能正确生成 Context 的基本用法
  • ⚠️ AI 经常忽略性能优化(split context、useMemo)
  • ⚠️ AI 可能把所有状态都放在一个 Context 里
  • ❌ AI 生成的代码需要人工审查性能问题

我的经验是:让 AI 生成 Context 代码后,需要手动检查:

  • 是否需要拆分成多个 Context?
  • value 对象是否需要 useMemo?
  • 是否有不必要的重渲染?

Context 的适用场景

  • ✅ 数据变化不频繁(主题、语言、用户信息)
  • ✅ 只需要跨 2-3 层组件
  • ✅ 简单项目,不想引入额外依赖
  • ❌ 数据频繁变化(表单输入、动画)
  • ❌ 需要复杂的状态更新逻辑

第二层复杂度:状态更新逻辑变复杂

问题场景:购物车的复杂状态

// Environment: React
// Scenario: Shopping cart with complex operations

function Cart() {
  const [items, setItems] = useState([]);
  
  // Add item
  const addItem = (product) => {
    const existing = items.find(item => item.id === product.id);
    if (existing) {
      setItems(items.map(item =>
        item.id === product.id
          ? { ...item, quantity: item.quantity + 1 }
          : item
      ));
    } else {
      setItems([...items, { ...product, quantity: 1 }]);
    }
  };
  
  // Remove item
  const removeItem = (id) => {
    setItems(items.filter(item => item.id !== id));
  };
  
  // Update quantity
  const updateQuantity = (id, quantity) => {
    setItems(items.map(item =>
      item.id === id ? { ...item, quantity } : item
    ));
  };
  
  // Clear cart
  const clearCart = () => {
    setItems([]);
  };
  
  // Calculate total
  const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  
  // ... component render logic
}

问题分析

  • setState 逻辑散落在各个函数中
  • 每个函数都要处理不可变更新
  • 复杂的条件判断和数组操作
  • 难以追踪状态变化

解决方案2:useReducer

// Environment: React
// Scenario: Manage complex state with Reducer

// Define Action Types
const ACTIONS = {
  ADD_ITEM: 'ADD_ITEM',
  REMOVE_ITEM: 'REMOVE_ITEM',
  UPDATE_QUANTITY: 'UPDATE_QUANTITY',
  CLEAR_CART: 'CLEAR_CART'
};

// Reducer: Centralized state change logic
function cartReducer(state, action) {
  switch (action.type) {
    case ACTIONS.ADD_ITEM: {
      const existing = state.items.find(item => item.id === action.payload.id);
      if (existing) {
        return {
          ...state,
          items: state.items.map(item =>
            item.id === action.payload.id
              ? { ...item, quantity: item.quantity + 1 }
              : item
          )
        };
      }
      return {
        ...state,
        items: [...state.items, { ...action.payload, quantity: 1 }]
      };
    }
    
    case ACTIONS.REMOVE_ITEM:
      return {
        ...state,
        items: state.items.filter(item => item.id !== action.payload)
      };
    
    case ACTIONS.UPDATE_QUANTITY:
      return {
        ...state,
        items: state.items.map(item =>
          item.id === action.payload.id
            ? { ...item, quantity: action.payload.quantity }
            : item
        )
      };
    
    case ACTIONS.CLEAR_CART:
      return { ...state, items: [] };
    
    default:
      return state;
  }
}

// Use in component
function Cart() {
  const [state, dispatch] = useReducer(cartReducer, { items: [] });
  
  const addItem = (product) => {
    dispatch({ type: ACTIONS.ADD_ITEM, payload: product });
  };
  
  const removeItem = (id) => {
    dispatch({ type: ACTIONS.REMOVE_ITEM, payload: id });
  };
  
  // State update logic centralized in reducer
  // Component only dispatches actions
}

useReducer 的优势

  • ✅ 状态更新逻辑集中,易于维护
  • ✅ Action 类型明确,易于追踪
  • ✅ 测试友好(Reducer 是纯函数)
  • ✅ 适合复杂的状态转换

AI 友好度:⭐⭐⭐⭐

AI 能生成结构清晰的 Reducer,Switch-case 模式是 AI 熟悉的。但 AI 可能生成过于冗长的代码,Action types 和 actions 的组织方式可能不够优雅。

我的经验是:AI 生成的 Reducer 代码通常可用,但需要人工优化:

  • 提取重复的逻辑
  • 简化不可变更新(考虑 Immer)
  • 优化 Action 的组织方式

解决方案3:Zustand(AI 最爱)

// Environment: React + Zustand
// Scenario: More concise global state management

import { create } from 'zustand';

// Everything visible in one file
const useCartStore = create((set, get) => ({
  items: [],
  
  addItem: (product) => set((state) => {
    const existing = state.items.find(item => item.id === product.id);
    if (existing) {
      return {
        items: state.items.map(item =>
          item.id === product.id
            ? { ...item, quantity: item.quantity + 1 }
            : item
        )
      };
    }
    return {
      items: [...state.items, { ...product, quantity: 1 }]
    };
  }),
  
  removeItem: (id) => set((state) => ({
    items: state.items.filter(item => item.id !== id)
  })),
  
  updateQuantity: (id, quantity) => set((state) => ({
    items: state.items.map(item =>
      item.id === id ? { ...item, quantity } : item
    )
  })),
  
  clearCart: () => set({ items: [] }),
  
  // Derived state (auto-calculated)
  get total() {
    return get().items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  }
}));

// Use in component (very concise)
function Cart() {
  const { items, addItem, removeItem, total } = useCartStore();
  
  return (
    <div>
      {items.map(item => (
        <CartItem key={item.id} item={item} onRemove={removeItem} />
      ))}
      <p>Total: ${total}</p>
    </div>
  );
}

// Other components can easily access
function CartBadge() {
  const itemCount = useCartStore(state => state.items.length);
  return <span>{itemCount}</span>;
}

Zustand 的优势

  • ✅ 无需 Provider 包裹
  • ✅ 代码量少,一个文件搞定
  • ✅ 性能好(组件级别的精确订阅)
  • ✅ API 简单,学习成本低
  • ✅ TypeScript 支持好

与 useReducer 对比

特性 useReducer Zustand
样板代码 较多 很少
跨组件共享 需要 Context 原生支持
学习曲线 中等
DevTools 需要自己实现 内置支持

AI 友好度:⭐⭐⭐⭐⭐(最高)

为什么 AI 最爱 Zustand?

  • ✅ 单文件可见全貌,AI 容易理解上下文
  • ✅ 模式统一,生成代码质量高
  • ✅ 没有跨文件引用,不会遗漏关联
  • ✅ TypeScript 类型推断友好,AI 生成的类型也准确

我的实际体验

我:帮我用 Zustand 写个购物车状态管理
Claude:[生成完整、可用的代码]
我:几乎不需要修改,直接能用 ✅

我:帮我用 Redux 写个购物车
Claude:[生成 actions、reducers、types...]
我:需要检查各个文件的关联,修改不一致的地方 ⚠️

Zustand 的适用场景

  • ✅ 中小型项目
  • ✅ 需要全局状态,但不想写太多代码
  • ✅ 与 AI 协作开发(AI 生成质量高)
  • ✅ 团队成员 React 经验参差不齐
  • ⚠️ 超大型项目可能需要更严格的规范(考虑 Redux)

第三层复杂度:服务端数据的特殊性

问题场景:数据同步的困境

// Environment: React
// Scenario: Product list + product detail
// Problem: How to keep data consistent?

function ProductList() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    setLoading(true);
    fetchProducts()
      .then(setProducts)
      .finally(() => setLoading(false));
  }, []);
  
  // Problem 1: Data may be stale when returning from detail page
  // Problem 2: Other users modified product, I see old data
  // Problem 3: Same product may show different data in list vs detail
}

function ProductDetail({ id }) {
  const [product, setProduct] = useState(null);
  
  useEffect(() => {
    fetchProduct(id).then(setProduct);
  }, [id]);
  
  const updateProduct = async (data) => {
    await updateProductAPI(id, data);
    setProduct(data); // Update detail page
    // Problem: What about the list page data?
  };
}

传统方案的问题

  • 数据缓存:什么时候重新请求?
  • 数据同步:多个组件如何共享同一份数据?
  • 加载状态:每个组件都要写 loading/error 逻辑
  • 数据过期:如何判断数据需要刷新?

解决方案4:React Query

// Environment: React + React Query
// Scenario: Elegantly manage server state

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// List page
function ProductList() {
  const { data: products, isLoading, error } = useQuery({
    queryKey: ['products'],
    queryFn: fetchProducts,
    staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
  });
  
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

// Detail page
function ProductDetail({ id }) {
  const queryClient = useQueryClient();
  
  const { data: product } = useQuery({
    queryKey: ['product', id],
    queryFn: () => fetchProduct(id),
  });
  
  const updateMutation = useMutation({
    mutationFn: (data) => updateProductAPI(id, data),
    onSuccess: (updatedProduct) => {
      // Update detail cache
      queryClient.setQueryData(['product', id], updatedProduct);
      
      // Invalidate list, trigger refetch
      queryClient.invalidateQueries(['products']);
      
      // Data auto synced!
    },
  });
  
  return (
    <div>
      <h1>{product.name}</h1>
      <button onClick={() => updateMutation.mutate(newData)}>
        Update
      </button>
    </div>
  );
}

React Query 的优势

  • ✅ 自动管理缓存
  • ✅ 自动重新获取(窗口获得焦点时、网络恢复时)
  • ✅ 自动去重(多个组件请求同一数据时只发一次请求)
  • ✅ 乐观更新、失败回滚
  • ✅ 分页、无限滚动支持
  • ✅ 内置 loading/error 状态

与 Zustand 的分工

状态类型 工具选择 示例
客户端状态 Zustand/Context Modal switch, theme, form draft
服务端状态 React Query User info, product list, order data

重要的认知转变

  • React Query 不是"状态管理库"
  • 它是"服务端状态同步工具"
  • 服务端数据有特殊的生命周期(获取、缓存、失效、重新获取)

AI 友好度:⭐⭐⭐⭐

AI 生成 React Query 代码的特点

  • ✅ AI 能生成标准的 useQuery/useMutation 代码
  • ✅ 常见模式(loading、error、success)AI 很熟悉
  • ⚠️ 复杂的缓存策略 AI 可能生成不当
  • ⚠️ optimistic updates 的逻辑 AI 容易出错

我的经验是:

  • 让 AI 生成基础的 useQuery 代码:质量很高 ✅
  • 涉及复杂的 cache invalidation:需要人工审查 ⚠️
  • Mutation 的 onSuccess/onError 逻辑:AI 可能不够完善 ⚠️

SWR vs React Query

// Environment: React + SWR
// Scenario: SWR syntax (more concise)

import useSWR from 'swr';

function ProductList() {
  const { data, error } = useSWR('/api/products', fetcher);
  // Simpler, but slightly less powerful
}

对比

特性 React Query SWR
功能完整度 更强大 够用
API 复杂度 稍复杂 更简洁
社区规模 更大 较小
AI 生成质量 ⭐⭐⭐⭐ ⭐⭐⭐⭐

AI 对两者的支持

  • 两者都是声明式 API,AI 都能生成好
  • SWR 更简单,AI 生成的代码更"干净"
  • React Query 功能更强,但 AI 可能用不到高级特性

第四层复杂度:Redux 真的需要吗?

Redux 的定位

// Environment: React + Redux Toolkit
// Scenario: Modern Redux (already much simpler)

import { createSlice, configureStore } from '@reduxjs/toolkit';

// Slice: combines actions and reducer
const cartSlice = createSlice({
  name: 'cart',
  initialState: { items: [] },
  reducers: {
    addItem: (state, action) => {
      // Redux Toolkit supports "mutable" syntax (uses Immer internally)
      const existing = state.items.find(item => item.id === action.payload.id);
      if (existing) {
        existing.quantity += 1;
      } else {
        state.items.push({ ...action.payload, quantity: 1 });
      }
    },
    removeItem: (state, action) => {
      state.items = state.items.filter(item => item.id !== action.payload);
    },
  },
});

// Store
const store = configureStore({
  reducer: {
    cart: cartSlice.reducer,
  },
});

// Use in component
function Cart() {
  const items = useSelector(state => state.cart.items);
  const dispatch = useDispatch();
  
  return (
    <button onClick={() => dispatch(cartSlice.actions.addItem(product))}>
      Add to Cart
    </button>
  );
}

Redux 的优势

  • ✅ 强大的 DevTools(时间旅行调试)
  • ✅ 严格的状态管理规范(适合大团队)
  • ✅ 中间件生态丰富(redux-saga、redux-thunk)
  • ✅ 社区最大,资源最多

Redux 的问题

  • ❌ 即使用了 Toolkit,代码量仍然多
  • ❌ 学习曲线陡峭
  • ❌ 简单功能也需要完整的流程

AI 友好度:⭐⭐⭐(中等)

AI 生成 Redux 代码的特点

  • ✅ Redux Toolkit 的 createSlice AI 能正确生成
  • ⚠️ 但跨文件的关联(types、actions、selectors)容易出问题
  • ⚠️ 中间件、异步 action 的逻辑 AI 容易生成过时的写法
  • ❌ 大型项目的文件组织 AI 可能不够合理

我的实际体验

我:用 Redux Toolkit 写个购物车
Claude:[生成 slice、store 配置...]
我:代码能用,但需要检查:
    - 是否遵循了项目的文件组织规范?
    - Selector 是否需要用 reselect 优化?
    - 异步逻辑是否应该用 createAsyncThunk?

何时真正需要 Redux?

我的思考(不一定准确):

✅ 适合 Redux 的场景

  • 超大型项目(100+ 组件,10+ 开发者)
  • 需要严格的代码规范和审查
  • 需要时间旅行调试
  • 复杂的状态依赖关系
  • 需要中间件(日志、埋点、权限控制)

❌ 不需要 Redux 的场景

  • 中小型项目(Zustand 够用)
  • 快速迭代(Redux 太重)
  • 团队 React 经验不足(学习成本高)
  • 主要是服务端数据(React Query 更合适)

一个判断标准

如果你不确定是否需要 Redux,那你可能不需要它。 — Dan Abramov(Redux 作者)

AI 协作的建议

  • 与 AI 协作时,Zustand 的开发效率更高
  • Redux 需要更多人工审查和调整
  • 除非项目确实需要 Redux 的严格性,否则优先 Zustand

决策树:如何选择状态管理方案

完整的决策流程

graph TD
    A[需要管理状态?] --> B{状态类型?}
    
    B --> |服务端数据| C[React Query / SWR]
    
    B --> |客户端状态| D{使用范围?}
    
    D --> |单个组件| E[useState / useReducer]
    
    D --> |多个组件| F{层级关系?}
    
    F --> |父子2层内| G[Props传递]
    
    F --> |跨3层以上| H{项目规模?}
    
    H --> |小型| I{数据变化频率?}
    I --> |低| J[Context]
    I --> |高| K[Zustand]
    
    H --> |中型| K[Zustand]
    
    H --> |大型| L{团队规模?}
    L --> |小于5人| K
    L --> |大于5人| M[Redux]
    
    style C fill:#e1f5dd
    style E fill:#e1f5dd
    style G fill:#e1f5dd
    style J fill:#fff4cc
    style K fill:#d4edff
    style M fill:#ffe0e0

方案对比表

方案 学习成本 代码量 性能 AI友好度 适用场景
useState 最少 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 单组件状态
Context ⭐⭐ ⭐⭐⭐ ⭐⭐⭐ 跨层级、低频变化
useReducer ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ 复杂状态逻辑
Zustand ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 全局状态(推荐)
React Query ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ 服务端数据(必选)
Redux ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ 大型项目、严格规范

我的推荐组合

小型项目(个人项目、demo):

useState + Context + React Query

中型项目(几人小团队):

Zustand (client state) + React Query (server data)

大型项目(跨团队协作):

Redux (complex logic) + React Query (server data)

AI 协作优先

Zustand (most efficient) + React Query

延伸与发散:AI 时代的状态管理思考

AI 生成代码的特点总结

通过前面的分析,我发现 AI 在状态管理方面有明显的倾向:

AI 擅长的

  • ✅ 模式统一的代码(Zustand、React Query)
  • ✅ 单文件可见全貌(不需要跨文件理解)
  • ✅ 声明式 API(useQuery、useState)
  • ✅ 结构清晰的 Reducer

AI 不擅长的

  • ❌ 跨文件的依赖关系(Redux 的 actions/reducers 分离)
  • ❌ 性能优化细节(Context 的 split、memo)
  • ❌ 复杂的缓存策略
  • ❌ 架构级别的决策(该用哪个工具)

AI 会"骗"你什么?

问题1:AI 可能推荐过于复杂的方案

你:帮我做个 todo list
AI:[生成完整的 Redux 方案]
实际:useState 就够了

为什么?

  • AI 的训练数据中,Redux 的示例很多
  • AI 倾向生成"完整"的解决方案
  • 但不一定考虑你的项目规模

问题2:AI 可能忽略性能问题

// Environment: React + Context
// Scenario: AI generated Context code

const AppContext = createContext();

function AppProvider({ children }) {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');
  const [cart, setCart] = useState([]);
  
  // ❌ AI may not tell you: this causes all consumers to re-render
  return (
    <AppContext.Provider value={{ user, theme, cart, setUser, setTheme, setCart }}>
      {children}
    </AppContext.Provider>
  );
}

应该做的

// Split into multiple Contexts
const UserContext = createContext();
const ThemeContext = createContext();
const CartContext = createContext();

问题3:AI 可能生成过时的写法

// AI may generate old Redux pattern
const ADD_TODO = 'ADD_TODO';

function addTodo(text) {
  return { type: ADD_TODO, text };
}

function todoReducer(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [...state, { text: action.text }];
    default:
      return state;
  }
}

// Actually Redux Toolkit's createSlice is more concise

如何与 AI 更好地协作

策略1:明确告诉 AI 项目规模

❌ Not good: Help me with state management
✅ Better: I'm building a medium-sized project (20 components), 
          need to manage user info and cart, use Zustand

策略2:要求 AI 说明选择理由

你:为什么选择 Redux 而不是 Zustand?
AI:因为你提到了需要时间旅行调试和中间件...
你:哦我不需要这些,那用 Zustand 吧

策略3:分步骤验证

  1. 让 AI 生成基础代码

  2. 自行检查性能和安全性

  3. 让 AI 优化特定部分(而非完全重写)

策略4:建立自己的代码模板

  1. 将已验证的优秀代码保存为模板

  2. 下次让 AI “基于此模板生成代码”

  3. AI 将模仿你的模板,而不是使用其默认模式

未来的思考

问题:AI 时代,状态管理会如何演进?

我的一些猜想(不一定对):

  1. 更简洁的 API

    • AI 友好的工具会越来越流行(Zustand、Jotai)
    • 复杂的样板代码工具可能被淘汰
  2. 智能化的状态管理

    • AI 能否自动判断何时需要状态管理?
    • AI 能否自动优化性能问题?
  3. 本地优先(Local-first)架构

    • 离线优先的应用越来越多
    • 状态同步会变得更复杂
    • 需要新的工具和模式
  4. AI 原生的状态设计

    • 如果从一开始就考虑 AI 协作
    • 状态管理工具会如何设计?

待探索的问题

  • Signals(SolidJS)会成为主流吗?
  • 服务端组件(RSC)如何改变状态管理?
  • AI Agent 执行多步骤任务的状态如何设计?

小结

这篇文章更多是我在 AI 协作开发中的思考和实践。

核心收获

  • 状态管理不是"选库",而是"理解需求 → 选择合适方案"
  • AI 擅长生成简洁、统一的代码(Zustand、React Query)
  • AI 不擅长架构决策和性能优化
  • 与 AI 协作时,人类需要把控方向,AI 负责执行

实用建议

  • 优先选择 AI 友好的工具(Zustand + React Query)
  • 明确告诉 AI 项目规模和具体需求
  • 审查 AI 生成的代码(尤其是性能和架构)
  • 建立自己的代码模板,让 AI 模仿

开放性问题

  • 你在 AI 协作开发中遇到过哪些坑?
  • AI 生成的状态管理代码,你会直接用还是会修改?
  • 如果让你设计一个"AI 友好"的状态管理库,你会怎么做?

参考资料

2026 技术风向:为什么在 AI 时代,PostgreSQL 彻底成为了全栈工程师的首选数据库

作者 NEXT06
2026年2月16日 15:12

在 Web 开发的黄金十年里,LAMP 架构(Linux, Apache, MySQL, PHP)奠定了 MySQL 不可撼动的霸主地位。那是互联网的草莽时代,业务逻辑相对简单,读多写少,开发者对数据库的诉求仅仅是“稳定存储”。

然而,时间来到 2026 年。随着 Node.js 与 TypeScript 生态的统治级渗透,以 Next.js、NestJS 为代表的现代全栈框架(Modern Stack)彻底改变了应用开发的范式。在这个由 Serverless、Edge Computing 和 AI 驱动的新时代,MySQL 逐渐显得力不从心。与此同时,PostgreSQL(下文简称 PG)凭借其惊人的演进速度,成为了全栈工程师事实上的“默认选项”。

这不仅仅是技术偏好的转移,更是架构复杂性倒逼下的必然选择。

建筑学的视角:预制板房 vs 模块化摩天大楼

要理解为什么 PG 在现代架构中胜出,我们必须从底层设计哲学说起。如果把数据库比作建筑:

MySQL 像是一栋“预制板搭建的经济适用房”。
它结构紧凑,开箱即用,对于标准的居住需求(基础 CRUD、简单事务)来说,它表现优异且成本低廉。但是,它的结构是固化的。如果你想在顶楼加建一个停机坪(向量搜索),或者把承重墙打通做成开放式空间(非结构化数据存储),你会发现极其困难。它的存储引擎(InnoDB)虽然优秀,但与上层逻辑耦合较紧,扩展性受限。

PostgreSQL 像是一座“钢结构模块化摩天大楼”。
它的底座(存储与事务引擎)极其坚固,严格遵循 SQL 标准与 ACID 原则。但它最核心的竞争力在于其可插拔的模块化设计(Extensibility)

  • 你需要处理地理空间数据?插入 PostGIS 模块,它立刻变成专业的 GIS 数据库。
  • 你需要做高频时序分析?插入 TimescaleDB 模块。
  • 你需要 AI 向量搜索?插入 pgvector 模块。

PG 不仅仅是一个数据库,它是一个数据平台内核。这种“无限生长”的能力,完美契合了 2026 年复杂多变的业务需求。

全栈工程师偏爱 PG 的三大理由

在 Next.js/NestJS 的全栈生态中,Prisma 和 Drizzle ORM 的流行进一步抹平了数据库的方言差异,让开发者更能关注数据库的功能特性。以下是 PG 胜出的三个关键维度。

1. JSONB:终结 NoSQL 的伪需求

在电商系统中,我们经常面临一个棘手的问题:商品(SKU)属性的非结构化。

  • 衣服:颜色、尺码、材质。
  • 手机:屏幕分辨率、CPU型号、内存大小。
  • 图书:作者、ISBN、出版社。

在 MySQL 时代,为了处理这些动态字段,开发者通常有两种痛苦的选择:要么设计极其复杂的 EAV(实体-属性-值)模型,要么引入 MongoDB 专门存储商品详情,导致需要维护两个数据库,并在应用层处理数据同步(Distributed Transaction 问题)。

MySQL 虽然支持 JSON 类型,但在索引机制和查询性能上一直存在短板。

PG 的解法是 JSONB(Binary JSON)。
PG 不仅仅是将 JSON 作为文本存储,而是在写入时将其解析为二进制格式。这意味着:

  1. 解析速度极快:读取时无需重新解析。
  2. 强大的索引支持:你可以利用 GIN(Generalized Inverted Index,通用倒排索引)对 JSON 内部的任意字段建立索引。

场景示例:
不需要引入 MongoDB,你可以直接在 PG 中查询:“查找所有红色且内存大于 8GB 的手机”。

SQL

-- 利用 @> 操作符利用 GIN 索引进行极速查询
SELECT * FROM products 
WHERE attributes @> '{"color": "red"}' 
AND (attributes->>'ram')::int > 8;

对于全栈工程师而言,这意味着架构的极度简化:One Database, All Data Types.

2. pgvector:AI 时代的“降维打击”

AI 应用的爆发,特别是 RAG(检索增强生成)技术的普及,催生了向量数据库(Vector Database)的需求。

传统的 AI 架构通常是割裂的:

  • MySQL:存储用户、订单等元数据。
  • Pinecone/Milvus:存储向量数据(Embeddings)。
  • Redis:做缓存。

这种架构对全栈团队简直是噩梦。你需要维护三套基础设施,处理数据一致性,还要编写复杂的胶水代码来聚合查询结果。

PG 的解法是 pgvector 插件。
通过安装这个插件,PG 瞬间具备了存储高维向量和进行相似度搜索(Cosine Similarity, L2 Distance)的能力。更重要的是,它支持 HNSW(Hierarchical Navigable Small World)索引,查询性能足以应对绝大多数生产场景。

实战场景:AI 电商系统的“以图搜图”
用户上传一张图片,系统需要推荐相似商品,但同时必须满足“价格低于 1000 元”且“有库存”的硬性条件。

在 PG 中,这只是一个 SQL 查询

SQL

SELECT id, name, price, attributes
FROM products
WHERE stock > 0                       -- 关系型过滤
  AND price < 1000                    -- 关系型过滤
ORDER BY embedding <=> $1             -- 向量相似度排序($1 为用户上传图片的向量)
LIMIT 10;

这种混合查询(Hybrid Search)能力是 PG 对专用向量数据库的降维打击。它消除了数据搬运的成本,保证了事务的一致性(你肯定不希望搜出来的商品其实已经下架了)。

3. 生态与插件:长期主义的选择

MySQL 的功能迭代主要依赖于 Oracle 官方的发版节奏。而 PG 的插件机制允许社区在不修改核心代码的前提下扩展数据库功能。

在 Node.js 全栈项目中,我们经常会用到:

  • pg_cron:直接在数据库层面运行定时任务,无需在 NestJS 里写 cron job。
  • PostGIS:处理配送范围、地理围栏,这是目前地球上最强大的开源 GIS 引擎。
  • zombodb:将 Elasticsearch 的搜索能力集成到 PG 索引中。

对于全栈工程师来说,PG 就像是一个拥有海量 npm 包的运行时环境,你总能找到解决特定问题的插件。

实战架构图谱:构建 Next-Gen AI 电商

基于上述分析,一个典型的 2026 年现代化全栈电商系统的后端架构可以被压缩得极其精简。我们不再需要“全家桶”式的中间件,一个 PostgreSQL 集群足矣。

架构设计

  • 技术栈:Next.js (App Router) + Prisma ORM + PostgreSQL.
  • 数据模型设计

TypeScript

// Prisma Schema 示例
model Product {
  id          Int      @id @default(autoincrement())
  name        String
  price       Decimal
  stock       Int
  // 核心特性 1: 结构化数据与非结构化数据同表
  attributes  Json     // 存储颜色、尺码等动态属性
  
  // 核心特性 2: 原生向量支持 (通过 Prisma Unsupported 类型)
  embedding   Unsupported("vector(1536)") 
  
  // 核心特性 3: 强一致性关系
  orders      OrderItem[]
  
  @@index([attributes(ops: JsonbPathOps)], type: Gin) // GIN 索引加速 JSON 查询
  @@index([embedding], type: Hnsw) // HNSW 索引加速向量搜索
}

业务流转

  1. 商品录入:结构化字段存入 Column,非结构化规格存入 attributes (JSONB),同时调用 OpenAI API 生成 Embedding 存入 embedding 字段。
  2. 交易环节:利用 PG 成熟的 MVCC(多版本并发控制)和 ACID 事务处理高并发订单写入,无需担心锁竞争(相比 MySQL 的 Gap Lock,PG 在高并发写入下往往表现更优)。
  3. 搜索推荐:利用 pgvector 实现基于语义或图片的推荐,同时结合 attributes 中的 JSON 字段进行精准过滤。

结论:Simplicity is Scalability(简单即是扩展)。少维护一个 MongoDB 和一个 Pinecone,意味着系统故障点减少了 66%,开发效率提升了 100%。

结语:数据库的终局

在 2026 年的今天,我们讨论 PostgreSQL 时,已经不再仅仅是在讨论一个关系型数据库(RDBMS)。

PostgreSQL 已经演变成了一个通用多模态数据平台(General-Purpose Multi-Model Data Platform) 。它既是关系型数据库,也是文档数据库,更是向量数据库和时序数据库。

对于追求效率与掌控力的全栈工程师而言,MySQL 依然是 Web 1.0/2.0 时代的丰碑,但在构建 AI 驱动的复杂应用时,PostgreSQL 提供了更广阔的自由度和更坚实的底层支撑。

拥抱 PostgreSQL,不仅是选择了一个数据库,更是选择了一种“做减法”的架构哲学。

我写了个 code-review 的 Agent Skill, 没想到火了

作者 神三元
2026年2月16日 14:35

前两天随手写了个 Claude Code 的 Skill,专门做 Code Review 的,发了条推之后就没太在意。

结果第二天醒来一看,GitHub Star 刷刷往上涨,评论区也炸了,不少人说"终于有个靠谱的 Code Review 工具了"。

image.png

说实话,有点意外。

倒不是说这个 Skill 有多了不起,而是它戳中了一个很真实的痛点——大部分团队的 Code Review,要么走过场,要么全靠人肉。

先说说为什么要做这个

做这个 Skill 的起因其实很简单。

我自己平时写代码,改完了之后经常想让 Claude Code 帮我 review 一下。直接跟它说"帮我看看代码有没有问题",它确实会给你一些反馈,但说实话,质量参差不齐。

这就跟新来的实习生做 Code Review 一样,不是他不想认真看,是他不知道该看什么、怎么看、按什么优先级来。

所以问题的本质是:模型需要一套结构化的 Review 框架,告诉它该检查什么、怎么分级、用什么格式输出。

这不就是 Skill 最擅长干的事吗?

code-review-expert 是什么

一句话概括:一个让 AI 用资深工程师的视角帮你做 Code Review 的 Skill。

安装方式就一行:

npx skills add sanyuan0704/code-review-expert

装好之后在 Claude Code 里输入 /code-review-expert,它就会自动 review 你当前的 git changes。

整个 review 流程我是精心设计过的,分成这么几步:

第一步:Preflight(了解改动范围)

它会先跑 git diff 看看你改了哪些文件、改了多少行。如果改动量超过 500 行,它会先按模块分批 review,不会一口气全看完然后给你一堆乱七八糟的反馈。

第二步:SOLID + 架构检查

这一步是我花了最多时间打磨的。我写了一份详细的 SOLID checklist,把每个原则对应的"坏味道"都列出来了。

比如检查 SRP(单一职责),它不会只是泛泛地说"这个文件职责太多了",而是会问一个很具体的问题:"这个模块有几个不同的修改理由?" 如果一个文件既管 HTTP 请求,又管数据库操作,还管业务逻辑,那它大概率违反了 SRP。

第三步:发现可以删掉的代码

这步其实挺有意思的。很多项目里都有一堆死代码——feature flag 关掉的、被废弃的 API、没人用的工具函数。它会帮你找出来,并且区分"可以直接删"和"需要制定计划再删"两种情况。

第四步:安全扫描

XSS、SQL 注入、SSRF、路径穿越、竞态条件、密钥泄露……这些它都会检查。

其中竞态条件(Race Condition)这块我写的特别详细,因为这是很多人在 review 时最容易忽略的。它会专门去找 check-then-act 模式、读-改-写操作、并发数据库访问这些容易出问题的场景。

第五步:代码质量扫描

错误处理有没有吞掉异常?有没有数据库的 N+1 查询?空值检查到不到位?这些"小问题"在生产环境里都可能变成大事故或者性能问题。

最后:结构化输出 + 确认

所有发现按严重程度分成四个等级:

等级 含义 怎么处理
P0 严重 必须 block merge
P1 高危 应该在合并前修复
P2 中等 这个 PR 修或者建个 follow-up
P3 低优 可选优化

输出之后,它不会自作主张去改代码。而是先问你:要修全部,还是只修 P0/P1,或者修指定的。

这个"先 review 再确认"的设计是我特意做的——Code Review 的价值不只是发现问题,更重要的是让你理解问题。如果 AI 直接帮你改了,你连有什么问题都不知道,那这个 review 就没意义了。

为什么我觉得它火了

发完推之后,仓库几天内涨到了 460+ Star,40+ Fork。

评论区和私信里,大家反馈最多的是两点:

第一,"终于有个体系化的 Review 方案了"

很多独立开发者和小团队,根本没有 Code Review 的流程。不是不想做,是没人帮你 review。有了这个 Skill,相当于随时有个资深工程师帮你把关。

这个需求其实比我想象的要大。我之前以为 Code Review 主要是大厂的需求,没想到独立开发者和小团队对这块的渴求更强烈——因为他们更没有犯错的资本。

第二,"终于不是 AI 味十足的泛泛建议了"

image.png

这要归功于那几份 checklist。我把 security-checklist、solid-checklist、code-quality-checklist 都放在了 references/ 目录下,每份都是实打实的检查清单,不是那种"注意安全问题"之类的废话。

比如安全检查那份,光竞态条件就列了四个子类:共享状态访问、TOCTOU(检查后使用)、数据库并发、分布式系统。每个子类下面都有具体的代码模式和需要问的问题。

这就是 Skill 的魅力——你把专业知识结构化地喂给模型,它的输出质量会有质的提升。

怎么做到的?聊聊 Skill 的设计思路

这个 Skill 的结构很简单:

code-review-expert/
├── SKILL.md                  # 主文件,定义整个 review 流程
├── agents/
│   └── agent.yaml            # Agent 配置
└── references/
    ├── solid-checklist.md    # SOLID 原则检查清单
    ├── security-checklist.md # 安全检查清单
    ├── code-quality-checklist.md # 代码质量检查清单
    └── removal-plan.md       # 代码清理计划模板

核心设计有几个关键点:

1. references 实现按需加载

这是 Skill 体系最优雅的地方。

四份 checklist 的内容加起来好几千字,如果全塞进 SKILL.md,一上来就会吃掉大量上下文窗口。所以我把它们放在 references/ 里,SKILL.md 里只在需要的步骤写 Load references/xxx.md

模型执行到那个步骤时才会去读对应的文件,用完就可以"忘掉"了。这就是之前文章里讲过的 Progressive Disclosure(渐进式加载),Skills 最精妙的设计之一。

2. Workflow 要设计得有节奏感

我试过把所有检查点平铺在一起,效果很差——模型会东一榔头西一棒子,安全问题和命名规范混在一起说。

最后我按照真实的 Code Review 流程来编排:先看改动范围,再看架构设计,然后看安全,最后看代码质量。每一步之间是递进关系,从宏观到微观。

这个设计借鉴了人来做 Code Review 的习惯——好的 reviewer 不会上来就抠细节,而是先理解整体改动的意图和影响范围。

写在最后

你猜我写这个 skill 花了多久?

3,2,1,揭晓答案。

我只花了 10 分钟。不可思议吧。

怎么做到的?现在 claude 官方有一个叫 skill-creator 的 skill,帮你来写 skill,然后基于它可以很快搭出骨架来。后续,就是基于我的专业经验,引导 agent 帮我把一些关键的原则拆分为各个 checklist 文档,聊个几轮,这个高质量的 skill 就完工了。

回头看这件事,我觉得这也是 Skills 生态最让人兴奋的地方:每个有专业积累的开发者,都可以很快把自己的经验沉淀成一个 Skill,让 AI 帮更多人受益。

你不需要会写 MCP Server,不需要懂协议,不需要搞 OAuth 鉴权。就是一个 Markdown 文件 + 几份参考文档,仅此而已。

仓库在这里,欢迎 Star 和提 PR:

GitHub: sanyuan0704/code-review-expert

安装:npx skills add sanyuan0704/code-review-expert

如果你也在做 Skill 开发,或者有什么好用的 Skill 推荐,评论区欢迎来聊。

AI Agent开发之向量检索:一篇讲清「稀疏 + 稠密 + Hybrid Search」怎么落地

2026年2月15日 20:02

AI Agent开发之向量检索:一篇讲清「稀疏 + 稠密 + Hybrid Search」怎么落地

核心结论

在 AI 搜索和知识库场景中,混合检索(Hybrid Search)是当前最优解:

  • 稠密向量(Dense):擅长处理语义相似的查询,能够理解同义词、口语化表达
  • 稀疏向量(Sparse):擅长精确匹配关键词,如产品名称、接口名、错误码等专有术语
  • 混合检索(Hybrid):通过 RRF(Reciprocal Rank Fusion)算法融合两者优势,在生产环境中表现最稳定

单独使用稠密向量会导致专有名词召回不准确,而仅使用稀疏向量则无法理解语义相近的不同表述。混合检索能够同时规避这两个问题。

应用场景与痛点分析

典型应用场景

混合检索方案特别适用于以下前端场景:

  • 站内搜索:用户使用自然语言或关键词检索站内内容
  • 帮助中心问答:智能匹配用户问题与知识库文档
  • 聊天助手上下文召回:为 AI 助手提供相关上下文信息

单一检索方案的局限性

仅使用稠密向量检索时的问题:

  • 专有名词召回不稳定:如 "ERR_CONNECTION_RESET" 等错误码可能无法准确匹配
  • 短查询偏移:当用户输入 2~6 个词的短查询时,容易产生语义偏移

仅使用 BM25/关键词检索时的问题:

  • 语义理解缺失:"登录失败" 和 "无法完成认证" 虽然语义相近,但因关键词不同导致召回效果差

技术架构

flowchart LR
  A["文档内容"] --> B["生成稠密向量<br/>Embedding"]
  A --> C["生成稀疏向量<br/>分词+词频"]
  B --> D["写入向量库(dense)"]
  C --> E["写入向量库(sparse)"]

  Q["用户Query"] --> Q1["Query Embedding"]
  Q --> Q2["Query Sparse Vector"]

  Q1 --> S1["Dense Search"]
  Q2 --> S2["Sparse Search"]
  S1 --> F["RRF 融合排序"]
  S2 --> F
  F --> R["TopK 返回前端"]

稀疏向量与稠密向量的本质区别

稀疏向量(Sparse)

  • 来源:分词 + 词频(TF),可叠加 IDF/BM25
  • 特征:高维稀疏(大部分是 0)
  • 长处:关键词强匹配、可解释

示例文本:我是好学生,每天8点起床

分词后:

["我", "是", "好", "学生", "每天", "8", "点", "起床"]

稀疏结构(示意):

{
  indices: [102, 1552, 30091],
  values: [1, 1, 1]
}

稠密向量(Dense)

  • 来源:Embedding 模型(如 text-embedding-3-small
  • 特征:低维连续浮点向量
  • 长处:语义理解强(能懂同义改写)

核心实现

以下代码采用通用写法,不依赖特定项目结构,可直接迁移到任意 TypeScript 项目中

文档入库:双向量写入策略

async function addDocument(content: string, metadata?: Record<string, any>) {
  const dense = await embedText(content) // number[]
  const sparse = textToSparseVector(content) // { indices, values }

  await qdrant.upsert('documents', {
    points: [
      {
        id: crypto.randomUUID(),
        vector: {
          dense,
          bm25: sparse,
        },
        payload: { content, metadata },
      },
    ],
  })
}

稀疏向量生成:分词 + 哈希 + 词频统计

import { createRequire } from 'node:module'
import { Jieba } from '@node-rs/jieba'

type SparseVector = { indices: number[]; values: number[] }

const require = createRequire(import.meta.url)
const { dict } = require('@node-rs/jieba/dict') as { dict: Uint8Array }
const jieba = Jieba.withDict(dict)

function fnv1aHash(str: string): number {
  let hash = 0x811c9dc5
  for (let i = 0; i < str.length; i++) {
    hash ^= str.charCodeAt(i)
    hash = Math.imul(hash, 0x01000193)
  }
  return hash >>> 0
}

function textToSparseVector(text: string): SparseVector {
  const tokens = jieba
    .cutForSearch(text, true)
    .map((t) => t.trim().toLowerCase())
    .filter(Boolean)
    .filter((t) => !/^[\p{P}\p{S}\p{Z}]+$/u.test(t))

  const tf = new Map<number, number>()
  for (const token of tokens) {
    const idx = fnv1aHash(token)
    tf.set(idx, (tf.get(idx) ?? 0) + 1)
  }

  const entries = [...tf.entries()].sort((a, b) => a[0] - b[0])
  return {
    indices: entries.map(([i]) => i),
    values: entries.map(([, v]) => v),
  }
}

向量数据库配置:双向量索引声明

await qdrant.createCollection('documents', {
  vectors: {
    dense: { size: 512, distance: 'Cosine' },
  },
  sparse_vectors: {
    bm25: { modifier: 'idf' },
  },
})

说明:

  • 稀疏向量通常先传 TF(词频)
  • IDF 在向量库侧处理(这里是 modifier: 'idf'

查询实现:三种检索模式

type SearchMode = 'dense' | 'sparse' | 'hybrid'

async function search(query: string, topK = 5, mode: SearchMode = 'hybrid') {
  const querySparse = textToSparseVector(query)
  const queryDense = mode === 'sparse' ? null : await embedText(query)

  if (mode === 'dense') return searchDense(queryDense!, topK)
  if (mode === 'sparse') return searchSparse(querySparse, topK)

  const [denseRes, sparseRes] = await Promise.all([
    searchDense(queryDense!, topK),
    searchSparse(querySparse, topK),
  ])
  return fuseByRRF(denseRes, sparseRes, topK)
}

RRF 融合算法:工程化的最佳选择

const RRF_K = 60
const rrf = (rank: number) => 1 / (RRF_K + rank + 1)

RRF(Reciprocal Rank Fusion)算法的核心思想是基于排名而非分数进行融合。当同一文档在稠密检索和稀疏检索的结果中排名都靠前时,其最终融合分数会更高。

相比于传统的加权融合方法,RRF 的优势在于:

  • 无需手动调整稠密向量和稀疏向量的权重比例
  • 对不同业务场景的适应性更强
  • 实现简单且效果稳定

实施路径

基于上述技术方案,完整的实施流程包括以下步骤:

  1. 选择 Embedding 模型:初期可选择 512 维的轻量级模型,平衡性能与成本
  2. 实现双向量生成:在文本入库时同时生成稠密向量和稀疏向量
  3. 配置向量数据库:创建包含 vectorssparse_vectors 的集合
  4. 实现混合检索:搜索接口默认使用 hybrid 模式
  5. 提供模式切换:为前端提供检索模式切换能力,支持关键词优先场景(sparse 模式)

常见问题与最佳实践

稀疏查询向量为空的处理

当查询文本全是标点符号或停用词时,稀疏向量可能为空。此时应返回空数组或降级到纯稠密检索,避免出现异常。

稠密向量的必要性检查

在稠密检索分支中,必须确保 embedding 已成功生成。空向量应直接抛出错误,而非静默返回不可靠的结果。

向量维度变更与数据迁移

当 embedding 模型的向量维度发生变化(如从 512 维升级到 1536 维)时,现有的向量集合通常无法直接复用,需要重新生成所有文档的向量并迁移数据。

中文分词词典的重要性

业务专有术语如果未被包含在分词词典中,会显著影响稀疏向量的召回效果。建议根据业务场景定制分词词典,加入领域特定术语。

总结

混合检索方案将向量检索技术从算法研究转化为可落地的搜索体验工程实践:

  • 稠密向量负责语义理解,解决同义词和口语化表达问题
  • 稀疏向量负责关键词精确匹配,确保专有名词召回准确性
  • 混合检索通过 RRF 算法融合两者优势,保证生产环境的稳定性

手把手从 0 诠释大模型 API 的本质: Tools + MCP + Skills

作者 ArcX
2026年2月15日 14:05

本文写于 2026 年 02 月 15 日.

如今 AI Agent 的各种新概念层出不穷:

  • Tools
  • MCP
  • Skills

许多人都会有这样的疑问: Tools 和 MCP 有什么区别? 我用了 MCP 还需要 Tools 吗? Skills 是取代 MCP 的吗? 本文会从 LLM API 的底层设计开始, 一步步介绍 Tools 和 MCP 的区别, 手动实现一个非常简易的 MCP (简易到你会觉得"就这?"), 最后简单提一下 Skills.

几个重要事实

  • 大模型是无状态的, 它对你们的过往对话一点都没有记忆. 每次调用 LLM API, 都是一次全新的请求, 就像换了一个完全陌生的人说话.
  • 大模型本身的开发(或许)很难, 需要很强的数学知识. 但是大模型应用开发不难, 做纯工程开发的传统程序员也可以很快上手.
  • MCP 和 Skills 都是纯工程层面的设施, 和 AI 毫无关系. 也就是说, 在这两个概念出现以前, 你完全可以自己实现一套类似的机制, 不需要 LLM API 支持.

基于以上几个事实, 本文会选择 Anthropic API 来解释. 因为 OpenAI 的 Responses API 提供了一个叫做 previous_response_id 的参数, 很容易误导人以为 LLM 本身有记忆功能. 但实际上 LLM 是没有记忆的, 这个 previous_response_id 并不会给 LLM 使用, 而是 OpenAI 的服务层面的工程设施, 相当于 OpenAI 帮我们存了历史记录, 然后发给 LLM. Conversations API 同理.

相比之下, Anthropic API 就原生了许多, 更容易感受到 LLM API 的本质.

技术栈

请注意区分 @anthropic-ai/sdk@anthropic-ai/claude-agent-sdk. 前者是 Anthropic API 的封装, 本质上是一个 HTTP Client, 封装了大量的调用 API 的方法; 后者是对 Claude Code (Claude CLI) 的封装, 封装了大量调用 claude 命令行的方法.

本文会使用 GLM-4.7-flash 这个兼容 Anthropic API 的免费模型来节约成本, 毕竟 LLM 应用开发最大的痛点就是每次调试运行都需要花钱.

const client = new Anthropic({
  baseURL: 'https://api.z.ai/api/anthropic', // 国际版, 你也可以使用国内版, 国内版认证方式是 apiKey
  authToken: ZAI_API_KEY,
});

Hello World

首先从一个最简单的请求开始:

const resp = await client.messages.create({
  max_tokens: 1024,
  messages: [
    {
      role: 'user',
      content: '英国的首都是哪里',
    },
  ],
  model: 'glm-4.7-flash',
});

console.log(resp);

Output (省略掉不重要的字段):

{
  "id": "msg_202602151117137d34660397a4418d",
  "type": "message",
  "role": "assistant",
  "model": "glm-4.7-flash",
  "content": [
    {
      "type": "text",
      "text": "英国的首都是**伦敦**(London)。"
    }
  ],
  "stop_reason": "end_turn"
}

多轮对话

正如上面反复提到的, LLM 是无状态的, 每次调用都像是一个全新的完全陌生的人对话. 想象一下, 如果你要和一个人聊天, 每聊完一句, 对面都会换一个人, 那么对方换的人应该如何继续和你的聊天? 当然就是把你之前的聊天历史全部看一遍. 所以调用 LLM 的时候, 每次都需要把历史记录全部传过去.

// 用一个 messages 数组来维护历史记录
const messages: MessageParam[] = [
  {
    role: 'user',
    content: '英国的首都是哪里',
  },
];

const resp = await client.messages.create({
  max_tokens: 1024,
  messages,
  model: 'glm-4.7-flash',
});

// 重点: 将 LLM 的第一次回复放到数组里
messages.push({
  role: 'assistant',
  content: resp.content,
});

// 再加入第二次对话内容
messages.push({
  role: 'user',
  content: '介绍一下这个城市的污染情况',
});

console.log(inspect(messages));

const resp2 = await client.messages.create({
  max_tokens: 1024,
  messages,
  model: 'glm-4.7-flash',
});

console.log(resp2);

可以看看第二次调用 API 传入的 messages 内容是:

[
  {
    "role": "user",
    "content": "英国的首都是哪里"
  },
  {
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "英国的首都是**伦敦**。"
      }
    ]
  },
  {
    "role": "user",
    "content": "介绍一下这个城市的污染情况"
  }
]

而 resp2 成功返回了伦敦的污染情况, 说明 LLM 确实感知到了上一次对话内容的城市是伦敦.

{
  "id": "msg_20260215115536fd125b1bca954cf6",
  "type": "message",
  "role": "assistant",
  "model": "glm-4.7-flash",
  "content": [
    {
      "type": "text",
      "text": "伦敦作为全球国际化大都市和前工业革命中心,其污染历史可以追溯到维多利亚时代,且至今仍是全球空气质量治理的“典型样本”..." // 我手动省略, 减少篇幅, 并非 LLM 省略
    }
  ],
  "stop_reason": "end_turn"
}

所以你应该也知道了, 所谓的 context windows, 其实可以简单理解为 messages 数组的文本长度, 而不是单条消息的长度.

Tools

原始方法

LLM 就像一个很聪明(虽然有时候会很蠢, 但是我们先假定 LLM 很聪明)的大脑, 但是它只有大脑, 没有眼睛 - 意味着它无法接收外界的信息(除了手动传入的 messages), 比如读一个文件; 没有手 - 意味着它无法做出任何行为, 比如修改一个文件. (可以把 LLM 想象成一个遮住眼睛的霍金).

Tools 就相当于给一个大脑安装了外置眼睛和手. 我们先用最朴素的方式让 LLM 调用工具: 直接在 prompt 里写, 有哪些工具, params 分别是什么, 然后让 LLM 选择一个使用, 并提供 params.

const messages: MessageParam[] = [
  {
    role: 'user',
    content: `写一句话介绍中国农历马年.
      你有以下 tools 可以调用:
      1. { name: "write", description: "write content to a file", params: 
        { "content": {"type": "string", description: "content"} },
        { "path": {"type": "string", description: "the path of the file to write"} },
       }

      2. { name: "read", description: "read content of a file", params: 
        { "path": {"type": "string", description: "the path of the file to read"} }
       }

       请你选择一个工具使用, 并且提供正确的 params. 你需要输出一个 JSON
    `,
  },
];

Output:

{
  "id": "msg_202602151218464370b8983c6c474d",
  "type": "message",
  "role": "assistant",
  "model": "glm-4.7-flash",
  "content": [
    {
      "type": "text",
      "text": "```json\n{\n  \"tool\": \"write\",\n  \"params\": {\n    \"content\": \"中国农历马年象征着奔腾不息的活力与豪迈,寓意着奋进、自由与驰骋。\",\n    \"path\": \"/马年介绍.txt\"\n  }\n}\n```"
    }
  ],
  "stop_reason": "end_turn"
}

可以看到, LLM 做到了选择正确的工具, 提供的参数内容倒是没问题, 但是存在以下几个巨大的问题:

  1. 返回的 text 本质上是个字符串. 虽然在 prompt 里明确要求了需要返回一个 JSON, 但是 LLM 依然返回了一个 JSON markdown, 而不是纯 JSON 字符串.
  2. prompt 并不可靠. LLM 无法做到 100% 遵循 prompt, 尤其是能力比较差的模型, 它可能会输出"好的, 下面是我调用工具的 JSON: xxx". 也就是说, 并不能保证输出一定是一个 JSON markdown.
  3. 就算输出是一个 JSON markdown, 我们还需要去解析这个 markdown, 一旦涉及到嵌套, 也就是 params 里也包含反引号, 会更加复杂.
  4. 无法保证输出的 JSON 100% 遵循了 prompt 里的格式, 比如我在调用的时候就出现过返回了 arguments 字段, 而不是 params.

基于以上问题, Tool Use (或者叫 Tool Call, Function Call, 一个意思. Anthropic 的官方术语是 Tool Use) 被内置进了 LLM, 成为了 LLM 自身的一个能力. 也就是说, 如果一个 LLM 不支持 Tool Use, 那么我们基本是没法在工程层面去做 polyfill, 也就无法实现调用 tool.

标准方法

上面的例子, 换标准的 Tool Use 方法:

const messages: MessageParam[] = [
  {
    role: 'user',
    content: `写一个关于中国农历马年的一句话介绍, 写入 test.txt 里`,
  },
];

const resp = await client.messages.create({
  max_tokens: 1024,
  messages,
  model: 'glm-4.7-flash',
  tools: [
    {
      name: 'write',
      description: 'write content to a file',
      input_schema: {
        type: 'object',
        properties: {
          content: {
            type: 'string',
            description: 'content',
          },
          path: {
            type: 'string',
            description: 'the path of the file to write',
          },
        },
      },
    },
    // read 同理, 省略掉
  ],
});

Output:

{
  "id": "msg_20260215123307fffbbd1b9fd84652",
  "type": "message",
  "role": "assistant",
  "model": "glm-4.7-flash",
  "content": [
    {
      "type": "text",
      "text": "我来写一句关于中国农历马年的介绍并保存到文件中。"
    },
    {
      "type": "tool_use",
      "id": "call_49f0c1dbe920406192ce9347",
      "name": "write",
      "input": {
        "content": "中国农历马年象征着活力、热情与自由,是充满朝气与拼搏精神的吉祥年份。",
        "path": "test.txt"
      }
    }
  ],
  "stop_reason": "tool_use"
}

可以看到这次的 content 里多了一个 tool_use 的 block, 里面写明了需要调用的 tool 的名字和参数. 这个 block 的类型是结构化的, 也就是说可以 100% 保证格式是正确, 符合预期的 (但是不能保证 100% 有这个 block, 取决于 LLM 的能力, 太蠢的 LLM 可能无法决策到底用哪个 tool). 这样我们就可以根据这个结构化的 tool_use block, 去执行对于的函数调用.

结果回传

考虑一个场景: 让 LLM 阅读一个文件并分析内容. 经过上面的内容, 你应该知道具体的流程是:

  1. User 要求 LLM 阅读某个文件并分析内容, 并且传入 read tool schema
  2. LLM 决定使用 read tool, 参数是文件路径
  3. User 根据路径读取文件内容, 然后传给 LLM
  4. LLM 成功输出分析结果
const tools: ToolUnion[] = [
  // 本文省略具体内容, read 和 write 两个 tools
];

const messages: MessageParam[] = [
  {
    role: 'user',
    content: `分析一下 package.json`,
  },
];

// 初始请求
const resp = await client.messages.create({
  max_tokens: 1024,
  messages,
  model: 'glm-4.7-flash',
  tools,
});

// 把 LLM 的第一次返回加入到 messages 里
messages.push({
  role: 'assistant',
  content: resp.content,
});

// 第一次返回大概率会包含 tool_use block
// content 是一个数组, 可能额外包含一个 text, 也可能直接就是一个 tool_use
// content 可能包含多个 tool_use, 用户需要把所有的都调用, 然后根据 tool_use_id 去匹配结果
const toolUseResults: ContentBlockParam[] = [];
for (const block of resp.content) {
  if (block.type === 'tool_use') {
    switch (block.name) {
      case 'read':
        try {
          const content = await readFile(block.input.path, 'utf-8');
          toolUseResults.push({ tool_use_id: block.id, type: 'tool_result', content, is_error: false }); // is_error 告诉 LLM 这个调用是否成功
        } catch (err) {
          toolUseResults.push({
            tool_use_id: block.id,
            type: 'tool_result',
            content: JSON.stringify(err),
            is_error: true,
          });
        }

        break;

      case 'write':
        try {
          await writeFile(block.input.path, block.input.content);

          toolUseResults.push({ tool_use_id: block.id, type: 'tool_result', content: 'success', is_error: false });
        } catch (err) {
          toolUseResults.push({
            tool_use_id: block.id,
            type: 'tool_result',
            content: JSON.stringify(err),
            is_error: true,
          });
        }
        break;
    }
  }
}
// 将 tool use results 传给 LLM
messages.push({ role: 'user', content: toolUseResults });

console.log(inspect(messages));

const resp2 = await client.messages.create({
  max_tokens: 1024,
  messages,
  model: 'glm-4.7-flash',
  tools,
});
console.log(resp2);

第二次传给 LLM 的 messages 为:

[
  {
    "role": "user",
    "content": "分析一下 package.json"
  },
  {
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "我来帮你分析 package.json 文件。首先,我需要读取这个文件来查看它的内容。"
      },
      {
        "type": "tool_use",
        "id": "call_eeb390dfe8f345269ff95f42",
        "name": "read",
        "input": {
          "path": "package.json"
        }
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "tool_use_id": "call_eeb390dfe8f345269ff95f42",
        "type": "tool_result",
        "content": "{\n  \"name\": \"gh-scout\",\n  \"module\": \"index.tsx\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"devDependencies\": {\n    \"@types/bun\": \"latest\"\n  },\n  \"peerDependencies\": {\n    \"typescript\": \"^5\"\n  },\n  \"dependencies\": {\n    \"@anthropic-ai/sdk\": \"^0.74.0\"\n  }\n}\n",
        "is_error": false
      }
    ]
  }
]

第二次从 LLM 返回的结果为:

{
  "id": "msg_2026021512452122fcf4da65474784",
  "type": "message",
  "role": "assistant",
  "model": "glm-4.7-flash",
  "content": [
    {
      "type": "text",
      "text": "## 📦 `package.json` 文件分析\n\n### 基本信息\n\n| 字段 | 值 | 说明 |\n|------|-----|------|\n| **name** | `\"gh-scout\"` | 项目名称,用于 npm 包标识 |\n| **module** | `\"index.tsx\"` | ES 模块入口文件 |\n| **type** | `\"module\"` | 使用 ES6 模块语法 |\n| **private** | `true` | 标记为私有项目,防止误发布到 npm |\n\n### 依赖管理\n\n#### 🔧 开发依赖 (`devDependencies`)\n```json\n\"@types/bun\": \"latest\"\n```\n- 类型定义包,用于 Bun 运行时的 TypeScript 类型支持\n- `\"latest\"` 表示使用最新版本\n\n#### 🤝 对等依赖 (`peerDependencies`)\n```json\n\"typescript\": \"^5\"\n```\n- **关键点**:`typescript` 被标记为对等依赖\n- 这意味着**项目不直接依赖 TypeScript**,而是期望用户环境中已安装 TypeScript\n- 适用于插件/工具类项目,让用户自己选择 TS 版本\n\n#### ⚡ 运行时依赖 (`dependencies`)\n```json\n\"@anthropic-ai/sdk\": \"^0.74.0\"\n```\n- Anthropic AI 的官方 SDK\n- 版本范围 `^0.74.0` 表示安装 0.74.0 或更高版本(不超过主版本号)\n\n---\n\n### 📝 总结\n\n这是一个**轻量级的 TypeScript 工具项目**(`gh-scout`),主要特点:\n\n1. **无运行时依赖**:除了 Anthropic SDK 外,没有其他运行时依赖\n2. **依赖 TypeScript**:用户自己提供 TS 环境\n3. **使用 Bun**:通过 `@types/bun` 表明使用 Bun 运行环境\n4. **模块化设计**:ES 模块类型,入口为 `index.tsx`\n\n项目看起来是一个与 GitHub 相关的工具(从名字 \"gh-scout\" 推测),可能用于分析或监控 GitHub 相关的操作。"
    }
  ],
  "stop_reason": "end_turn"
}

可以看到, LLM 第一次告诉我们需要调用 read tool 来读取文件内容. 我们调用完毕后把结果传给 LLM, LLM 第二次就成功分析出了内容.

插个题外话: 看到这里, 你应该也觉得原生 LLM 的方式实在是太繁琐了.

  • messages 要手动维护
  • tool_use 要手动解析 LLM 的返回, 手动调用, 然后手动把结果传到 messages 数组里
  • 如果 LLM 后续还要调用其他 tools, 还需要手动写一个循环

这正是现在各种 AI Agent 框架的意义, 比如 LangChain, LangGraph, Agno 等, 它们底层其实也都是做这种事情, 和传统领域的框架一样, 把繁琐的步骤都封装好了, 就像写 React 就不需要手动去操作 DOM 一样.

MCP

上面的方式虽然繁琐, 但也完全覆盖了所有场景了. 任何 tool use 都可以用上面的方式去实现. 那么为什么还需要 MCP 呢?

MCP 是什么

MCP (model context protocol) 是一个协议, 定义了 MCP Client 和 MCP Server 的通信方式. MCP 的原理和 AI/LLM 没有任何关系, 只是定义了 tools/resources/prompt 三种信息的通信格式.

MCP 解决了什么问题

假设现在没有 MCP 这个概念.

众所周知, LLM 非常擅长写文档类的东西, 比如 PR description. 所以现在你想让 LLM 帮你在 github 提一个 PR. 你需要先定义一个 tool:

const tools: ToolUnion[] = [
  {
    name: 'github_create_pr',
    description: 'create a PR on github',
    input_schema: {
      type: 'object',
      properties: {
        repo: {
          type: 'string',
          description: 'The repo name. Format: {owner}/{repo_name}',
        },
        source_branch: {
          type: 'string',
          description: 'The source branch name',
        },
        target_branch: {
          type: 'string',
          description: 'The target branch name',
        },
        title: {
          type: 'string',
          description: 'The title of the PR',
        },
        description: {
          type: 'string',
          description: 'The description body of the PR',
        },
      },
    },
  },
];

然后实现这个 tool 的调用过程:

case 'github_create_pr':
  const { repo, source_branch, target_branch, title, description } = block.input;
  const [owner_name, repo_name] = repo.split('/');

  try {
    // 也可以用 gh cli
    const resp = await fetch(`https://api.github.com/repos/${owner_name}/${repo_name}/pulls`, {
      method: 'post',
      headers: {
        accept: 'application/vnd.github+json',
        authorization: 'Bearer GITHUB_TOKEN',
      },
      body: JSON.stringify({
        title,
        body: description,
        base: source_branch,
        head: target_branch,
      }),
    });

    toolUseResults.push({
      tool_use_id: block.id,
      type: 'tool_result',
      content: await resp.text(),
      is_error: false,
    });
  } catch (err) {
    toolUseResults.push({
      tool_use_id: block.id,
      type: 'tool_result',
      content: JSON.stringify(err),
      is_error: true,
    });
  }
  break;

每加一个这样的 tool, 都需要花费大量的精力. 但实际上这些 tools 是高度通用的, 调用 github 是一个很普遍的需求.

此时你可能想到, 那我封装一个 github_tools 不就可以了?

于是你行动力拉满, 自己(或者让 AI)封装了一个 github_tools, 发布到了 npm 上, 其他用户可以像这样使用你的库:

import { tools as githubTools, callTool } from '@arc/github_tools';

const tools = [...myTools, ...githubTools];

for (const block of resp.content) {
  if (block.type === 'tool_use') {
    if (block.name.startsWith('github')) {
      const result = await callTool(block);
    }
  }
}

但是此时又有了两个新的问题:

  1. 你的新项目使用了 Go/Rust, 用不了 npm 包.
  2. 由于 Anthropic API 太贵, 你决定迁移到 DeepSeek API, 但是 DeepSeek 对 Anthropic 的兼容性不是很好(假设), 有些格式不匹配, 导致你的库调用失败.

MCP 的出现就是为了解决上面的问题. MCP 本质上是把 tools 的定义和执行都外置出去了. MCP 分为 Client 和 Server, 其中 Server 就是外置出去的部分, 负责 tools 的定义和执行. 而 Client 就是留在 AI 应用的部分, 负责和 Server 通信:

  • Hi Server, 告诉我有哪些 tools 可以用?
  • Hi Server, 我现在要调用 github_create_pr 这个 tool, 参数是 { xxx }

最简易的 MCP 实现

知道了 MCP 的设计思想, 那么我们完全可以写一个最简易的实现:

const server = async ({ type, body }: { type: string; body?: any }): Promise<string> => {
  if (type === 'list_tools') {
    return JSON.stringify([
      {
        name: 'github_create_pr',
        description: 'create a PR on github',
        input_schema: {
          type: 'object',
          properties: {
            repo: {
              type: 'string',
              description: 'The repo name. Format: {owner}/{repo_name}',
            },
            source_branch: {
              type: 'string',
              description: 'The source branch name',
            },
            target_branch: {
              type: 'string',
              description: 'The target branch name',
            },
            title: {
              type: 'string',
              description: 'The title of the PR',
            },
            description: {
              type: 'string',
              description: 'The description body of the PR',
            },
          },
        },
      },
    ]);
  }

  if (type === 'call_tool') {
    switch (body.name) {
      case 'github_create_pr':
        const { repo, source_branch, target_branch, title, description } = body.input;
        const [owner_name, repo_name] = repo.split('/');
        try {
          const resp = await fetch(`https://api.github.com/repos/${owner_name}/${repo_name}/pulls`, {
            method: 'post',
            headers: {
              accept: 'application/vnd.github+json',
              authorization: 'Bearer GITHUB_TOKEN',
            },
            body: JSON.stringify({
              title,
              body: description,
              base: source_branch,
              head: target_branch,
            }),
          });
          return await resp.text();
        } catch (err) {
          return JSON.stringify(err);
        }
    }
  }

  return 'Unknown type';
};

为了简单起见, 我直接写的是一个函数. 你完全可以将其做成一个 HTTP server, 因为反正这个函数的返回类型是 string, 可以作为 HTTP Response.

然后再写一个 client:

class McpClient {
  async listTools() {
    const tools = await server({ type: 'list_tools' });
    return JSON.parse(tools) as ToolUnion[];
  }

  async callTool(name: string, params: any) {
    const res = await server({ type: 'call_tool', body: params });
    return res;
  }
}

发现了吗? 上面的代码和 LLM 一点关系都没有, 这也是我一直在强调的重点: MCP 是工程设计, 不是 LLM 自身能力. 你完全可以脱离 AI, 直接使用 github 的官方 mcp server, 手动调用里面提供的方法. AI 在这里面唯一做的事情只是帮你决定调用的 tool_name + params.

用我们自己实现的 MCP Client 和 Server 改写上面的代码:

const messages: MessageParam[] = [
  {
    role: 'user',
    content: `分析一下 package.json`,
  },
];

const mcpClient = new McpClient();
const resp = await client.messages.create({
  max_tokens: 1024,
  messages,
  model: 'glm-4.7-flash',
  tools: await mcpClient.listTools(),
});

const toolUseResults: ContentBlockParam[] = [];
for (const block of resp.content) {
  if (block.type === 'tool_use') {
    if (block.name.startsWith('github')) {
      try {
        const result = await mcpClient.callTool(block.name, block.input);
        toolUseResults.push({ tool_use_id: block.id, type: 'tool_result', content: result, is_error: false });
      } catch (err) {
        toolUseResults.push({
          tool_use_id: block.id,
          type: 'tool_result',
          content: JSON.stringify(err),
          is_error: true,
        });
      }
    }
  }
}
messages.push({ role: 'user', content: toolUseResults });

const resp2 = await client.messages.create({
  max_tokens: 1024,
  messages,
  model: 'glm-4.7-flash',
  tools,
});
console.log(resp2);

瞬间简洁了不少. github 相关的 tools 定义和实现都外置到了 MCP Server 上, 这样就做了两层解耦:

  1. 具体语言解耦 - 你可以用任何语言实现 MCP Server, 只要它能处理字符串.
  2. LLM 解耦 - 你可以用任何支持 tool use 的 LLM, MCP 协议里单独定义了字段, 和 LLM 自己的字段无关.

Skills

现在你已经了解到了:

  1. Tool Use 是 LLM 自身的能力.
  2. MCP 不是 LLM 自身的能力, 而是工程设计, 辅助 Tool Use 用的.

那么最近很火的 Skills 又是什么呢? 是取代 MCP 的吗? 当然不是.

LLM 的 context 是非常宝贵的. 如果在系统提示词里放入太多的内容, 会导致系统提示词本身就占据大量 context. 举个例子, 假设你在开发一个 Coding Agent, 你集成了 github MCP Server, 那么每次 LLM API 调用, 都会把完整的 github MCP 相关的 tools 定义全部发给 LLM. 如果绝大部分用户根本就不会用 github 的能力, 那你就平白无故浪费了大量 context.

这就是 Skills 解决的问题: 渐进式披露, 或者叫按需加载.

我个人猜测 Skills 应该也是工程设计, 也不是 LLM 的能力, 因为我们完全可以自己实现一套机制, 用下面的系统提示词:

你是一个全能专家. 你拥有以下技能:

1. 做饭: 川菜, 粤菜, 日料, 英国美食.
2. 旅游: 规划旅游路线, 选择最佳景点, 解说历史遗迹.
3. 写代码: Typescript, Rust, Go, Python.
...
99. 视频制作: 制作爆款视频, 通过制造各种对立吸引流量.
100. Slides 制作: 制作精美的, 吸引领导眼光的 Slides.

所有的技能都被单独放到了 .skills 目录里. 当用户的问题与某个技能相关时, 你需要使用 Read tool 来读取对应技能的全部文档.

看到了吗? 系统提示词里只放了最基本的技能名字和简介(也就是 SKILL.md 开头的 name + description), 没有放具体技能的内容 (比如具体怎么做菜, 具体怎么写代码, 具体制造哪种对立更符合当下的热点), 大幅节约了 context.

如果此时用户问"帮我用 Rust 写个基本的 HTTP Server", 那么 LLM 第一条返回的消息应该就包含一个 read 的 tool_use, 读取 .skills/coding 里所有的内容, 里面就会包含具体的细节, 比如 "不要用 unwrap", "优先使用 axum 框架" 等. 用户把这些内容通过 tool_use_result 发给 LLM 后, LLM 再去写最终的代码给用户.

所以 Skills 也并不是什么神奇的事情, 并不是说 Skills 赋予了 AI 大量额外的能力, 只是单纯地通过按需加载, 节约了 context, 从而可以放大量的 Skills 在目录里. 毕竟在 Skills 出现之前, 你完全也可以把具体的写代码能力写到系统提示词里, LLM 照样会拥有完整的写代码的能力.

总结

本文从 0 开始一步步讲述了 LLM API 的设计, 多轮对话, 原生 Tool Use 的方式, MCP 的原理, Skills 的思想. 让我们回顾一下几个核心要点:

Tool Use - LLM 的核心能力

Tool Use 是 LLM 模型本身的能力, 需要模型在训练时就支持. 它让 LLM 能够:

  • 理解工具的定义和参数
  • 根据用户意图决策应该调用哪个工具
  • 结构化的格式输出工具调用信息

如果一个 LLM 不支持 Tool Use, 我们几乎无法通过工程手段来弥补, 因为用 prompt 的方式既不可靠, 又难以解析.

MCP - 工程层面的协议

MCP 是纯粹的工程设计, 和 AI 完全无关. 它解决的是工程问题:

  • 跨语言: 用任何语言都可以实现 MCP Server, 不局限于某个生态
  • 解耦: tools 的定义和实现从应用代码中分离出去
  • 复用: 同一个 MCP Server 可以被多个应用、多个 LLM 使用
  • 标准化: 统一了工具的通信协议, 避免了各自为政

MCP 的价值在于降低了集成成本, 让开发者可以专注于业务逻辑, 而不是重复造轮子.

Skills - 优化 Context 的策略

Skills 同样是工程层面的优化, 核心思想是:

  • 按需加载: 不把所有能力都塞进系统提示词
  • 渐进式披露: 需要什么能力才加载什么内容
  • 节约 Context: 让有限的 context window 发挥更大价值

Skills 不是新技术, 而是一种最佳实践模式, 在 Skills 概念出现之前我们就可以自己实现类似机制.

三者的关系

Tool Use, MCP, Skills 并不是互相取代的关系, 而是相辅相成:

┌─────────────────────────────────────────┐
│          AI Application                 │
│  ┌────────────────────────────────┐     │
│  │  Skills (按需加载能力)          │     │
│  │  - 系统提示词优化                │     │
│  │  - Context 管理                 │     │
│  └────────────────────────────────┘     │
│                                         │
│  ┌────────────────────────────────┐     │
│  │  MCP Client (工具集成层)        │     │
│  │  - 从 MCP Server 获取工具定义    │     │
│  │  - 调用 MCP Server 执行工具     │     │
│  └────────────────────────────────┘     │
│                    ↓                    │
│  ┌────────────────────────────────┐     │
│  │  LLM with Tool Use (AI 能力层) │     │
│  │  - 理解工具                      │     │
│  │  - 决策调用                      │     │
│  └────────────────────────────────┘     │
└─────────────────────────────────────────┘
                    ↕
        ┌──────────────────────┐
        │   MCP Server (外部)   │
        │   - github tools      │
        │   - filesystem tools  │
        │   - database tools    │
        └──────────────────────┘
  • Tool Use 是基础, 没有它其他都无从谈起
  • MCP 让工具的集成变得简单和标准化
  • Skills 让能力的组织变得高效

实践建议

在实际开发 AI 应用时:

  1. 选择支持 Tool Use 的 LLM: 这是硬性要求, 没有商量余地
  2. 优先使用现有的 MCP Server: 不要重复造轮子, github/filesystem 等常用工具都有官方 MCP Server
  3. 合理组织 Skills: 如果你的系统提示词超过几千 tokens, 考虑用 Skills 模式进行按需加载
  4. 理解工程本质: MCP 和 Skills 都是工程问题, 理解其原理后完全可以根据需求自己实现或调整

最后

希望本文帮助你厘清了 Tool Use, MCP, Skills 三者的关系. 记住核心观点: Tool Use 是 AI 能力, MCP 和 Skills 是工程设计. 它们各司其职, 共同构建了现代 AI Agent 的能力体系.

当你在开发 AI 应用时遇到问题, 先问自己: 这是 LLM 能力的问题, 还是工程设计的问题? 如果是 LLM 能力的问题, 我们就没法自己解决了, 只能换 LLM; 如果是工程设计的问题, 在这个极高速发展的行业, 如果还没有解决方案, 那我们是完全有能力去解决的.

目前属于 LLM 能力(需要训练支持)的概念:

  • Tool Use
  • Thinking
  • Structured Output
  • Multimodal

属于工程设计, 但是很难去 polyfill, 需要服务提供方支持的概念:

  • Streaming
  • Cache
  • Batch API

属于工程设计, 并且比较容易 polyfill 的概念:

  • MCP
  • Skills
  • SubAgent
❌
❌