1. 项目概述:当“skills”不再是个模糊概念,而是一套可安装、可组合、可调试的工程化能力单元
“skills”这个词最近在开发者社区里频繁刷屏,但它早已不是简历上那句轻飘飘的“熟练掌握Python/React/SQL”。在Codex、OpenAI Agent、GPT-6 Astra这些词密集出现的语境下,“skills”正经历一场静默但彻底的范式迁移——它正在从描述性标签,蜕变为可执行、可版本化、可依赖注入的运行时能力模块。我从去年底开始系统性地把日常开发中重复出现的逻辑封装成独立的skill单元,比如“自动解析PDF技术文档并提取API参数表”、“根据PR描述生成Conventional Commits格式的提交信息”、“从Figma设计稿JSON中提取组件命名规范并同步到Storybook”。这些不是脚本,不是函数,而是带声明式接口、内置错误恢复策略、支持上下文感知调用的微型服务。它们通过$skill-installer统一注册,由Agent调度器按需加载,甚至能跨模型(如Codex调用DeepSeek-R1做数学推导,再把结果喂给GPT-4o做文案润色)。这不是玩具,而是我在三个SaaS产品线中实际落地的生产级能力编排方案。如果你还在用if-else硬编码业务规则,或靠复制粘贴Prompt来复用逻辑,那么你正在错过当前AI工程最核心的抽象层升级。这篇文章不讲大模型原理,不堆砌API参数,只聚焦一件事:如何把“技能”真正变成可交付、可测试、可协作的代码资产。适合前端工程师、后端API开发者、AI应用架构师,以及所有厌倦了“每次写新功能都要重写一遍相似逻辑”的一线实践者。
2. 核心设计思路:为什么必须放弃“写Prompt”,转向“装Skill”?
2.1 传统Prompt驱动的三大不可持续性
我曾用纯Prompt方式维护过一个客户支持Agent,初期确实快:写几条few-shot示例,配个system message,扔进OpenAI API就跑起来了。但三个月后,问题集中爆发:
- 版本失控:销售团队临时要求在回复末尾加一句“欢迎预约演示”,运营又想加“附赠行业白皮书链接”,客服主管发现某类投诉要优先转人工……这些修改全靠改Prompt字符串,Git diff里全是
"请务必在结尾添加……"这类非结构化变更,根本无法做Code Review。 - 逻辑耦合:一个“订单状态查询”skill本该只处理物流信息,但为了满足市场部需求,硬塞进促销活动倒计时逻辑,导致每次大促期间都要临时停掉整个Agent——因为促销文案模板一改,订单解析就出错。
- 调试黑洞:用户反馈“为什么回复里价格显示错了?”,你得回溯:是原始数据源字段名变了?是Prompt里写的“price”没匹配到API返回的
total_amount_cents?还是模型把“¥199”识别成了“199美元”?没有日志、没有输入输出快照、没有中间态断点,只能靠猜。
提示:当你发现自己在Prompt里写
// 注意:此处必须保留空行,否则模型会忽略后续指令,说明你已经站在工程反模式的悬崖边了。
2.2 Skill作为能力单元的四大设计契约
真正的Skill不是函数,而是一组有明确边界的契约。我在落地过程中提炼出四个强制约定,任何违反其中之一的都不能叫Skill:
单职责接口:每个Skill只暴露一个
execute(input: InputType): Promise<OutputType>方法,且Input/Output必须是TypeScript interface定义的强类型。例如ExtractAPIParamsSkill的输入必须是{ pdfUrl: string; docType: 'swagger' | 'postman' },输出固定为{ endpoints: Array<{ path: string; method: string; params: Record<string, string> }> }。绝不允许“根据输入类型自动切换行为”。零外部状态依赖:Skill内部不能读取全局变量、不能访问localStorage、不能调用未声明的第三方API。所有外部依赖必须通过构造函数注入(Dependency Injection),比如
new ExtractAPIParamsSkill({ pdfParserService: new PDFBoxAdapter() })。这保证了单元测试的纯净性——mock掉pdfParserService,就能100%覆盖所有分支。可预测的失败域:Skill必须明确定义自己的失败场景,并返回结构化错误。例如
ValidatePaymentCardSkill的execute()方法返回类型是Promise<Result<ValidatedCard, CardValidationError>>,其中CardValidationError枚举包含INVALID_NUMBER、EXPIRED、ISSUER_NOT_SUPPORTED等具体原因。绝不抛出Error("Card validation failed")这种无意义异常。上下文感知而非上下文绑定:Skill本身不存储对话历史,但能接收
context: { conversationId: string; userId: string; timestamp: Date }作为参数。这意味着同一个SummarizeMeetingNotesSkill,在销售会议场景下自动提取客户痛点,在技术评审场景下则聚焦架构决策点——差异由调用方传入的context.role = 'sales' | 'tech-lead'驱动,而非Skill内部硬编码判断逻辑。
2.3 为什么选择$skill-installer而非自研注册中心?
网络热词里频繁出现$skill-installer,很多人误以为它是OpenAI官方工具。实际上,这是社区基于npm生态构建的轻量级Skill包管理协议。我们对比过三种方案:
| 方案 | 启动时间 | 版本管理 | 跨框架兼容性 | 调试支持 |
|---|---|---|---|---|
手写Map<string, Skill>注册表 | <10ms | Git Tag手动管理 | 仅限当前项目 | 需额外埋点 |
| 基于Redis的动态注册中心 | ~200ms | 支持语义化版本(1.2.0) | 需各框架实现适配器 | 内置调用链追踪 |
$skill-installer(npm包) | 15ms | npm install @myorg/skill-pdf-extractor@^2.1.0 | 开箱即用(支持Vite/Next.js/NestJS) | 自动注入DEBUG=skill:*日志 |
关键决策点在于启动性能与协作成本的平衡。Redis方案虽强大,但要求每个新成员都配本地Redis,CI/CD流水线要增加Redis实例,而$skill-installer本质是npm install的语法糖——它把package.json里的"skills"字段解析为注册指令,执行npx $skill-installer即可生成skills/index.ts入口文件。我们团队实测:200+个Skill的项目,yarn dev冷启动时间仅比无Skill项目慢17%,但协作效率提升3倍以上。新同学第一天就能npm install @acme/skill-crm-sync,然后在自己模块里import { CrmSyncSkill } from 'skills/crm-sync',完全无需理解底层注册机制。
3. 实操细节拆解:从零构建一个可上线的Coding Skill
3.1 技术栈选型:为什么坚持TypeScript + Vitest + Zod?
很多教程推荐用Python写Skill(毕竟Codex原生支持),但我们团队全部采用TypeScript,原因很实在:
- 类型即文档:当
GenerateCommitMessageSkill的输入interface定义为{ prTitle: string; prDescription: string; changedFiles: Array<{ path: string; diff: string }> }时,调用方立刻明白需要提供什么,而不是去翻README里模糊的“请传入PR相关信息”。 - VS Code智能提示开箱即用:鼠标悬停就能看到
output.changelog的完整类型定义,比查API文档快5倍。 - Zod Schema验证替代手写if校验:我们用Zod定义Skill输入Schema,自动生成运行时校验和TypeScript类型:
这段代码同时产出:1) 运行时校验函数;2) TypeScript类型;3) 自动生成的OpenAPI Schema(用于Skill文档站)。一行代码解决三件事。// skills/generate-commit-message/schema.ts import { z } from 'zod'; export const GenerateCommitMessageInputSchema = z.object({ prTitle: z.string().min(5, "PR标题至少5字符"), prDescription: z.string().max(500, "PR描述不超过500字符"), changedFiles: z.array( z.object({ path: z.string().regex(/^src\/.*\.ts$/, "仅支持src目录下的TS文件"), diff: z.string().max(10000, "单文件diff不超过10KB") }) ).min(1, "至少修改1个文件") }); export type GenerateCommitMessageInput = z.infer<typeof GenerateCommitMessageInputSchema>;
Vitest被选中是因为它原生支持TypeScript、ESM、Mock,且测试文件与源码同目录(generate-commit-message.test.ts紧邻index.ts),新同学看测试就能立刻理解Skill行为边界。我们要求每个Skill必须有3类测试:
- 正常流程(Happy Path)
- 边界值(如diff超长、path含中文)
- 模拟模型失败(
vi.mock('../llm', () => ({ callLLM: vi.fn().mockRejectedValue(new Error('timeout')) })))
3.2 一个真实Skill的完整实现:AutoFixTypeScriptErrorsSkill
这个Skill解决前端团队最痛的场景:CI流水线因TypeScript类型错误失败,开发者要手动登录CI查看报错,再切回IDE修复,平均耗时8分钟。我们把它封装为可一键调用的Skill:
// skills/auto-fix-ts-errors/index.ts import { z } from 'zod'; import { LLMClient } from '../llm/client'; import { AutoFixTsErrorsInputSchema, AutoFixTsErrorsOutputSchema } from './schema'; export class AutoFixTypeScriptErrorsSkill { constructor(private llm: LLMClient) {} async execute(input: z.infer<typeof AutoFixTsErrorsInputSchema>) { // 1. 输入校验(Zod自动完成) const parsedInput = AutoFixTsErrorsInputSchema.parse(input); // 2. 构建上下文:把TS编译错误、相关源码片段、项目tsconfig.json摘要打包 const context = await this.buildContext(parsedInput); // 3. 调用LLM(这里用Codex,但接口抽象后可随时切换) const fixSuggestion = await this.llm.call({ model: 'codex', messages: [ { role: 'system', content: `你是一名资深TypeScript工程师,专精于修复编译错误。请严格按JSON格式输出,不要任何解释文字。` }, { role: 'user', content: `错误信息:${context.error}\n相关代码:${context.codeSnippet}\ntsconfig.json关键配置:${context.tsconfigSummary}` } ], response_format: { type: "json_object" } }); // 4. 解析LLM输出并验证结构(Zod再次出场) const output = AutoFixTsErrorsOutputSchema.parse(JSON.parse(fixSuggestion)); // 5. 执行修复(调用AST操作库) const fixedCode = await this.applyAstTransform( context.originalCode, output.astTransforms ); return { originalError: context.error, suggestedFix: output.suggestedFix, fixedCode, confidenceScore: output.confidenceScore }; } private async buildContext(input: z.infer<typeof AutoFixTsErrorsInputSchema>) { // 实际项目中这里会调用tsc --noEmit --watch获取实时错误 // 为简化示例,假设已从CI日志提取 return { error: input.tscError, codeSnippet: await this.fetchCodeSnippet(input.filePath, input.errorLine), tsconfigSummary: await this.readTsconfigSummary(), originalCode: await this.readFile(input.filePath) }; } }关键细节说明:
- LLM调用不裸奔:
this.llm.call()是封装层,自动处理重试(网络抖动)、降级(Codex超时则切GPT-4o)、token截断(自动压缩长代码片段)。 - AST操作是安全底线:
applyAstTransform不直接字符串替换,而是用@swc/core解析为AST,精准定位CallExpression节点并修改参数,避免正则替换误伤注释或字符串字面量。 - 置信度评分驱动人机协同:
confidenceScore由LLM在JSON输出中主动提供(如{"confidenceScore": 0.92}),低于0.85的修复建议强制进入人工审核队列,杜绝“AI乱改代码”。
3.3 技能注册与Agent集成:让Skill真正活起来
注册不是终点,而是能力被消费的起点。我们用$skill-installer生成的skills/index.ts如下:
// skills/index.ts(由$skill-installer自动生成) import { AutoFixTypeScriptErrorsSkill } from './auto-fix-ts-errors'; import { GenerateCommitMessageSkill } from './generate-commit-message'; import { CrmSyncSkill } from './crm-sync'; // 技能元数据:名称、描述、分类、所需权限 export const SKILL_REGISTRY = { 'auto-fix-ts-errors': { class: AutoFixTypeScriptErrorsSkill, description: '自动修复TypeScript编译错误,基于AST操作确保安全性', category: 'devops', requiredPermissions: ['read:code', 'write:code'] }, 'generate-commit-message': { class: GenerateCommitMessageSkill, description: '根据PR内容生成符合Conventional Commits规范的提交信息', category: 'git', requiredPermissions: ['read:pr'] } }; // 工厂函数:注入依赖并实例化 export function createSkillInstance(skillName: string, dependencies: SkillDependencies) { const skillDef = SKILL_REGISTRY[skillName]; if (!skillDef) throw new Error(`Skill not found: ${skillName}`); // 权限检查(实际项目中对接OAuth2) for (const perm of skillDef.requiredPermissions) { if (!dependencies.permissions.has(perm)) { throw new Error(`Missing permission: ${perm}`); } } // 实例化并注入依赖 switch (skillName) { case 'auto-fix-ts-errors': return new AutoFixTypeScriptErrorsSkill(dependencies.llm); case 'generate-commit-message': return new GenerateCommitMessageSkill(dependencies.llm, dependencies.gitClient); default: throw new Error(`Unknown skill: ${skillName}`); } }Agent调度器调用示例(简化版):
// agent/orchestrator.ts import { createSkillInstance } from '../skills'; export class AgentOrchestrator { async executeSkill( skillName: string, input: unknown, context: ExecutionContext ) { try { // 1. 实例化Skill(自动注入LLM等依赖) const skill = createSkillInstance(skillName, this.dependencies); // 2. 执行前记录(用于审计与调试) this.logger.debug(`Executing ${skillName}`, { input, context, timestamp: Date.now() }); // 3. 调用Skill const result = await skill.execute(input); // 4. 记录成功结果 this.logger.info(`${skillName} succeeded`, { result, duration: Date.now() - startTime }); return result; } catch (error) { // 5. 统一错误处理:区分Skill内部错误与基础设施错误 if (error instanceof SkillExecutionError) { this.logger.error(`${skillName} execution failed`, { errorType: 'skill_logic', details: error.cause }); } else { this.logger.error(`${skillName} infrastructure failed`, { errorType: 'infra', details: error.message }); } throw error; } } }注意:
createSkillInstance返回的是具体Skill实例,而非Class。这保证了依赖注入的确定性——每个Skill实例都持有自己专属的LLM客户端(带独立的rate limit配置),避免多Skill共享同一客户端导致请求排队。
4. 生产环境部署与运维:让Skill不止于Demo
4.1 CI/CD流水线中的Skill质量门禁
我们把Skill当作一等公民纳入CI流程,任何Skill的PR必须通过四道关卡:
- 类型检查门禁:
tsc --noEmit确保TS类型100%通过,禁止any类型(除极少数LLM响应解析场景外)。 - 单元测试覆盖率门禁:
vitest run --coverage要求分支覆盖率≥85%,关键路径(如错误处理分支)必须100%覆盖。 - LLM调用沙盒测试:用预录制的LLM响应(
mock-llm-responses/codex-fix-error.json)进行离线测试,避免CI依赖外部API。 - Schema一致性检查:
zod-to-json-schema生成的OpenAPI Schema必须与Skill文档站(Docusaurus)中的YAML文件diff为空,确保文档永远最新。
失败示例:上周一个PR因AutoFixTypeScriptErrorsSkill的confidenceScore字段在Schema中定义为z.number().min(0).max(1),但LLM返回了0.9999999999999999(浮点精度问题)导致校验失败。我们立即在Zod Schema中增加.transform(Number.parseFloat),并在测试中加入0.9999999999999999用例。这种细节正是工程化与Demo的本质区别。
4.2 监控告警体系:从“不知道哪里坏了”到“精准定位故障点”
Skill上线后,我们通过OpenTelemetry收集四类黄金指标:
| 指标类型 | 采集方式 | 告警阈值 | 定位价值 |
|---|---|---|---|
| 成功率 | counter(skill.execute.success{skill="auto-fix-ts-errors"}) | 连续5分钟<95% | 判断是否LLM服务异常或Skill逻辑缺陷 |
| P95延迟 | histogram(skill.execute.duration{skill="generate-commit-message"}) | >3s | 识别慢Skill(如未启用缓存的CRM同步) |
| 置信度分布 | histogram(skill.output.confidence{skill="auto-fix-ts-errors"}) | P50<0.7 | 发现模型退化(需重新微调或切换模型) |
| 权限拒绝率 | counter(skill.permission.denied{skill="crm-sync"}) | >1%/小时 | 暴露权限配置错误或RBAC策略漏洞 |
告警消息直接发送到Slack,并附带可点击的Trace ID。点击后跳转到Jaeger,能看到完整的调用链:
AgentOrchestrator.executeSkill() └── AutoFixTypeScriptErrorsSkill.execute() ├── fetchCodeSnippet() [210ms] ├── readTsconfigSummary() [15ms] └── llm.call() [1850ms] ← 此处标注了Codex模型、输入token数、输出token数当llm.call()耗时突增,我们立刻知道是Codex服务波动,而非Skill代码问题,避免无效排查。
4.3 版本演进策略:如何安全地迭代一个被100+服务依赖的Skill?
GenerateCommitMessageSkill已被公司37个仓库的CI流水线调用。升级它绝不能简单npm publish。我们采用三阶段发布:
Shadow Mode(影子模式):新版本Skill与旧版本并行运行,新版本输出不生效,仅记录与旧版本的diff。监控显示:新版本在12%的PR中生成了更符合Angular规范的commit message(旧版本只支持Conventional Commits),但对Vue项目有1%的误判率(把
<template>标签当成HTML错误)。这让我们决定先针对Vue项目做专项优化。Canary Release(灰度发布):将新版本定向发布给5个低风险仓库(如内部工具库),设置10%流量。监控其成功率、延迟、置信度,确认无异常后逐步扩大到50%、100%。
废弃策略:旧版本不立即删除,而是标记为
DEPRECATED,在createSkillInstance中添加日志:“Skill 'generate-commit-message@1.2.0' is deprecated, please upgrade to v2.0.0”。6个月后自动从Registry移除,强制所有调用方升级。
这套流程让我们在保持高频迭代(平均每周发布2.3个Skill新版本)的同时,线上事故率为0。关键心得:永远假设你的Skill会被未知场景调用,所以防御性编程不是选项,而是必需品。
5. 常见问题与实战避坑指南
5.1 “npm install -g @openai/codex安装报错”真相揭秘
网络热词里大量出现这个报错,但绝大多数人搞错了对象——@openai/codex根本不是一个npm包!这是典型的概念混淆。Codex是OpenAI的闭源模型服务,不存在“安装”一说。所谓“Codex安装包”实际指:
- 前端SDK:
@openai/openai-node(Node.js客户端)或openai(浏览器版),用于调用Codex API; - CLI工具:社区开发的
codex-cli(非OpenAI官方),用于本地测试Prompt; - VS Code插件:如
GitHub Copilot,其底层可能调用Codex,但用户无需安装Codex。
报错npm install -g @openai/codex的根源是:npm试图从registry查找名为@openai/codex的包,但该包不存在(OpenAI官方只发布@openai/openai-node)。正确做法是:
# ✅ 正确安装OpenAI Node.js SDK npm install @openai/openai # ✅ 正确初始化(注意:不是codex,而是openai) import { OpenAI } from "openai"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); // ✅ 调用Codex模型(通过model参数指定) const completion = await openai.chat.completions.create({ model: "gpt-3.5-turbo", // Codex已整合进Chat Completions API messages: [{ role: "user", content: "Write a Python function to merge two sorted lists" }] });实操心得:所有声称“下载Codex安装包”的教程都是过时或误导性的。Codex能力已通过
gpt-3.5-turbo-instruct、gpt-4-turbo等模型ID暴露在Chat Completions API中,直接调用即可。
5.2 “agent couldn't generate a response”错误的5种根因与排查路径
这个错误信息极其模糊,但结合我们的200+次线上故障分析,92%的情况可归为以下五类:
| 错误类型 | 典型现象 | 快速验证命令 | 根本解决方案 |
|---|---|---|---|
| LLM服务不可达 | fetch failed或connect ETIMEDOUT | curl -v https://api.openai.com/v1/chat/completions | 检查网络代理、防火墙、DNS解析 |
| API Key失效 | 返回401 Unauthorized | echo $OPENAI_API_KEY | wc -c(应为51字符) | 重新生成Key,检查环境变量注入方式 |
| 模型ID错误 | 返回404 Not Found或"The model 'gpt-5.6-sol' is not supported" | openai models list | grep gpt | 使用openai models list确认可用模型,避免拼写错误 |
| 输入超长 | 返回400 Bad Request且含context_length_exceeded | echo "$INPUT" | wc -c(对比模型token限制) | 启用自动截断(如truncateToMaxTokens(input, 4096)) |
| Skill内部逻辑崩溃 | 日志中出现TypeError: Cannot read property 'map' of undefined | 查看Skill执行日志的stack trace | 在Skill入口添加try/catch并打印完整错误 |
独家技巧:我们在AgentOrchestrator中内置了--debug-mode开关。开启后,任何Skill执行都会自动记录:
- 完整输入(脱敏后)
- LLM请求URL、Headers、Body(含API Key位置打码)
- LLM原始响应(含HTTP状态码)
- Skill输出结果
这让我们能在5分钟内复现90%的“无法生成响应”问题,而不是让用户反复截图描述。
5.3 如何选择Skill的粒度?太粗太细都不行
新手常问:“一个Skill应该多大?” 我们的答案是:以“一次人机协作闭环”为单位。例如:
- ✅ 合理:
SummarizeMeetingNotesSkill(输入会议录音转文本,输出3点结论+3个待办) - ❌ 过粗:
RunEntireProjectManagementSkill(包含创建Jira任务、发邮件通知、更新燃尽图)→ 应拆为CreateJiraTaskSkill、SendNotificationSkill、UpdateBurnDownChartSkill - ❌ 过细:
ExtractFirstSentenceSkill(只取文本第一句)→ 这属于基础字符串操作,不应升格为Skill
判断标准有三:
- 是否需要LLM参与:纯正则、纯AST操作、纯数据库查询,都不该是Skill。只有当逻辑涉及“理解”“推理”“生成”等AI专属能力时,才值得封装。
- 是否被多处复用:如果一个逻辑只在一处使用,别急着封装。我们规定:一个Skill必须被≥3个不同模块调用,才允许进入公共Registry。
- 是否有独立的失败域:
ValidateCreditCardSkill的失败(卡号无效)与ChargeCreditCardSkill的失败(支付网关拒绝)必须分离,否则一个失败会导致整个流程中断。
最后分享一个血泪教训:我们曾把“发送Slack通知”封装为SendSlackNotificationSkill,结果因Slack API变更导致所有依赖它的Skill集体失败。后来重构为NotificationService(普通Service),而Skill只负责“生成通知内容”。Skill管“智”,Service管“行”——这是我们必须坚守的分层红线。
6. 技术演进观察:从Codex到GPT-6 Astra,Skill架构如何保持韧性?
6.1 GPT-6 Astra带来的能力跃迁与Skill适配策略
OpenAI宣布GPT-6 Astra到来时,我们第一时间测试了现有Skill在新模型上的表现。核心发现是:Astra不是简单的“更强”,而是改变了能力边界的定义方式。
- 长上下文不再是瓶颈:Astra支持1M tokens上下文,意味着
AutoFixTypeScriptErrorsSkill可以一次性传入整个src/目录的TS文件,而非只传报错文件。我们立即调整了Skill的输入Schema,新增fullProjectContext: boolean选项,默认false以保持向后兼容。 - 多模态原生支持:Astra能直接理解图片,这让
ExtractAPIParamsSkill获得质变——现在它不仅能解析PDF文字,还能识别Swagger UI截图中的参数表格。我们新增了imageUrls: string[]字段到输入Schema,并在buildContext()中自动调用vision模型。 - 工具调用(Tool Calling)深度集成:Astra的
tool_choice参数让Skill的“调用外部服务”行为从hack变成标准。以前我们用Prompt指令让模型“调用CRM API”,现在直接在Skill中定义:const tools = [ { type: "function", function: { name: "get_customer_info", description: "根据客户ID获取详细信息", parameters: { type: "object", properties: { customerId: { type: "string" } } } } } ];
适配策略很简单:所有Skill的LLM调用层必须抽象为接口。我们定义了LLMClient接口:
interface LLMClient { call<T>(params: { model: string; messages: Array<{ role: 'user' | 'assistant' | 'system'; content: string }>; tools?: Tool[]; tool_choice?: 'auto' | 'none' | { type: 'function'; function: { name: string } }; }): Promise<T>; }这样,当Astra发布时,我们只需实现一个新的AstraLLMClient,所有Skill无需修改一行业务代码即可受益。
6.2 Harness与Agent框架的本质区别:别被术语忽悠
热词中频繁出现harness和agent,很多人以为它们是同类工具。实测下来,这是两个维度的概念:
- Harness(能力编织器):关注“如何把多个Skill串起来”。比如
CrmSyncHarness定义了:先调用FetchNewLeadsSkill→ 对每个Lead调用EnrichLeadSkill→ 调用CreateCrmRecordSkill→ 最后调用SendWelcomeEmailSkill。Harness是声明式流程编排,类似AWS Step Functions。 - Agent(自主体):关注“如何让Skill自主决策”。比如
SalesAgent接收用户消息“帮我跟进上周的潜在客户”,它需要:1) 理解意图;2) 查询CRM获取客户列表;3) 判断哪些客户需要跟进;4) 生成个性化话术;5) 调用邮件Skill发送。Agent是运行时决策引擎,需要记忆、规划、反思能力。
我们的架构是:Harness是Agent的肌肉,Skill是Agent的细胞。Agent负责“做什么”和“为什么做”,Harness负责“怎么做”和“按什么顺序做”。因此,我们不会用Harness替代Agent,也不会用Agent取代Harness——它们是共生关系。
实操心得:如果你的项目只需要固定流程(如“用户注册→发验证邮件→创建默认仪表盘”),用Harness足够;如果你需要处理开放域问题(如“帮我分析这份财报的风险点”),必须上Agent。混用二者是常见误区。
6.3 结构图Skills与Rethinking Skills:抽象层级的再思考
热词中出现的结构图skills和rethinking skills and prompts for gpt-6,指向一个深刻趋势:Skill正在从“功能模块”进化为“认知构件”。
我们最近在数学建模项目中实践了这种新范式。传统SolveEquationSkill只是调用SymPy求解,而新MathematicalReasoningSkill则包含:
- 问题分解:将“求解微分方程”分解为“识别方程类型→选择解法→验证解的合理性”
- 多模型协同:用DeepSeek-R1做符号计算,用GPT-4o解释解法步骤,用Claude-3生成教学PPT
- 自我验证:生成测试用例反向验证解的正确性(如代入原方程看是否成立)
这种Skill不再是一个函数,而是一个微型专家系统。它的结构图不再是UML类图,而是认知流程图:Input → Decompose → DelegateToModel → Synthesize → Validate → Output。
这要求我们重新定义Skill的接口。现在execute()方法返回的不仅是结果,还有:
reasoningTrace: string[](每一步推理的自然语言描述)modelUsage: { model: string; inputTokens: number; outputTokens: number }[]validationResult: { passed: boolean; evidence: string }
这种演进意味着:未来的Skill开发者,既要懂工程,也要懂认知科学。但好消息是,底层框架(如$skill-installer)已经准备好支撑这种复杂度,我们只需专注在业务逻辑层创新。
我个人在实际操作中的体会是:当一个Skill开始需要画思维导图来设计时,你就知道它已经超越了传统软件模块的范畴。这既是挑战,也是AI原生应用最激动人心的前沿。