当前位置: 首页 > news >正文

构建影墨·今颜提示词库:数据库设计与管理系统开发

构建影墨·今颜提示词库:数据库设计与管理系统开发

最近在尝试用AI生成一些小红书风格的图片和文案,效果时好时坏,挺看运气的。后来我发现,问题的关键往往出在“提示词”上。一个好的提示词,能直接决定生成内容的质量和风格。于是,我就想,能不能把这些好用的提示词都攒起来,分门别类地管理好,下次用的时候直接调用,岂不是省时省力?

这个想法催生了“影墨·今颜”提示词库项目。它的核心很简单:建一个数据库,把那些验证过的好提示词、对应的生成参数和效果评分都存进去,然后再做个简单的网页来管理它们。这听起来像不像一个经典的“数据库课程设计”项目?没错,它就是一个非常贴近实际应用场景的数据库实践。今天,我就来分享一下这个从想法到实现的完整过程,希望能给想做类似工具的朋友一些启发。

1. 需求分析与数据库设计

做任何系统,第一步都是想清楚要干什么。对于提示词库,我们的核心需求很明确:存得好、找得快、用得方便

具体来说,我们需要存储和管理以下几类信息:

  1. 提示词本身:这是核心,包括正向提示词(描述想要什么)和负向提示词(描述不想要什么)。
  2. 生成参数:比如用哪个模型、采样方法、迭代步数、图片尺寸等。这些参数和提示词搭配,才能复现出好效果。
  3. 分类与标签:提示词千千万,没有分类就像大海捞针。我们需要按风格(如“国风”、“ins风”)、按内容(如“美食”、“穿搭”)、按用途(如“封面图”、“商品图”)来打标签。
  4. 效果评价:这个词条生成的效果到底怎么样?是“惊艳”还是“一般”?需要有一个地方让使用者打分或写评语,方便后续筛选优质词条。
  5. 预览与关联:光看文字描述不够直观,最好能关联上用它生成的效果图。一个提示词可能对应多张效果图(尝试了不同参数),一张效果图也可能由多个提示词组合生成。

基于这些需求,我开始设计数据库表。这里我选择了最常用的MySQL。设计的原则是避免数据冗余,建立清晰的关联关系。

1.1 核心表结构设计

我设计了五张核心表,它们之间的关系可以用一个简单的图来理解:提示词是中心,它拥有参数,被打上标签,收获了评分,并关联着效果图

-- 1. 提示词主表 (prompts) CREATE TABLE prompts ( id INT PRIMARY KEY AUTO_INCREMENT, positive_prompt TEXT NOT NULL COMMENT '正向提示词', negative_prompt TEXT COMMENT '负向提示词', description VARCHAR(500) COMMENT '词条描述、使用心得', author VARCHAR(100) COMMENT '贡献者', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, is_public BOOLEAN DEFAULT TRUE COMMENT '是否公开' ); -- 2. 生成参数表 (generation_params) CREATE TABLE generation_params ( id INT PRIMARY KEY AUTO_INCREMENT, prompt_id INT NOT NULL, model_name VARCHAR(100) COMMENT '如 stable-diffusion-v1.5', sampler VARCHAR(50) COMMENT '采样器,如 Euler a', steps INT DEFAULT 20, cfg_scale DECIMAL(3,1) DEFAULT 7.5 COMMENT '提示词相关性', width INT DEFAULT 512, height INT DEFAULT 512, seed BIGINT COMMENT '随机种子,用于复现', FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE ); -- 3. 标签表 (tags) 与关联表 (prompt_tags) CREATE TABLE tags ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) UNIQUE NOT NULL COMMENT '标签名,如“国风”、“美食”', type VARCHAR(20) COMMENT '标签类型:style, content, usage等' ); CREATE TABLE prompt_tags ( prompt_id INT, tag_id INT, PRIMARY KEY (prompt_id, tag_id), FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE ); -- 4. 效果评分表 (ratings) CREATE TABLE ratings ( id INT PRIMARY KEY AUTO_INCREMENT, prompt_id INT NOT NULL, user_id VARCHAR(100) COMMENT '评分用户标识', score TINYINT CHECK (score >= 1 AND score <= 5) COMMENT '1-5分', comment TEXT COMMENT '评价详情', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE ); -- 5. 效果图表 (preview_images) CREATE TABLE preview_images ( id INT PRIMARY KEY AUTO_INCREMENT, prompt_id INT NOT NULL, param_id INT COMMENT '关联的具体参数id', image_url VARCHAR(500) NOT NULL COMMENT '图片存储路径或URL', thumbnail_url VARCHAR(500) COMMENT '缩略图路径', generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, FOREIGN KEY (param_id) REFERENCES generation_params(id) ON DELETE SET NULL );

