☰
2026创新项目实训-个人博客(五):用 Spring AI Agent Skills 给博客加一个可配置的 AI 助手
2026/9/28 19:49:58 网站建设 项目流程

1. 从「硬编码提示词」到「可配置助手」:博客第五阶段要解决什么

做个人博客到第五阶段,很多人会卡在同一个地方:AI 助手能跑,但改一次行为就要动一次 Java 代码。想让它从「通用问答」变成「懂我博客的助手」,得改 Prompt;想让它按文章分类回答,得改 Service;想换个模型通道,又得翻配置文件。三件事耦合在一起,改一处崩三处。

这一篇要落地的,是把这个助手拆成「可配置」的结构:用 Spring AI 的 Agent Skills 机制,把助手的行为定义从代码里抽出来,放进SKILL.md和skill.meta.yml;把模型通道统一交给 TaoToken 的 Key/API 管理。最终效果是——你新增一个「读书笔记助手」或者「代码答疑助手」,只需要加一个目录、写两个文件,不用重新编译后端。

适合谁看:已经用 Spring Boot 搭好博客后端、跑通过一次 Spring AI 对话、现在想让 AI 助手「可配置化」的同学。如果你还没跑通最基础的 ChatClient 调用,建议先把上一阶段的对话接口跑起来再回来。

我试过把这套结构套在一个只有 3 个分类的小博客上,从「改代码」变成「改 Markdown」,迭代速度差别很明显。下面按「先讲清结构 → 再给可复制配置 → 最后本地验证」的顺序走,每一步都能直接抄。

2. TaoToken 前置:把 Key 和 API 通道先统一

在写 Skill 之前,先把模型通道这件事定下来。原因很简单:Agent Skills 解决的是「助手行为怎么配」,但行为配好了,最终还是要发请求给模型。如果每个 Skill 各自维护一套 Key 和 base_url,配置会散得到处都是。

TaoToken 在这里的角色是统一入口:一个 Key、一个 API 地址,博客里所有 Skill 共用。这样你在SKILL.md里只关心「助手该怎么说话」,通道的事交给统一配置。

先拿到 Key。打开控制台,在 API Keys 页面创建一个:

https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite

创建后你会得到一串sk-开头的 Key,复制保存。注意两点:一是这个 Key 只显示一次,丢了只能重建;二是别把它提交进 Git,后面我们用环境变量注入。

API 的基础地址是:

https://taotoken.net/api

这个地址不加任何查询参数,直接作为 Spring AI 的base-url使用。如果你用的是 OpenAI 兼容协议(Spring AI 的 OpenAI starter 就是),把 base-url 指向它、把 Key 填进去即可。

提示:Key 建议放在环境变量TAOTOKEN_API_KEY里,配置文件用${TAOTOKEN_API_KEY}引用,避免明文进仓库。

通道定好之后,后面所有 Skill 的调用都走这一条路,不用再关心「这个助手用哪个 Key」。

3. 可复制配置:settings.json 与 config.toml 骨架

这一节给两份可直接抄的配置骨架。一份是给编辑器/客户端用的settings.json,一份是给 Spring AI 项目用的config.toml。两者分工不同:前者管「开发时怎么连模型」,后者管「博客运行时怎么读 Skill」。

3.1 settings.json:开发期通道配置

如果你在本地用支持 OpenAI 兼容协议的客户端调试 Skill 的 Prompt 效果,可以先用这份settings.json把通道指过去:

{ "provider": "openai-compatible", "baseUrl": "https://taotoken.net/api", "apiKey": "${TAOTOKEN_API_KEY}", "model": "claude-sonnet-4-5", "temperature": 0.3, "maxTokens": 2048 }

几个参数说明一下。baseUrl固定指向 TaoToken 的 API 地址;apiKey用环境变量占位,别写死;temperature设 0.3 是因为博客助手多数场景要稳定输出(比如按分类回答),不需要太发散;maxTokens按你博客回答长度调,2048 对大多数问答够用。

3.2 config.toml:Spring AI 运行时配置

博客后端运行时,用config.toml管理 Skill 目录和模型参数:

