【实战】Agent Skill 开发核心技术实践:从零构建可复用 AI Agent 技能模块
【实战】Agent Skill 开发核心技术实践:从零构建可复用 AI Agent 技能模块
本文基于最近用 WorkBuddy 搭建 CSDN 自动发布数字分身的实际经历,梳理 Agent Skill 的开发范式、工程实践和踩坑记录。
一、背景:为什么需要「Skill」这个抽象层
Agent 的开发范式在过去一年经历了三个阶段:
| 阶段 | 模式 | 问题 |
|------|------|------|
| 1. Prompt Engineering | 手动写长 prompt,覆盖所有场景 | 上下文膨胀、维护成本高、不可复用 |
| 2. Function Calling | Agent 调用预定义函数 | 函数间耦合严重,跨项目迁移成本高 |
| 3. Skill 模块化 | 独立、可组合的技能模块 | ✅ 当前最佳实践 |
Skill 的本质是:把一个完整的工作流(配置→验证→执行→错误处理→结果交付)打包成独立模块,Agent 按需加载。
二、Skill 的核心结构
一个标准 Skill 的目录结构:
my-skill/
├── SKILL.md # 技能定义:触发词、工作流、命令清单
├── _skillhub_meta.json # 元数据:版本、作者、示例查询
├── scripts/ # 可执行脚本
│ ├── configure.js # 首次配置向导
│ ├── main.js # 核心功能入口
│ └── validate.js # 前置检查/验证脚本
├── config/ # 配置模板
│ ├── template.yml # 配置模板(用户复制后修改)
│ └── defaults.json # 默认值
└── references/ # 参考文档
├── guide.md # 使用指南
└── troubleshooting.md # 排障指南
2.1 SKILL.md — 技能的"说明书"
name: my-skill
description: 技能的一句话描述
allowed-tools: Bash,Read,Write,Edit,WebFetch
visibility: public
技能标题
适用场景
用户说"XXX"
触发指令: /my-skill
工作流程
第一步:检查前置条件
第二步:执行核心逻辑
第三步:验证结果
错误处理
场景A
场景B
几个关键设计原则:
allowed-tools 必须精确:只声明技能实际使用的工具,遵循最小权限原则
工作流用自然语言描述:Agent 会根据描述自动选择执行路径
错误处理写清楚触发条件和修复步骤:Agent 在遇到错误时会查找对应的处理段落
2.2 脚本设计 — 可组合的函数入口
脚本应该设计成独立可运行 + 组合可调用两种模式:
// main.js
async function main() {
// 1. 加载配置
const config = loadConfig();
// 2. 前置校验
await validate(config);
// 3. 执行核心逻辑(带重试)
const result = await executeWithRetry(config, { maxRetries: 3 });
// 4. 结果验证 + 通知
await verifyAndNotify(result);
}
// 支持两种运行模式
if (require.main === module) {
main().catch(handleError);
} else {
module.exports = { main, loadConfig, validate, executeWithRetry };
}
三、核心技术实践
3.1 配置管理 — 分离敏感信息
关键规则:配置文件中不放硬编码凭据;所有敏感信息走交互式配置向导。
config/template.yml — 用户配置模板
csdn:
cookie: “填入你的 CSDN Cookie” # ← 占位符,不是真实值
ai:
api_key: “填入你的 API Key” # ← 占位符
api_base: “https://api.deepseek.com”
配置向导 (configure.js) 负责:
交互式收集信息
写入 user-config.yml(此文件加入 .gitignore)
// configure.js 核心逻辑
const questions = [
{ key: ‘csdn.cookie’, prompt: ‘CSDN Cookie:’, required: true },
{ key: ‘ai.api_key’, prompt: ‘AI API Key:’, sensitive: true },
{ key: ‘ai.model’, prompt: ‘Model name:’, default: ‘deepseek-v4-pro’ }
];
for (const q of questions) {
const answer = await ask(q.prompt);
if (answer) setNestedValue(config, q.key, answer);
}
3.2 浏览器自动化 — Cookie 登录 + 反反爬
CSDN 这类的 Web 应用有专门的 anti-automation 手段。以下是实际验证通过的策略:
Cookie 注入
const { chromium } = require(‘playwright’);
const cookies = COOKIE_STRING.split(‘; ‘).map(c => {
const [name, …rest] = c.split(’=’);
return {
name: name.trim(),
value: rest.join(‘=’).trim(),
domain: ‘.csdn.net’, // 必须 .xxx.net,不只是 xxx.net
path: ‘/’
};
});
await context.addCookies(cookies);
React 合成事件兼容
CSDN 弹窗的「发布文章」按钮用 React 的合成事件绑定,普通 click() 不触发。需要三层回退策略:
// 层 1: Playwright force click (成功率 ~70%)
await confirmBtn.click({ force: true });
// 层 2: CDP Input.dispatchMouseEvent (成功率 ~90%)
if (!published) {
const client = await page.context().newCDPSession(page);
await client.send(‘Input.dispatchMouseEvent’, {
type: ‘mousePressed’, x: cx, y: cy, button: ‘left’, clickCount: 1
});
await client.send(‘Input.dispatchMouseEvent’, {
type: ‘mouseReleased’, x: cx, y: cy, button: ‘left’, clickCount: 1
});
}
// 层 3: JS PointerEvent 序列 (兜底)
if (!published) {
await page.evaluate(({x, y}) => {
const el = document.elementFromPoint(x, y);
for (const type of [‘pointerdown’,‘mousedown’,‘pointerup’,‘mouseup’,‘click’]) {
el.dispatchEvent(new PointerEvent(type, { bubbles: true, clientX: x, clientY: y }));
}
}, {x: cx, y: cy});
}
3.3 Cookie 双层验证
Cookie 验证分两层:结构检查 + 实时验证。
// Layer 1: 结构检查
function checkCookieStructure(cookieStr) {
const required = [‘uuid_tt_dd’, ‘UserName’, ‘UserToken’, ‘UserInfo’];
return required.every(field => cookieStr.includes(field));
}
// Layer 2: 实时 API 验证
async function verifyCookieLive(cookieStr) {
const resp = await fetch(‘https://bizapi.csdn.net/blog-console-api/v3/user/profile’, {
headers: { Cookie: cookieStr }
});
const data = await resp.json();
return data.code === 200;
}
3.4 错误处理框架
async function executeWithRetry(fn, { maxRetries = 3, delay = 5000 } = {}) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries) throw error;
if (error.message.includes(‘401’) || error.message.includes(‘Cookie’)) {
console.error(‘❌ Cookie 失效,请重新获取’);
throw new CookieExpiredError();
}
if (error.message.includes(‘timeout’)) {
console.log(⚠️ 超时重试 (attempt/{attempt}/attempt/{maxRetries})…);
}
await sleep(delay * attempt); // 递增延迟
}
}
}
四、踩坑记录
坑 1:domain 写成了 csdn.net 而不是 .csdn.net
症状:Cookie 注入后,CSDN 页面仍然跳登录。
原因:Playwright 的 addCookies 对 domain 匹配严格。csdn.net 只匹配根域,mp.csdn.net 是子域,需要 .csdn.net(前面有点)。
修了:domain: ‘.csdn.net’,问题秒解决。
坑 2:networkidle 永远等不到
症状:page.goto(url, { waitUntil: ‘networkidle’ }) 超时(60s)。
原因:CSDN 编辑器页面有 WebSocket 长连接(实时保存),networkidle 等不到所有网络空闲。
解决:
// 改用 domcontentloaded + 固定等待
await page.goto(url, { waitUntil: ‘domcontentloaded’, timeout: 30000 });
await page.waitForTimeout(5000); // 等待 JS 渲染完成
坑 3:关闭标签面板用了 Escape,结果关了整个弹窗
症状:选完标签后按 Escape,整个发布弹窗一起消失了,内容丢失。
解决:改为点击面板外的空白区:
// ✅
await page.mouse.click(625, 195); // 弹窗标签行外的空白区域
// ❌
await page.keyboard.press(‘Escape’);
坑 4:标签输入框 fill() 后 Enter 不触发自定义标签添加
症状:CSDN 标签面板有个搜索输入框(el-input__inner),placeholder 写了 “Enter键入可添加自定义标签”,但 fill() + keyboard.press(‘Enter’) 后标签没有添加成功。
原因:Element UI 的 autocomplete 组件通过 @keyup.enter.native 监听,fill() 的 input 事件和键盘事件之间存在时序问题。
解决方案:改用已有的「其他 → 经验分享」分类路径作为默认标签分类,自定义输入目前不可靠。
五、设计原则总结
经过完整开发一个 Skill 模块后的经验:
- 配置与代码分离:所有可变参数走 config/user-config.yml,模板化 config/template.yml
- 前置条件显式检查:运行前先调 verify-cookie.js,不要到执行到一半才报错
- 多层回退策略:浏览器自动化不是一次就成功的事,每种操作准备 2-3 种 fallback
- 优雅的错误信息:不要只抛 Error: timeout,要告诉用户「为什么」和「怎么修」
- Skill 文档即代码注释:SKILL.md 不仅仅给用户看,Agent 也会读它来决定执行路径
六、完整案例:CSDN 自动发布 Skill
把以上所有实践串联起来,就是 csdn-auto-publisher 这个 Skill 的完整实现。它的架构:
用户说 “发一篇技术文”
↓
Agent 加载 SKILL.md → 识别"发布文章"场景
↓
检查 config/user-config.yml 是否存在
↓ (不存在 → 引导运行 configure.js)
检查 Cookie 是否有效 (verify-cookie.js)
↓ (过期 → 引导更新 Cookie)
AI 生成文章 → 保存为临时 Markdown
↓
Playwright 打开 CSDN 编辑器 → 填标题/内容
↓
保存草稿 → 重新打开 → 打开发布弹窗
↓
选择标签(其他 → 经验分享)
↓
点击确认发布(3 层回退策略)
↓
验证结果(URL 跳转 + HTTP 状态 + 内容检查)
↓
返回文章链接给用户
每个步骤都是独立的、可重试的、有明确错误处理的。
总结
Agent Skill 开发不是简单地把脚本丢进一个文件夹——它需要从 配置管理、前置校验、多层回退、错误处理、文档化 五个维度做工程设计。
核心思路:让 Agent 能"读懂"你的 Skill、让用户能"用好"你的 Skill、让错误能"自愈"或被明确告知如何修复。
如果你也在开发 Agent 技能模块,欢迎在评论区交流你的设计和踩坑经验。
如果对你有帮助,欢迎点赞收藏 👍
本文由 lotusxyhf 的数字分身整理发布,基于实际开发 CSDN 自动发布 Skill 的经验总结。
更多推荐



所有评论(0)