这个设计有几个考虑点:

  • 灵活性:一个提示词可以对应多套参数(generation_params),方便记录哪种参数组合效果最好。
  • 可扩展性:标签系统(tags+prompt_tags)可以随时增加新标签,分类维度不受限。
  • 数据完整性:使用外键约束(FOREIGN KEY)和级联删除(ON DELETE CASCADE),确保删除一个提示词时,它的参数、标签关联、评分和图片记录也被清理干净,避免垃圾数据。
  • 性能考虑:为经常查询的字段,如prompt_id,tag_id,以及tags.name,建立了索引,加快搜索速度。

2. 管理系统后端开发

数据库设计好了,接下来就要让数据“活”起来,也就是开发后端API。我选择了Python的Flask框架,因为它轻量、灵活,适合快速构建这种管理系统的后端。

2.1 环境搭建与核心接口

首先,建立数据库连接,并规划出系统需要提供的核心功能接口。

# app.py from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy from datetime import datetime app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://username:password@localhost/prompt_lib_db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) # 这里将之前SQL定义的模型用SQLAlchemy的ORM方式重新定义一遍(示例仅展示Prompt模型) class Prompt(db.Model): __tablename__ = 'prompts' id = db.Column(db.Integer, primary_key=True) positive_prompt = db.Column(db.Text, nullable=False) negative_prompt = db.Column(db.Text) description = db.Column(db.String(500)) author = db.Column(db.String(100)) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) is_public = db.Column(db.Boolean, default=True) # 定义关系 params = db.relationship('GenerationParam', backref='prompt', cascade='all, delete-orphan') tags = db.relationship('Tag', secondary='prompt_tags', backref='prompts') ratings = db.relationship('Rating', backref='prompt', cascade='all, delete-orphan') previews = db.relationship('PreviewImage', backref='prompt', cascade='all, delete-orphan') # 其他模型(GenerationParam, Tag, Rating, PreviewImage)的定义类似,此处省略... # 创建数据库表(首次运行) with app.app_context(): db.create_all()

接下来,实现最关键的提示词管理查询检索接口。

# 1. 创建新的提示词条目 @app.route('/api/prompts', methods=['POST']) def create_prompt(): data = request.json try: new_prompt = Prompt( positive_prompt=data['positive_prompt'], negative_prompt=data.get('negative_prompt', ''), description=data.get('description', ''), author=data.get('author', 'anonymous') ) # 处理标签(假设前端传标签名数组,如 ['国风', '美食']) tag_names = data.get('tags', []) for name in tag_names: tag = Tag.query.filter_by(name=name).first() if not tag: tag = Tag(name=name) db.session.add(tag) new_prompt.tags.append(tag) # 处理参数 if 'params' in data: param_data = data['params'] new_param = GenerationParam( model_name=param_data.get('model_name'), sampler=param_data.get('sampler'), steps=param_data.get('steps', 20), cfg_scale=param_data.get('cfg_scale', 7.5), width=param_data.get('width', 512), height=param_data.get('height', 512), seed=param_data.get('seed') ) new_prompt.params.append(new_param) db.session.add(new_prompt) db.session.commit() return jsonify({'message': '创建成功', 'id': new_prompt.id}), 201 except Exception as e: db.session.rollback() return jsonify({'error': str(e)}), 500 # 2. 多功能查询提示词(核心) @app.route('/api/prompts', methods=['GET']) def get_prompts(): # 获取查询参数 keyword = request.args.get('q', '') # 关键词搜索 tag_name = request.args.get('tag') # 按标签筛选 page = int(request.args.get('page', 1)) per_page = int(request.args.get('per_page', 20)) # 构建查询 query = Prompt.query.filter(Prompt.is_public == True) if keyword: query = query.filter(db.or_( Prompt.positive_prompt.contains(keyword), Prompt.description.contains(keyword) )) if tag_name: query = query.join(Prompt.tags).filter(Tag.name == tag_name) # 排序和分页 prompts = query.order_by(Prompt.updated_at.desc()).paginate(page=page, per_page=per_page, error_out=False) # 构造返回数据 result = { 'items': [{ 'id': p.id, 'positive_prompt': p.positive_prompt[:200] + '...' if len(p.positive_prompt) > 200 else p.positive_prompt, 'description': p.description, 'author': p.author, 'tags': [tag.name for tag in p.tags], 'preview': p.previews[0].thumbnail_url if p.previews else None # 取第一张预览图 } for p in prompts.items], 'total': prompts.total, 'page': page, 'per_page': per_page } return jsonify(result)

