从零构建图书创作与二次文创 AI Agent 系统:技术分享
·
从零构建图书创作与二次文创 AI Agent 系统:技术分享
从零构建图书创作与二次文创 AI Agent 系统:技术分享
一、项目背景
在 AIGC 时代,如何让大模型成为作家的“智能助手”,而不仅仅是一个聊天机器人?本文介绍一个基于 DeepSeek API 的图书创作 + 二次文创 AI Agent 系统,实现从世界观搭建、人物设定、大纲生成到章节续写的全链路创作能力,同时支持基于原著的同人番外、短剧脚本等衍生内容开发。
二、技术选型
| 层级 | 技术 | 说明 |
|---|---|---|
| 后端 | Python 3 + Flask | 轻量 Web 框架,提供 API 和页面渲染 |
| 前端 | 原生 HTML/CSS/JS + marked.js | 单页面应用,Markdown 实时渲染 |
| AI | DeepSeek v4-flash | 通过 OpenAI 兼容协议调用 |
| 通信 | fetch + SSE (Server-Sent Events) | 流式生成,实时展示 |
| 存储 | JSON 文件 | 模板、历史记录持久化,零依赖 |
| 部署 | Docker + docker-compose | 一键容器化部署 |
三、系统架构
┌─────────────┐ HTTP/SSE ┌──────────────┐ Proxy ┌─────────────────┐
│ Browser │ ◄──────────────► │ Flask App │ ◄─────────► │ DeepSeek API │
│ (HTML+JS) │ │ (app.py) │ │ (v4-flash) │
└─────────────┘ └──────┬───────┘ └─────────────────┘
│
▼
┌──────────────┐
│ JSON Storage │
│ data/ │
└──────────────┘
核心设计原则:
- API Key 不暴露:所有大模型请求经后端代理,前端只调内部接口
- 流式输出:长文本生成采用 SSE,用户可实时看到生成过程
- 上下文保持:每次请求携带完整对话历史,保证内容连贯
- 数据持久化:JSON 文件存储,重启不丢失
四、核心模块详解
1. Prompt 工程:动态构建系统提示词
app.py 中的 build_system_prompt() 函数将用户选择的参数拼接为结构化 Prompt:
def build_system_prompt(params):
genre = params.get("genre", "")
style = params.get("style", "")
perspective = params.get("perspective", "")
stage = params.get("stage", "full")
custom = params.get("custom", "")
# 体裁角色定义
genre_roles = {
"短文": "你是一位优秀的短篇作家...",
"长篇小说": "你是一位长篇小说作家...",
"故事脚本": "你是一位编剧...",
}
# 文风描述
style_descs = {
"情感细腻": "文字风格要情感细腻...",
"爽文风格": "节奏明快,爽感十足...",
"悬疑紧张": "营造紧张悬疑的氛围...",
}
parts = []
parts.append(genre_roles.get(genre, "你是一位专业的创作助手。"))
if style in style_descs:
parts.append(style_descs[style])
if perspective == "第一人称":
parts.append('使用第一人称叙事,以"我"的视角展开故事。')
# 创作阶段指令
stage_instructions = {
"worldbuilding": "你现在处于【世界观搭建】阶段...",
"characters": "你现在处于【人物设定】阶段...",
"outline": "你现在处于【大纲生成】阶段...",
"continue": "你现在处于【章节续写】阶段...",
"polish": "你现在处于【内容润色】阶段...",
}
if stage in stage_instructions:
parts.append(stage_instructions[stage])
if custom:
parts.append(f"额外要求:{custom}")
return "\n".join(parts)
关键点:
- 通过字典映射将用户下拉框选择转换为专业指令
- 支持"创作阶段"切换,同一主题在不同阶段获得不同指导
- 自定义指令追加到末尾,灵活扩展
2. 流式生成:SSE 实时推送
传统 API 调用需要等待完整响应,对于长文本体验极差。本系统采用 Server-Sent Events 实现流式输出:
@app.route('/api/generate/stream', methods=['POST'])
def generate_stream():
data = request.json
system_prompt = build_system_prompt(data.get('params', {}))
user_input = data.get('user_input', '')
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_input}
]
def event_stream():
full_response = ""
token_count = 0
try:
resp = make_llm_request(messages, stream=True, temperature=0.8)
for line in resp.iter_lines(decode_unicode=True):
if not line or not line.startswith("data: "):
continue
data_str = line[6:].strip()
if data_str == "[DONE]":
break
try:
chunk = json.loads(data_str)
content = chunk["choices"][0].get("delta", {}).get("content", "")
if content:
full_response += content
token_count += 1
yield f"data: {json.dumps({'content': content, 'count': token_count})}\n\n"
except:
continue
# 保存历史
save_history(project_id, system_prompt, user_input, full_response, token_count)
yield f"data: {json.dumps({'done': True, 'project_id': project_id})}\n\n"
except Exception as e:
yield f"data: {json.dumps({'error': str(e)})}\n\n"
return Response(
stream_with_context(event_stream()),
mimetype="text/event-stream",
headers={"Cache-Control": "no-cache"}
)
前端接收逻辑:
function callSSE(url, body, onChunk, onDone, onError) {
abortController = new AbortController();
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
signal: abortController.signal
}).then(response => {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
function read() {
reader.read().then(({ done, value }) => {
if (done) { onDone && onDone(); return; }
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
if (data.error) { onError(data.error); return; }
if (data.done) { onDone(data); return; }
onChunk(data); // 逐字追加显示
} catch (e) {}
}
}
read();
});
}
read();
});
}
3. 模板系统:内置 + 自定义
模板存储在 data/templates_default.json 和 data/templates_custom.json,结构如下:
{
"小说": {
"都市爽文": {
"system": "你是一位网文大神级作家。请创作一部都市爽文...",
"user_template": "题材:{theme}\n文风:{style}\n",
"params": {"文风": "爽文风格", "篇幅": "3000字开篇"}
}
}
}
套用流程:
- 前端点击"套用"按钮
- 自动填充右侧参数面板(文风、篇幅等)
- 将
user_template填入输入框,用户只需替换{theme}占位符 - 点击生成即可
保存自定义模板:
@app.route('/api/templates/save', methods=['POST'])
def save_template():
data = request.json
name = data.get('name')
category = data.get('category', '自定义')
config = data.get('config')
with open(CUSTOM_TEMPLATE_FILE, "r", encoding="utf-8") as f:
customs = json.load(f)
if category not in customs:
customs[category] = {}
customs[category][name] = config
with open(CUSTOM_TEMPLATE_FILE, "w", encoding="utf-8") as f:
json.dump(customs, f, ensure_ascii=False, indent=2)
return jsonify({"status": "ok"})
4. 二次文创:元素提取 + 定向生成
步骤 1:提取原文核心元素
@app.route('/api/derivative/extract', methods=['POST'])
def extract_elements():
text = request.json.get('text', '')
messages = [
{"role": "system", "content": """你是一个专业的文本分析专家。请提取以下字段:
- characters: 人物列表
- scenes: 场景列表
- plot_points: 关键剧情点
- quotes: 经典台词
- worldview: 世界观要素
仅返回JSON,不要额外解释。"""},
{"role": "user", "content": f"原文:\n{text[:8000]}"}
]
resp = make_llm_request(messages, stream=False, temperature=0.3)
result = resp.json()["choices"][0]["message"]["content"]
# 清理可能的 markdown 代码块标记
cleaned = re.sub(r'^```\w*\n?', '', result.strip())
cleaned = re.sub(r'\n?```$', '', cleaned)
elements = json.loads(cleaned)
return jsonify({"elements": elements})
步骤 2:基于方向生成衍生内容
DERIVATIVE_DIRECTIONS = {
"同人番外": "请基于原文的人物和世界观,创作一篇同人番外故事...",
"剧情改写": "请基于原文的故事框架,进行剧情改写...",
"结局重写": "请为原文重新设计一个结局...",
"短剧脚本": "请将原文改编为短剧脚本格式...",
"有声书台词": "请将原文改编为有声书台词版本...",
}
@app.route('/api/derivative/generate/stream', methods=['POST'])
def derivative_generate_stream():
direction = data.get('direction', '同人番外')
direction_prompt = DERIVATIVE_DIRECTIONS.get(direction)
system_prompt = f"你是一位专业的文创开发专家。{direction_prompt}"
if custom_instruction:
system_prompt += f"\n额外要求:{custom_instruction}"
user_content = f"原文内容:\n{original_text[:6000]}"
if elements:
user_content += f"\n\n已提取的核心元素:\n{json.dumps(elements)}"
# ... 后续流式生成逻辑同上
5. 速率限制与 Token 统计
简易速率限制(防滥用):
_rate_records = {} # {ip: [timestamps]}
RATE_LIMIT = 30 # 每分钟最多 30 次
RATE_WINDOW = 60 # 窗口 60 秒
def check_rate_limit(client_ip):
now = time.time()
records = _rate_records.get(client_ip, [])
records = [t for t in records if now - t < RATE_WINDOW]
if len(records) >= RATE_LIMIT:
return False
records.append(now)
_rate_records[client_ip] = records
return True
在每个 API 入口调用:
client_ip = request.remote_addr
if not check_rate_limit(client_ip):
return jsonify({"error": "请求过于频繁,请稍后再试"}), 429
Token 统计:
_token_stats = {"total_input": 0, "total_output": 0, "requests": 0}
# 每次生成完成后累加
_token_stats["total_output"] += token_count
_token_stats["requests"] += 1
@app.route('/api/stats')
def get_stats():
return jsonify(_token_stats)
前端左下角实时展示今日消耗。
五、前端交互设计
1. 三栏布局
┌──────────┬──────────────────────────┬──────────────┐
│ 左侧导航 │ 中央编辑区 │ 右侧参数面板 │
│ │ │ │
│ ✏ 创作 │ [输入框] │ 创作主题 │
│ 📋 模板 │ [生成结果 Markdown] │ 体裁下拉 │
│ 🔄 文创 │ │ 文风下拉 │
│ 📁 历史 │ │ 篇幅下拉 │
│ │ │ 自定义指令 │
│ │ │ [保存模板] │
└──────────┴──────────────────────────┴──────────────┘
2. 创作阶段导航
用户在"完整创作 / 世界观搭建 / 人物设定 / 大纲生成 / 章节续写 / 内容润色"之间切换,后端根据当前阶段调整 System Prompt。
3. 选中段落定向改写
- 用户在生成结果中选中一段文字
- 自动弹出"改写"按钮
- 输入指令(如"让描写更生动")
- 后端调用
/api/generate/rewrite,返回改写后内容并替换原文
六、Docker 部署
Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN mkdir -p data/history data/user_templates uploads
EXPOSE 5000
CMD ["python", "app.py"]
docker-compose.yml:
version: '3.8'
services:
book-ai-agent:
build: .
container_name: book-ai-agent
ports:
- "5000:5000"
volumes:
- ./data:/app/data
- ./uploads:/app/uploads
environment:
- TZ=Asia/Shanghai
restart: unless-stopped
启动命令:
docker-compose up -d --build
访问 http://localhost:5000 即可使用。
七、关键技术总结
| 技术点 | 实现方式 | 价值 |
|---|---|---|
| Prompt 动态拼接 | 参数字典映射 + 阶段指令 | 让非技术人员也能生成高质量 Prompt |
| SSE 流式输出 | Flask stream_with_context + 前端 ReadableStream |
长文本生成体验提升 10 倍 |
| 模板系统 | JSON 文件存储 + 前端卡片展示 | 降低使用门槛,沉淀最佳实践 |
| 二次文创 | 两阶段处理(提取→生成) | 结构化理解原文,衍生更精准 |
| 速率限制 | 基于 IP 的时间窗口计数 | 防止 API 滥用,控制成本 |
| Docker 化 | 多阶段构建 + Volume 挂载 | 一键部署,数据持久化 |
八、源码地址
本项目完整代码已开源,包含:
app.py:Flask 后端(730 行)templates/index.html:前端单页面(988 行)Dockerfile+docker-compose.yml:容器化配置requirements.txt:Python 依赖
GitHub:
九、后续优化方向
- 向量数据库集成:引入 Chroma/Pinecone,实现长文档语义检索
- 多模型路由:根据任务复杂度自动选择 deepseek-v4-flash / deepseek-chat
- 协作功能:支持多人同时编辑同一项目,实时同步
- 移动端适配:响应式布局,支持手机端创作
- 插件系统:允许开发者扩展新的文创方向或模板类型
结语: 本系统展示了如何将大模型 API、Prompt 工程、Web 开发有机结合,打造一个真正可用的 AI 辅助创作工具。希望对你有所启发!
更多推荐



所有评论(0)