ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

使用 Claude Skills 构建专业级 AI Agent:TaoToken 统一 Key 接入与 Server Actions 实战

使用 Claude Skills 构建专业级 AI Agent:TaoToken 统一 Key 接入与 Server Actions 实战 1. 为什么要在 Next.js 里用 Claude Skills 搭 AI AgentClaude Skills 是一套基于文件系统的可复用能力包它把「领域知识 标准流程 可执行脚本」打包成一个目录让模型在需要时自动加载。放到 Next.js TypeScript 项目里它解决的是一个很具体的工程问题AI Agent 的提示词散落在各个 Server Actions 里改一处漏一处新人接手根本不知道业务规则藏在哪。我试过把校验逻辑、鉴权顺序、返回结构全塞进一个 800 行的 prompt 字符串结果就是每次加字段都要重新调一遍模型还经常漏掉 Zod 校验。Claude Skills 的思路是把这些规则外置成SKILL.md加脚本文件Server Actions 只负责调用规则由 Skill 统一维护。这套方案适合谁三类人最明显。第一类是正在用 Next.js App Router 做全栈项目、已经写了十几个 Server Actions 的开发者规则重复到想吐。第二类是想把 AI 能力接进内部系统、但不想让业务逻辑和模型调用耦合在一起的团队。第三类是已经在用 Claude Code 或 Cline 做日常编码想把项目规范沉淀下来的人。核心检索词先明确Claude Skills 是持久化、自动触发、支持代码执行的智能模块和一次性 Prompt 的区别在于它按需渐进加载。元数据约 100 tokens 常驻指令在触发时加载资源文件用到才读所以不会一上来就吃掉上下文窗口。在 Next.js 场景下Agent 的调用点通常是 Server Actions。用户提交表单Action 里做鉴权、校验、调模型、写库、返回结果。如果每个 Action 都自己拼 prompt维护成本会指数上升。把「怎么调模型、用什么模型、返回什么结构」抽成 SkillAction 只关心业务输入输出这才是可维护的路径。而模型调用这一层我用 TaoToken 做统一 Key 和 API 通道。原因是 Skills 本身不绑定供应商但 Server Actions 里总得有个 base URL 和 key。TaoToken 提供兼容 Anthropic 风格的接口把鉴权和路由收口到一处换模型或加通道时不用改业务代码。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。下面按「目录结构 → 环境变量 → Server Action 调用 → 本地验证 → 排障」的顺序走一遍每一步都能直接复制。2. TaoToken 前置准备Key、Base URL 与 Skills 目录结构在写 Server Action 之前先把两件事定下来模型通道的凭证以及 Skills 在项目里的存放位置。这两件事没定后面代码会反复改。先说 TaoToken 这边。进入控制台创建 API Key路径是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。创建后你会拿到一串 key形如sk-开头。这个 key 只显示一次复制到本地.env.local。如果你还没决定用哪个模型可以先去模型对话页 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 试一下返回格式确认通道通了再写代码。Base URL 统一用https://taotoken.net/api。注意这里不加任何查询参数鉴权靠x-api-key或Authorization头。模型 ID 按你实际选的填比如claude-sonnet-4-5这类。三个要素记牢Base URL、Key、Model ID后面配置文件里三件套缺一不可。然后是 Skills 目录。Claude Code 默认从~/.claude/skills/读取但项目级 Skill 更适合放在仓库里方便 Git 共享。我推荐的结构是项目根目录下建skills/每个 Skill 一个子目录目录名用 kebab-case。这样团队成员 clone 下来就能用不需要每人手动配。一个可复制的目录长这样my-nextjs-app/ ├── app/ │ ├── api/ │ │ └── agent/ │ │ └── route.ts │ └── actions/ │ └── generate.ts ├── skills/ │ └── nextjs-agent-writer/ │ ├── SKILL.md │ ├── validate-input.ts │ ├── build-prompt.ts │ └── examples/ │ └── sample-action.ts ├── lib/ │ └── taotoken.ts ├── .env.local └── package.jsonSKILL.md是入口里面写 Description、Triggers、Instructions。validate-input.ts和build-prompt.ts是可执行脚本Skill 触发时可以调用。examples/放参考代码模型按需读取不占常驻上下文。SKILL.md的内容我一般这样写重点是 Triggers 要具体别写「生成代码」这种泛词# Next.js Agent Writer Skill ## Description 为 Next.js App Router 项目生成符合团队规范的 Server Action 与 API Route 包含 Zod 校验、鉴权检查、统一错误返回结构。 ## Triggers - 生成 Server Action - 写一个带校验的 API Route - 调用模型并返回结构化结果 ## Instructions 1. 所有 Server Action 必须包含 use server 指令 2. 输入必须用 Zod 校验schema 定义在文件顶部 3. 调用模型前必须检查 session未登录返回 401 4. 模型调用统一走 lib/taotoken.ts 导出的 client 5. 返回结构固定为 { ok: boolean, data?: T, error?: string } 6. 禁止在客户端组件中直接调用模型接口 ## Code Execution - validate-input.ts校验传入对象是否符合 schema - build-prompt.ts根据业务类型拼接系统提示词环境变量文件.env.local里放三件套TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_MODELclaude-sonnet-4-5注意.env.local要进.gitignore别把 key 提交上去。团队协作时用.env.example占位真实值走部署平台的密钥管理。到这里前置就绪。下一步是把lib/taotoken.ts写出来作为所有 Server Actions 的统一出口。3. 可复制配置lib/taotoken.ts 与 Server Actions 调用示例这一节是全文的核心所有代码都能直接复制进项目。先写模型客户端封装再写 Server Action最后写 API Route 作为备选。lib/taotoken.ts的职责是收口 base URL、key、model并暴露一个callModel函数。这样以后换模型只改一个文件// lib/taotoken.ts const BASE_URL process.env.TAOTOKEN_BASE_URL ?? https://taotoken.net/api; const API_KEY process.env.TAOTOKEN_API_KEY; const DEFAULT_MODEL process.env.TAOTOKEN_MODEL ?? claude-sonnet-4-5; export type ModelMessage { role: user | assistant; content: string; }; export type ModelResult { ok: boolean; text?: string; error?: string; }; export async function callModel( messages: ModelMessage[], model: string DEFAULT_MODEL ): PromiseModelResult { if (!API_KEY) { return { ok: false, error: TAOTOKEN_API_KEY 未配置 }; } try { const res await fetch(${BASE_URL}/v1/messages, { method: POST, headers: { content-type: application/json, x-api-key: API_KEY, anthropic-version: 2023-06-01, }, body: JSON.stringify({ model, max_tokens: 1024, messages, }), }); if (!res.ok) { const detail await res.text(); return { ok: false, error: HTTP ${res.status}: ${detail.slice(0, 200)} }; } const json await res.json(); const text json?.content?.[0]?.text ?? ; return { ok: true, text }; } catch (err) { return { ok: false, error: (err as Error).message }; } }注意anthropic-version头走 Anthropic 兼容接口时通常需要带上。如果你的通道对头部有额外要求以控制台文档为准接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接下来是 Server Action。放在app/actions/generate.ts用use server标记输入用 Zod 校验鉴权用你项目里已有的auth()// app/actions/generate.ts use server; import { z } from zod; import { auth } from /auth; import { callModel } from /lib/taotoken; const inputSchema z.object({ topic: z.string().min(2).max(200), tone: z.enum([formal, casual]).default(formal), }); export type GenerateState { ok: boolean; data?: string; error?: string; }; export async function generateDraft( raw: unknown ): PromiseGenerateState { const session await auth(); if (!session?.user) { return { ok: false, error: 未登录 }; } const parsed inputSchema.safeParse(raw); if (!parsed.success) { return { ok: false, error: parsed.error.issues[0]?.message ?? 参数错误 }; } const { topic, tone } parsed.data; const systemHint tone formal ? 请用正式书面语输出。 : 请用轻松口语化语气输出。; const result await callModel([ { role: user, content: ${systemHint}\n主题${topic} }, ]); if (!result.ok) { return { ok: false, error: result.error }; } return { ok: true, data: result.text }; }这个 Action 的结构就是 Skill 里 Instructions 要求的先鉴权、再校验、再调模型、返回固定结构。如果你把SKILL.md放进项目Claude Code 在生成新 Action 时会自动套用这套模板不用每次手写。再给一个 API Route 版本适合需要被外部系统调用的场景// app/api/agent/route.ts import { NextRequest } from next/server; import { z } from zod; import { callModel } from /lib/taotoken; const bodySchema z.object({ prompt: z.string().min(1).max(2000), }); export async function POST(req: NextRequest) { const json await req.json().catch(() null); const parsed bodySchema.safeParse(json); if (!parsed.success) { return Response.json({ ok: false, error: 参数错误 }, { status: 400 }); } const result await callModel([{ role: user, content: parsed.data.prompt }]); if (!result.ok) { return Response.json({ ok: false, error: result.error }, { status: 502 }); } return Response.json({ ok: true, data: result.text }); }如果你用的是 Claude Code 做本地开发可以在项目根目录建.claude/settings.json把 Skill 目录指过去这样模型在写代码时能读到你的规范{ skills: { paths: [./skills] }, env: { TAOTOKEN_BASE_URL: https://taotoken.net/api } }注意 settings.json 里不要写 keykey 走环境变量。这个文件可以提交到仓库团队共享路径配置。到这里配置层就完整了一个客户端封装、一个 Server Action、一个 API Route、一个 Skill 目录、一个 settings 文件。下一步是本地跑起来验证。4. 本地验证从 curl 到页面调用的完整链路配置写完不验证等于没写。我习惯分三层验证先 curl 打通道再跑 Server Action 单测最后在页面里点一次。第一层curl 验证 TaoToken 通道是否通。这一步能排除 key 错误、base URL 拼错、模型 ID 不存在等问题curl -s https://taotoken.net/api/v1/messages \ -H content-type: application/json \ -H x-api-key: $TAOTOKEN_API_KEY \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet-4-5, max_tokens: 128, messages: [{role: user, content: 只回复两个字通了}] }正常返回里会有content数组第一项text字段是模型输出。如果返回 401说明 key 不对或没带上返回 404多半是路径拼错注意是/api/v1/messages而不是/v1/messages单独用。第二层写一个临时脚本直接调callModel验证封装逻辑。在项目根目录建scripts/check.ts用tsx跑// scripts/check.ts import { callModel } from ../lib/taotoken; async function main() { const res await callModel([{ role: user, content: 返回 JSON{ok:true} }]); console.log(JSON.stringify(res, null, 2)); } main();运行npx tsx scripts/check.ts如果输出{ ok: true, text: ... }说明封装没问题。这一步能提前发现环境变量没加载的问题因为tsx默认不读.env.local你需要用dotenv或node --env-file.env.local。第三层页面调用。建一个最简单的表单页把 Server Action 接上去// app/page.tsx import { generateDraft } from ./actions/generate; export default function Page() { async function action(formData: FormData) { use server; const topic String(formData.get(topic) ?? ); const res await generateDraft({ topic, tone: casual }); console.log(action result:, res); } return ( form action{action} input nametopic placeholder输入主题 / button typesubmit生成/button /form ); }提交后看终端日志如果打印出{ ok: true, data: ... }整条链路就通了。如果打印{ ok: false, error: 未登录 }说明auth()没拿到 session这是预期行为你需要先登录或临时注释掉鉴权检查做验证。验证通过后把 Skill 目录接进 Claude Code 试一次。在对话里输入「生成一个带 Zod 校验的 Server Action」观察它是否按SKILL.md的 Instructions 输出。如果它没读 Skill检查.claude/settings.json的路径是否正确以及SKILL.md的 Triggers 是否匹配你的措辞。实测下来Triggers 写得太泛会导致 Skill 不触发写得太窄又只对特定句子生效。我的经验是每个 Skill 配 3 到 5 个 Triggers覆盖同义表达比如「生成 Server Action」「写 Action」「新增 action 文件」都列上。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节按真实报错来每个都给出定位方法和修复动作。这些错误我在接入过程中基本都踩过一遍。401 Unauthorized。最常见三种原因。一是 key 没带上检查请求头里有没有x-api-key。二是 key 复制时带了空格或换行重新从控制台复制一次。三是环境变量没加载Next.js 里.env.local只在服务端生效客户端组件读不到确认你的调用发生在 Server Action 或 Route Handler 里。修复后重新 curl 一次确认。local proxy failed。这个报错通常出现在本地开发时请求发不出去多半是 base URL 写成了相对路径或者fetch在服务端运行时被某个中间件拦截。检查TAOTOKEN_BASE_URL是不是完整的https://taotoken.net/api不要写成/api。另外确认没有在next.config.js里配了会改写请求的 rewrites 规则。reading choices。这个报错说明代码按 OpenAI 的返回结构去读choices[0]但实际返回的是 Anthropic 风格的content[0].text。两种结构不一样别混用。如果你走的是 Anthropic 兼容接口就按json.content[0].text读如果走 OpenAI 兼容接口才用choices。检查lib/taotoken.ts里的解析逻辑和实际返回结构对齐。OAuth / authentication_error。如果你在 Claude Code 里配置了自定义通道但启动时仍走默认 OAuth 流程会报鉴权失败。这时候需要检查~/.claude/settings.json或项目级.claude/settings.json里的环境变量是否覆盖了默认端点。三件套要写全Base URL、Key、Model ID。缺任何一个都可能回落到默认鉴权。配置片段参考{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的key, ANTHROPIC_MODEL: claude-sonnet-4-5 } }注意这里 key 直接写在 settings 里只适合本地临时调试正式项目还是走环境变量注入。Skill 不触发。不是报错但很常见。检查SKILL.md的 Triggers 是否和你的输入匹配以及 Skill 目录是否在配置的搜索路径下。Claude Code 里可以用/skills命令查看已加载的 Skill 列表确认你的 Skill 在列。Zod 校验通过但模型返回空。多半是max_tokens设太小或者 prompt 里要求了结构化输出但模型没按格式返回。把max_tokens调到 1024 以上并在 prompt 里明确「只返回 JSON不要额外解释」。排障时如果拿不准返回结构直接去模型对话页 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 手动发一次请求看原始返回长什么样比在代码里猜快得多。接入细节以文档为准https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。6. 把 Skill 沉淀成团队资产Key 管理与长期编码路径代码跑通只是第一步真正省时间的是把 Skill 变成团队共享资产。这一节讲怎么管 Key、怎么共享 Skill、以及长期编码场景下怎么选通道。Key 管理上本地开发用.env.local部署环境用平台密钥管理。不要在代码里硬编码也不要把 key 写进settings.json提交。团队里每个人用自己的 key或者用统一的服务账号 key 但限制额度。TaoToken 控制台可以创建多个 key按项目或按人区分方便排查用量。创建入口在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。Skill 共享走 Git。把skills/目录提交到仓库新成员 clone 下来就能用。如果团队用 Claude Code可以在 README 里写清楚 Skill 目录位置和触发方式。更规范的做法是给每个 Skill 写一个简短的README.md说明它解决什么问题、Triggers 有哪些、依赖哪些脚本。长期编码和 Agent 场景如果调用量大、需要稳定通道可以看 Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 按套餐选比按量付费更可控。如果只是偶尔验证模型输出用模型对话页就够了。还有一个实践细节Skill 的 Instructions 要定期更新。项目规范变了Skill 不更新模型就会按旧规则生成代码。我一般把 Skill 更新和代码 review 绑在一起改规范的 PR 里必须同步改SKILL.md。最后说一个我踩过的坑。一开始我把所有规则都塞进一个 Skill结果 Triggers 互相冲突模型经常加载错。后来拆成三个一个管 Server Action 模板一个管 API Route 模板一个管错误处理规范。每个 Skill 职责单一触发准确率明显提升。Skill 不是越大越好按职责拆开反而更好维护。如果你还没开始建议先从一个最小的 Skill 做起只写 Server Action 的鉴权和校验规则跑通后再加脚本和示例。通道这边先把 key 建好、curl 打通再写代码。顺序对了后面基本不会卡。
返回列表