2.2 效果预览与评分功能

为了让词库更直观、更有参考价值,预览和评分功能必不可少。

# 3. 为提示词上传效果预览图 @app.route('/api/prompts/<int:prompt_id>/previews', methods=['POST']) def upload_preview(prompt_id): prompt = Prompt.query.get_or_404(prompt_id) if 'image' not in request.files: return jsonify({'error': '没有文件上传'}), 400 file = request.files['image'] # 这里应实现文件保存逻辑,例如保存到本地static文件夹或上传到云存储 # 假设保存后得到文件路径 import os from werkzeug.utils import secure_filename filename = secure_filename(file.filename) save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(save_path) # 生成缩略图(这里省略缩略图生成代码) thumbnail_path = generate_thumbnail(save_path) # 创建数据库记录 new_preview = PreviewImage( prompt_id=prompt_id, image_url=f'/static/uploads/{filename}', thumbnail_url=f'/static/thumbnails/{filename}' ) db.session.add(new_preview) db.session.commit() return jsonify({'message': '上传成功', 'preview_id': new_preview.id}) # 4. 为提示词评分 @app.route('/api/prompts/<int:prompt_id>/ratings', methods=['POST']) def rate_prompt(prompt_id): data = request.json user_id = data.get('user_id', 'guest') # 简单处理用户身份 # 检查是否已评分(可选,防止刷分) existing = Rating.query.filter_by(prompt_id=prompt_id, user_id=user_id).first() if existing: return jsonify({'error': '您已评过分'}), 400 new_rating = Rating( prompt_id=prompt_id, user_id=user_id, score=data['score'], comment=data.get('comment', '') ) db.session.add(new_rating) db.session.commit() # 可以在这里触发更新提示词的平均分(在Prompt模型中添加一个avg_score字段并计算) return jsonify({'message': '评分成功'})

3. 前端管理界面实现

后端API准备好了,我们需要一个界面来操作。为了简单快捷,我直接用HTML、CSS和JavaScript(配合一点Vue.js的响应式特性)写了个单页应用。

3.1 核心页面布局

前端主要包含三个区域:顶部的搜索筛选栏、左侧的标签导航、中间的主内容区。