[spring.ai.openai] base-url = "https://taotoken.net/api" api-key = "${TAOTOKEN_API_KEY}" chat.options.model = "claude-sonnet-4-5" chat.options.temperature = 0.3 [blog.skills] # Skill 根目录,每个子目录是一个助手 root = "src/main/resources/skills" # 默认启用的 Skill 名称 enabled = ["blog-assistant", "code-helper"] # 是否把 references 直接拼进 prompt(本项目采用的方式) inline-references = true

[blog.skills]这一段是自定义的,不是 Spring AI 官方配置,需要你在@ConfigurationProperties里接一下。root指向 Skill 目录,enabled控制哪些助手生效,inline-references对应后面要讲的「references 直接拼 prompt」策略。

注意:base-url结尾不要带/v1,Spring AI 的 OpenAI starter 会自己拼路径,多写一层会 404。

3.3 目录结构长什么样

配置定好后,Skill 目录按这个结构放:

src/main/resources/skills/ ├── blog-assistant/ │ ├── SKILL.md │ └── skill.meta.yml ├── code-helper/ │ ├── SKILL.md │ └── skill.meta.yml └── _shared/ └── references/ ├── java.md └── redis.md

_shared/references/放跨 Skill 共用的资料,比如 Java、Redis 这类通用知识,多个助手通过shared: true引用同一份,避免「这个助手的 Redis 资料新、那个还是旧的」。

4. SKILL.md 与 Prompt:让助手行为可配置

这一节是核心。Agent Skills 的思路是:把「助手是谁、怎么回答」写成自然语言,放进SKILL.md;把「系统怎么展示、分哪些类」放进skill.meta.yml。前者给模型看,后者给后端看,两者分开,token 开销更小、语义更清晰。

4.1 SKILL.md 示例:博客助手

以「博客 AI 助手」为例,blog-assistant/SKILL.md这样写:

--- name: blog-assistant description: 用于回答博客读者提问;优先围绕博客已有文章主题,结合文章内容给出解释,不确定时明确说明。 --- # Overview 你是这个个人博客的 AI 助手,目标是帮读者理解博客里的技术内容,而不是泛泛地聊技术。 # Instructions 1. 优先结合博客已有文章的主题回答,不要跑题到无关领域。 2. 回答顺序遵循:先给结论 -> 再给原因 -> 最后给可操作步骤。 3. 涉及代码时给出可运行片段,并标注语言。 4. 如果问题超出博客覆盖范围,明确说「这部分博客暂未涉及」,不要编造。 5. 不要一次性堆砌大段概念,控制单次回答长度。 # Additional Resources 回答前优先参考这些资料: - JAVA -> java.md - REDIS -> redis.md

SKILL.md的 YAML frontmatter 存元数据(name、description),Markdown body 存 persona 和规则。后端读取后,把 persona 注入 system prompt,模型就按这个角色回答。

4.2 skill.meta.yml:分类与展示配置

blog-assistant/skill.meta.yml管后端解析用的分类和 UI 配置:

displayName: 博客助手 display: icon: 📘 gradient: from-sky-500 to-blue-500 categories: - key: JAVA label: Java priority: CORE ref: java.md shared: true - key: REDIS label: Redis priority: NORMAL ref: redis.md shared: true

优先级规则可以这样定:ALWAYS_ONE必出且不参与轮转,CORE保底一题加轮转,NORMAL普通分配。博客助手场景下,CORE对应博客主攻方向,NORMAL对应偶尔涉及的。

4.3 Prompt 注入:persona 与 references 怎么拼

后端 Service 读取SKILL.md的 persona,拼进 system prompt:

String systemPrompt = skillSystemPromptTemplate.render() + buildSkillPersonaSection(skill) + outputConverter.getFormat();

user prompt 里只放模型真正需要的数据,比如分类分布和参考题库:

## 参考题库(references) {referenceSection}

