给小破站加了个 Prompt 模板管理系统
2026年07月08日8 次阅读0 人喜欢
AIPromptNext.jsTypeScriptPrismaAgent小破站建设
所属合集
给小破站加了个 Prompt 模板管理系统
说实话,之前小破站的 Prompt 管理挺乱的。Chat Agent 的系统提示词散落在 markdown 文件里,Create Agent 的模板塞在 content_templates 表中,每次想改个提示词还得翻代码或者查数据库。
这次干脆下定决心,搞了一个专门的 AI Template 系统。
为什么要做这个
之前的问题很明显:
- Chat Agent 的提示词在
docs/reference/chat-agent-system-prompt.md文件里,改完得重启服务 - Create Agent 的提示词在
content_templates表,但这个表本来是给内容模板用的,混在一起很别扭 - 想给 Agent 加个"小红书风格指南"之类的 Skill,没地方放
- 提示词改了就改了,没有版本历史,出问题都不知道回滚到哪
怎么设计的
最后搞了个双表结构:
tb_ai_template # 模板主表:存 slug、名称、类型、当前激活版本
tb_ai_template_version # 版本表:存每次修改的正文、checksum、变更说明
核心思路是:
- slug 作为稳定引用键,比如
xhs-style-guide、chat-agent-day - 正文统一用 LangChain mustache,也就是
{{变量}}这种格式 - 支持 @ 引用,在模板里写
@xhs-style-guide,系统会自动解析并注入 metadata - 版本管理,每次保存都是新版本,可以随时回滚或对比 diff
模板类型
支持 7 种类型:
| 类型 | 用途 |
|---|---|
prompt |
系统提示词 |
skill |
可复用的技能指南 |
style |
风格指南 |
context |
上下文模板 |
tool_instruction |
工具说明 |
schema |
输出格式定义 |
checklist |
检查清单 |
管理后台
在 AI Lab 下面加了个 Prompts 管理页面,三栏布局:
- 左边:模板列表,支持搜索和类型筛选
- 中间:Markdown 编辑器 + 实时预览
- 右边:元数据面板,显示 @ 引用的模板信息
编辑器支持 @ 提及,输入 @ 会弹出模板列表,选中后会自动高亮显示引用关系。
内置模板
系统初始化时会预置几个核心模板:
- chat-agent-day - 日间聊天提示词,温和文艺风格
- chat-agent-night - 夜间聊天提示词,赛博朋克风格
- agent-create-agent-system - 创作助手系统提示词
- xhs-style-guide - 小红书风格指南,作为 Skill 按需加载
这些模板之前散落在各处,现在统一管理了。
Agent 工具集成
给 Create Agent 加了两个新工具:
typescript
// 列出可用的 Prompt / Skill 模板
list_prompt_skills(query?, type?)
// 按 slug 加载完整模板正文
load_prompt_skill_template(slug, version?, variables?)
Agent 的工作流变成:
- 用户说"帮我把这篇博客改成小红书图文"
- Agent 先调用
list_prompt_skills看看有什么可用的模板 - 发现
xhs-style-guide和xhs-blog-to-note - 按需加载,生成内容
这样 Agent 不需要把所有指南都塞进 system prompt,按需读取就行,省 token。
迁移旧数据
写了个 import-legacy 接口,一键把 content_templates 表的旧数据迁移到新系统。迁移逻辑:
- 按
scenario字段映射到新的 slug - 内容相同的跳过,有变化的创建新版本
- 保留旧表数据,不删
技术细节
Prisma Schema
prisma
model TbAiTemplate {
id Int @id @default(autoincrement())
slug String @unique @db.VarChar(120)
key String? @unique @db.VarChar(120)
name String @db.VarChar(200)
type String @default("prompt") @db.VarChar(40)
scope String @default("system") @db.VarChar(40)
description String? @db.Text
aliases Json?
metadata_json Json?
current_version Int @default(1)
status String @default("ACTIVE") @db.VarChar(30)
// ...
}
model TbAiTemplateVersion {
id Int @id @default(autoincrement())
template_id Int
version Int
content String @db.LongText
checksum String? @db.VarChar(80)
metadata_json Json?
change_note String? @db.Text
status String @default("ACTIVE") @db.VarChar(30)
// ...
}
@ 引用解析
正则匹配 @slug 格式,然后查询模板表注入 metadata:
typescript
const MENTION_PATTERN = /(^|[\s([{,。;:、])@([A-Za-z0-9_.-]+)/g;
export async function resolveTemplateMentions(content: string) {
const rawMentions = Array.from(content.matchAll(MENTION_PATTERN))
.map((match) => match[2])
.filter(Boolean);
// ...查询模板并返回 metadata
}
LangChain Mustache
统一使用 mustache 格式,和 LangChain 的 PromptTemplate 兼容:
typescript
export function createMustachePromptTemplate(content: string) {
return PromptTemplate.fromTemplate(content, { templateFormat: 'mustache' });
}
还没做好的
- 别名查询性能 - 目前别名匹配是全表扫描到内存过滤,数据多了会慢
- 缓存 - 频繁读取的模板没有缓存,每次都查库
- 并发版本号 - 高并发下版本号可能冲突
这些后面再优化吧。
最后
这次改动把 Prompt 管理从"散落各处"变成了"统一管理",以后改提示词不用改代码了,后台直接编辑就行。版本历史也有了,改坏了能回滚。
下一步打算把模板和 Eval 系统打通,这样改了提示词还能自动跑评测,看看效果有没有变差。