<!-- index.html 简化版 --> <!DOCTYPE html> <html> <head> <title>影墨·今颜 - 提示词库管理</title> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <style> /* 基础样式:布局为上下+左右结构 */ body { font-family: sans-serif; margin: 0; padding: 20px; background: #f5f5f5; } .container { display: flex; max-width: 1200px; margin: auto; gap: 20px; } .sidebar { width: 200px; flex-shrink: 0; } .main-content { flex-grow: 1; } .search-bar { margin-bottom: 20px; } .prompt-card { background: white; border-radius: 8px; padding: 15px; margin-bottom: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .tags span { display: inline-block; background: #e0e0e0; padding: 3px 8px; margin-right: 5px; border-radius: 3px; font-size: 0.9em; } </style> </head> <body> <div id="app"> <h1>📚 影墨·今颜提示词库</h1> <div class="search-bar"> <input type="text" v-model="searchKeyword" placeholder="搜索提示词或描述..." @input="fetchPrompts"> <button @click="showCreateDialog = true">+ 添加新词条</button> </div> <div class="container"> <div class="sidebar"> <h3>标签筛选</h3> <ul> <li><a href="#" @click.prevent="selectTag('')">全部</a></li> <li v-for="tag in popularTags" :key="tag"> <a href="#" @click.prevent="selectTag(tag)">{{ tag }}</a> </li> </ul> </div> <div class="main-content"> <!-- 提示词列表 --> <div v-if="loading">加载中...</div> <div v-else> <div class="prompt-card" v-for="prompt in prompts" :key="prompt.id"> <div style="display: flex; gap: 15px;"> <img v-if="prompt.preview" :src="prompt.preview" alt="预览" style="width:100px; height:100px; object-fit:cover; border-radius:4px;"> <div style="flex-grow:1;"> <h4 style="margin-top:0;">{{ prompt.positive_prompt }}</h4> <p>{{ prompt.description }}</p> <div class="tags"> <span v-for="tag in prompt.tags" :key="tag">{{ tag }}</span> </div> <small>by {{ prompt.author }}</small> <div> <button @click="editPrompt(prompt)">编辑</button> <button @click="viewDetail(prompt.id)">详情</button> </div> </div> </div> </div> <!-- 分页 --> <div style="text-align: center; margin-top: 20px;"> <button @click="prevPage" :disabled="page <= 1">上一页</button> <span>第 {{ page }} 页 / 共 {{ totalPages }} 页</span> <button @click="nextPage" :disabled="page >= totalPages">下一页</button> </div> </div> </div> </div> <!-- 创建/编辑对话框 (通过v-if控制显示) --> <div class="dialog" v-if="showCreateDialog"> <!-- 对话框内容:表单,包含正负向提示词、描述、标签、参数等输入项 --> <h3>{{ editingPrompt ? '编辑' : '创建' }}提示词</h3> <textarea v-model="form.positive_prompt" placeholder="正向提示词..."></textarea> <textarea v-model="form.negative_prompt" placeholder="负向提示词(可选)..."></textarea> <input v-model="form.description" placeholder="描述..."> <input v-model="form.tagsInput" placeholder="标签,用逗号分隔..."> <button @click="submitForm">保存</button> <button @click="closeDialog">取消</button> </div> </div> <script> const { createApp, ref, computed, onMounted } = Vue; createApp({ setup() { const searchKeyword = ref(''); const selectedTag = ref(''); const page = ref(1); const perPage = 20; const prompts = ref([]); const total = ref(0); const loading = ref(false); const showCreateDialog = ref(false); const form = ref({ positive_prompt: '', negative_prompt: '', description: '', tagsInput: '' }); const popularTags = ref(['国风', 'ins风', '美食', '人像', '插画', '产品图']); // 计算总页数 const totalPages = computed(() => Math.ceil(total.value / perPage)); // 获取提示词列表 async function fetchPrompts() { loading.value = true; let url = `/api/prompts?page=${page.value}&per_page=${perPage}`; if (searchKeyword.value) url += `&q=${encodeURIComponent(searchKeyword.value)}`; if (selectedTag.value) url += `&tag=${encodeURIComponent(selectedTag.value)}`; try { const response = await fetch(url); const data = await response.json(); prompts.value = data.items; total.value = data.total; } catch (error) { console.error('获取数据失败:', error); } finally { loading.value = false; } } // 标签选择 function selectTag(tag) { selectedTag.value = tag; page.value = 1; fetchPrompts(); } // 分页 function prevPage() { if (page.value > 1) { page.value--; fetchPrompts(); } } function nextPage() { if (page.value < totalPages.value) { page.value++; fetchPrompts(); } } // 初始化 onMounted(fetchPrompts); // 监听搜索关键词变化(防抖处理) let debounceTimer; searchKeyword.value.$watch(() => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { page.value = 1; fetchPrompts(); }, 300); }); return { searchKeyword, selectedTag, page, prompts, total, loading, showCreateDialog, form, popularTags, totalPages, fetchPrompts, selectTag, prevPage, nextPage, editPrompt(prompt) { /* 打开编辑对话框并填充form */ }, submitForm() { /* 提交创建/编辑请求 */ }, closeDialog() { showCreateDialog.value = false; form.value = {}; }, viewDetail(id) { window.location.href = `/detail.html?id=${id}`; } }; } }).mount('#app'); </script> </body> </html>

这个前端界面虽然简单,但具备了管理系统的核心功能:浏览、搜索、筛选、分页查看,以及触发创建和编辑操作。通过Vue的响应式绑定,用户体验非常流畅。

4. 实际应用与效果

系统搭建完成后,我们团队开始往里面填充内容。主要来源有两个:一是平时自己用AI生成时积累下来的“黄金提示词”;二是从社区、平台上搜集整理的高质量词条。

使用流程变得非常顺畅:

  1. 当需要生成一组“国风美食”主题的图片时,设计师不再需要苦思冥想提示词。他只需要在系统里点击“国风”和“美食”标签,立刻就能看到几十个相关的、带有效果预览的提示词。
  2. 选中一个喜欢的词条,点开详情,不仅能看到完整的提示词,还能看到别人使用时的最佳参数配置(如模型、采样器、步数),甚至可以直接复制这些参数到自己的AI绘画工具里,极大提高了出图效率和效果稳定性。
  3. 生成出满意的图片后,设计师可以立刻将成果图作为“效果预览”上传到该词条下,并给出一个五星好评。这样,这个词条就变得更加丰富、有说服力。

带来的价值是实实在在的:

  • 效率提升:寻找和测试提示词的时间从小时级缩短到分钟级。
  • 质量稳定:复用经过验证的词条和参数,生成结果的质量下限提高了,减少了“抽卡”式的随机性。
  • 知识沉淀:团队的经验(什么词好用、配什么参数)被固化下来,新成员也能快速上手,避免了重复造轮子。
  • 激发创意:浏览词库本身就是一个学习过程,能看到各种奇思妙想的组合,常常能激发新的创作灵感。

这个“影墨·今颜”提示词库,从一个具体的内容创作痛点出发,最终落地成了一个能切实解决问题的工具。它本质上就是一个典型的、以应用为导向的数据库系统。从需求分析、表结构设计,到后端API开发、前端界面实现,整个流程走下来,不仅解决了实际问题,也是一次非常扎实的工程实践。如果你也在为管理越来越多的AI提示词而烦恼,不妨也试着动手搭建一个属于自己的小系统吧,这个过程本身,就充满了乐趣和成就感。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

http://www.cnnetsun.cn/news/1880847.html

相关文章:

  • ncmdump终极指南:轻松解锁网易云音乐NCM格式,让音乐自由播放
  • 四可与防逆流的协同之道:构建红区治理的技术组合拳
  • 基于深度回声状态网络DeepESN的锂离子电池SOH估算模型(NASA数据集)-创新算法【MATLAB】
  • 玻璃幕墙“室内侧”的耐撞击研究
  • Flash时代终结后的技术救赎:CefFlashBrowser如何让经典内容重获新生
  • Langchain4j(5)RAG之多格式文档加载(PDF / Word / TXT / 批量文件夹)
  • STM32CubeMX配置RMBG-2.0边缘计算设备
  • JavaEE|计算机是如何工作的
  • 小白程序员必看!收藏这份AI Agent“思考”与“行动”架构指南,轻松入门大模型开发
  • PotPlayer百度翻译插件:实现视频字幕实时多语言转换
  • 在线考试系统:防作弊与自动评分的实现技术
  • PHP全局使用局部变量+参数默认值+静态变量
  • Java八股文实践篇:从理论到实战,设计一个高并发AI图像生成服务
  • Qwen3.5-4B模型C语言代码审查与优化助手实战
  • RAG重排序(Rerank)入门基础教程(非常详细),收藏这一篇就够了!
  • Windows任务栏透明美化终极指南:TranslucentTB完整配置教程
  • Go语言的sync.Map.CompareAndDelete原子操作与条件
  • 哔哩下载姬技术解析:构建高效B站视频下载解决方案
  • Alibaba DASD-4B Thinking 对话工具解决“403 Forbidden”等API调用错误排查指南
  • “养虾”太贵劝退?华为云FlexNPU专治算力“吃空饷”
  • 手把手教你用ResNet-18镜像:无需代码,WebUI界面轻松识别1000种物体
  • Rust的匹配中的模式守卫优化与编译器在布尔表达式中
  • SpringBoot + 小程序实战:如何设计一个高可用的流浪动物救助系统后台?
  • 智慧树自动刷课插件完整指南:5分钟实现高效学习自动化
  • ClearerVoice-Studio企业级方案:基于SpringBoot的智能客服语音优化系统
  • HTML图片怎么在Firefox中调试对齐_Firefox开发者工具调图方法
  • DownKyi终极指南:3个高效技巧让你成为B站视频下载专家
  • OneAPI GPU显存优化:Ollama本地模型与云端模型混合调度策略
  • 比迪丽LoRA模型Keil5嵌入式开发趣味应用:为项目文档生成主题角色
  • 3分钟掌握电脑性能优化:开源工具UXTU终极指南