这里有个关键选择:references 不走FileSystemTools按需加载,而是由 Java 代码直接拼进 prompt。原因是博客助手多数是单次结构化输出,模型生成前必须看到全部参考材料。如果走标准流程让模型逐个调用工具读文件,N 个分类就是 N 次往返,延迟和输出质量都会受影响。这是对标准流程的有意偏离,换来的是更稳的单次输出。

5. 本地验证:一次请求跑通配置与调用

配置写完,先别急着接前端,本地发一次请求验证链路。目标是确认三件事:Key 能通、Skill 能被读到、persona 生效。

5.1 用 curl 验证通道

先用最直接的方式确认通道没问题:

curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "messages": [ {"role": "system", "content": "你是博客助手,回答简洁。"}, {"role": "user", "content": "Redis 缓存穿透怎么处理?"} ] }'

返回里能看到choices[0].message.content就说明通道通了。如果返回 401,检查 Key;返回 404,检查 base-url 有没有多写/v1。

5.2 验证 Skill 被正确加载

在 Spring Boot 启动时加一段日志,打印加载到的 Skill:

@PostConstruct public void logSkills() { skillRegistry.getAll().forEach(skill -> log.info("Loaded skill: {} -> {}", skill.getName(), skill.getDescription())); }

启动后控制台应该输出:

Loaded skill: blog-assistant -> 用于回答博客读者提问... Loaded skill: code-helper -> 用于回答代码相关问题...

如果某个 Skill 没出现,先看目录名和SKILL.md的name是否一致,再看enabled列表里有没有它。

5.3 验证 persona 生效

发一个测试问题,观察回答是否符合SKILL.md里定的规则。比如问一个博客没覆盖的领域:

用户:帮我写一段 Rust 的异步代码。

如果 persona 生效,助手应该回答「这部分博客暂未涉及」,而不是直接生成 Rust 代码。这一步能验证 persona 确实被注入进了 system prompt。

5.4 验证 references 注入

问一个博客覆盖的主题,比如「Java 里 HashMap 扩容机制」,看回答里有没有引用java.md的内容。如果回答明显基于你的题库,说明 references 拼接生效。

6. 本篇常见错排查

配置类问题大多集中在几个固定位置,按下面顺序排查效率最高。

Key 相关:401 基本都是 Key 问题。先确认环境变量TAOTOKEN_API_KEY在当前 shell 里能echo出来;再确认 Key 没被空格或换行污染;最后确认 Key 没被撤销。

base-url 相关:404 或路径错误,检查base-url是不是写成了https://taotoken.net/api/v1。正确写法是https://taotoken.net/api,/v1由 starter 自己拼。

Skill 加载相关:Skill 没被读到,先看root路径对不对,再看SKILL.md的 frontmatter 格式。YAML frontmatter 必须用---包起来,name和description不能少。

persona 不生效:回答风格和SKILL.md对不上,检查buildSkillPersonaSection有没有真的把 persona 拼进 system prompt,以及enabled列表里有没有这个 Skill。

references 没注入:回答里看不到题库内容,检查inline-references是不是true,以及skill.meta.yml里的ref文件名和_shared/references/下的实际文件名是否一致。

模型参数相关:回答太长或太短,调maxTokens;回答太发散,调低temperature。博客助手建议temperature在 0.2 到 0.4 之间。

注意:如果多个 Skill 同时启用,确认它们的name不重复,否则后加载的会覆盖前一个。

7. 下一步:把助手接到博客前端

到这里,配置和调用链路已经跑通。接下来可以做的,是把这套 Skill 接到博客的问答接口上:前端发问题,后端按当前文章分类选对应 Skill,注入 persona 和 references,走 TaoToken 通道拿回答。

如果你要长期跑这套助手、或者后面想接 Agent 做多轮编码辅助,可以看下 Coding Plan,它更适合持续性的编码场景:

https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite

接入细节和参数说明在文档里:

https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite

想先在网页里试模型效果,可以直接用模型对话:

https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite

一个实用技巧:新增 Skill 时,先只写SKILL.md的 persona,不加 references,跑通一次对话确认 persona 生效,再补skill.meta.yml和题库。这样出问题时能快速定位是 persona 层还是 references 层,比一次性全配上再排查省事得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询