给小破站加了个 Prompt 模板管理系统

2026年07月08日9 次阅读0 人喜欢
AIPromptNext.jsTypeScriptPrismaAgent小破站建设
所属合集

给小破站加了个 Prompt 模板管理系统

说实话,之前小破站的 Prompt 管理挺乱的。Chat Agent 的系统提示词散落在 markdown 文件里,Create Agent 的模板塞在 content_templates 表中,每次想改个提示词还得翻代码或者查数据库。

这次干脆下定决心,搞了一个专门的 AI Template 系统。

为什么要做这个

之前的问题很明显:

  1. Chat Agent 的提示词在 docs/reference/chat-agent-system-prompt.md 文件里,改完得重启服务
  2. Create Agent 的提示词在 content_templates 表,但这个表本来是给内容模板用的,混在一起很别扭
  3. 想给 Agent 加个"小红书风格指南"之类的 Skill,没地方放
  4. 提示词改了就改了,没有版本历史,出问题都不知道回滚到哪

怎么设计的

最后搞了个双表结构:

复制代码
tb_ai_template          # 模板主表:存 slug、名称、类型、当前激活版本
tb_ai_template_version  # 版本表:存每次修改的正文、checksum、变更说明

核心思路是:

  • slug 作为稳定引用键,比如 xhs-style-guidechat-agent-day
  • 正文统一用 LangChain mustache,也就是 {{变量}} 这种格式
  • 支持 @ 引用,在模板里写 @xhs-style-guide,系统会自动解析并注入 metadata
  • 版本管理,每次保存都是新版本,可以随时回滚或对比 diff

模板类型

支持 7 种类型:

类型 用途
prompt 系统提示词
skill 可复用的技能指南
style 风格指南
context 上下文模板
tool_instruction 工具说明
schema 输出格式定义
checklist 检查清单

管理后台

在 AI Lab 下面加了个 Prompts 管理页面,三栏布局:

  • 左边:模板列表,支持搜索和类型筛选
  • 中间:Markdown 编辑器 + 实时预览
  • 右边:元数据面板,显示 @ 引用的模板信息

编辑器支持 @ 提及,输入 @ 会弹出模板列表,选中后会自动高亮显示引用关系。

内置模板

系统初始化时会预置几个核心模板:

  1. chat-agent-day - 日间聊天提示词,温和文艺风格
  2. chat-agent-night - 夜间聊天提示词,赛博朋克风格
  3. agent-create-agent-system - 创作助手系统提示词
  4. xhs-style-guide - 小红书风格指南,作为 Skill 按需加载

这些模板之前散落在各处,现在统一管理了。

Agent 工具集成

给 Create Agent 加了两个新工具:

typescript 复制代码
// 列出可用的 Prompt / Skill 模板
list_prompt_skills(query?, type?)

// 按 slug 加载完整模板正文
load_prompt_skill_template(slug, version?, variables?)

Agent 的工作流变成:

  1. 用户说"帮我把这篇博客改成小红书图文"
  2. Agent 先调用 list_prompt_skills 看看有什么可用的模板
  3. 发现 xhs-style-guidexhs-blog-to-note
  4. 按需加载,生成内容

这样 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' });
}

还没做好的

  1. 别名查询性能 - 目前别名匹配是全表扫描到内存过滤,数据多了会慢
  2. 缓存 - 频繁读取的模板没有缓存,每次都查库
  3. 并发版本号 - 高并发下版本号可能冲突

这些后面再优化吧。

最后

这次改动把 Prompt 管理从"散落各处"变成了"统一管理",以后改提示词不用改代码了,后台直接编辑就行。版本历史也有了,改坏了能回滚。

下一步打算把模板和 Eval 系统打通,这样改了提示词还能自动跑评测,看看效果有没有变差。

加载评论中...