實驗一:AI故事生成平台
核心代碼
1. Story數據模型 (models/story.py)
import os
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
# 創建基類
Base = declarative_base()
class Story(Base):
"""
故事數據模型
用於存儲生成的兒童故事信息
"""
__tablename__ = 'stories'
# 主鍵
id = Column(Integer, primary_key=True, index=True)
# 故事標題
title = Column(String(255), nullable=False)
# 故事內容
content = Column(Text, nullable=False)
# 關鍵詞
keywords = Column(String(500))
# 目標年齡段
age_group = Column(String(50))
# 故事主題
theme = Column(String(100))
# 插圖URL
image_url = Column(String(500))
# 音頻URL
audio_url = Column(String(500))
# 是否收藏
is_favorite = Column(Boolean, default=False)
# 創建時間
created_at = Column(DateTime, default=datetime.utcnow)
# 更新時間
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def to_dict(self):
"""
將故事對象轉換為字典格式
便於JSON序列化和API響應
"""
return {
'id': self.id,
'title': self.title,
'content': self.content,
'keywords': self.keywords,
'age_group': self.age_group,
'theme': self.theme,
'image_url': self.image_url,
'audio_url': self.audio_url,
'is_favorite': self.is_favorite,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
2. 故事生成服務 (services/story_service.py)
import os
from dotenv import load_dotenv
from utils.aliyun_client import AliyunClient
# 加載環境變量
load_dotenv()
def generate_story(keywords, age_group, story_length, theme, voice_type=None):
"""
使用阿里雲百鍊大模型生成兒童故事
Args:
keywords: 關鍵詞列表,用於生成故事
age_group: 目標年齡段
story_length: 故事長度 (short, medium, long)
theme: 故事主題
voice_type: 聲音類型 (child, female, male)
Returns:
dict: 包含故事內容的字典
"""
# 構建提示詞
prompt = f"請為{age_group}歲的兒童創作一個有趣的童話故事。"
prompt += f"關鍵詞: {', '.join(keywords)}"
if theme:
prompt += f"主題: {theme}"
# 根據聲音類型調整故事風格
voice_style_map = {
'child': '用天真活潑、充滿童趣的語氣來講述,讓故事聽起來像小朋友在分享自己的經歷',
'female': '用温柔親切、富有母愛的語調來講述,讓故事充滿温暖和關懷',
'male': '用穩重有趣、略帶磁性的聲音來講述,讓故事既有趣味性又有安全感'
}
if voice_type and voice_type in voice_style_map:
prompt += f"\n{voice_style_map[voice_type]}"
# 根據故事長度設置字數要求
length_map = {
'short': '200-300字',
'medium': '400-600字',
'long': '800-1000字'
}
prompt += f"\n請確保故事字數控制在{length_map.get(story_length, '400-600字')}左右。"
prompt += "\n故事要有積極向上的價值觀,語言生動有趣,適合兒童閲讀。"
prompt += "\n請將故事內容分成適當的段落,每個段落圍繞一個小情節展開,讓閲讀體驗更好。"
prompt += "\n請提供一個吸引人的標題。"
print(f"📖 故事生成提示詞: {prompt}")
# 創建阿里雲客户端並調用API
client = AliyunClient()
try:
# 調用文本生成API
result = client.generate_text(prompt, model="qwen-max", temperature=0.7, max_tokens=1000)
print(f"📄 文本生成API響應: {result}")
# 提取故事內容
story_text = result.get('output', {}).get('text', '')
print(f"📖 提取的故事文本: '{story_text}'")
print(f"📏 故事文本長度: {len(story_text)}")
# 如果故事文本為空,返回錯誤
if not story_text or len(story_text.strip()) == 0:
raise Exception("文本生成API返回了空內容")
# 處理標題和正文,確保都不包含井號
lines = story_text.strip().split('\n')
title = lines[0].lstrip('# ') if lines else "童話故事"
# 處理內容,移除所有井號字符並在每段首行添加兩字符縮進
content_lines = lines[1:] if len(lines) > 1 else []
# 分組內容行到段落(空行分隔段落)
paragraphs = []
current_paragraph = []
for line in content_lines:
clean_line = line.replace('#', '')
if clean_line.strip():
current_paragraph.append(clean_line)
else:
if current_paragraph:
paragraphs.append('\n'.join(current_paragraph))
current_paragraph = []
# 添加最後一個段落
if current_paragraph:
paragraphs.append('\n'.join(current_paragraph))
# 為每個段落的首行添加兩字符縮進
indented_paragraphs = []
for paragraph in paragraphs:
if not paragraph:
continue
lines_in_paragraph = paragraph.split('\n')
if lines_in_paragraph:
# 首行添加縮進
lines_in_paragraph[0] = ' ' + lines_in_paragraph[0]
indented_paragraphs.append('\n'.join(lines_in_paragraph))
# 段落間用空行分隔
content = '\n\n'.join(indented_paragraphs)
# 處理特殊情況:如果沒有生成內容,使用備用方案
if not content.strip():
content = ' ' + story_text.replace('#', '') if story_text.strip() else ''
print(f"📖 最終標題: '{title}'")
print(f"📖 最終內容長度: {len(content)}")
return {
'title': title,
'content': content,
'keywords': keywords,
'age_group': age_group,
'story_length': story_length,
'theme': theme
}
except Exception as e:
print(f"❌ 故事生成異常: {e}")
raise Exception(f"生成故事時發生錯誤: {str(e)}")
3. 故事生成API路由 (routes/story_routes.py)
from flask import Blueprint, request, jsonify
from services.story_service import generate_story
# 創建故事相關的藍圖
bp = Blueprint('story', __name__)
# 生成故事的路由
@bp.route('/generate', methods=['POST'])
def generate_story_route():
try:
data = request.json
# 驗證必要的參數
if not data or 'keywords' not in data:
return jsonify({'error': '缺少必要的關鍵詞參數'}), 400
keywords = data['keywords']
age_group = data.get('age_group', '5-8') # 默認年齡段
story_length = data.get('story_length', 'medium') # 默認故事長度
theme = data.get('theme', '') # 可選主題
voice_type = data.get('voice_type', 'child') # 聲音類型
# 調用故事生成服務,傳入聲音類型
story_data = generate_story(keywords, age_group, story_length, theme, voice_type)
return jsonify(story_data), 200
except Exception as e:
return jsonify({'error': f'生成故事時發生錯誤: {str(e)}'}), 500
# 獲取所有故事的路由
@bp.route('/', methods=['GET'])
def get_all_stories():
try:
# 這裏將在實現故事管理功能時補充
return jsonify({'stories': []}), 200
except Exception as e:
return jsonify({'error': f'獲取故事列表時發生錯誤: {str(e)}'}), 500
# 獲取單個故事的路由
@bp.route('/<story_id>', methods=['GET'])
def get_story(story_id):
try:
# 這裏將在實現故事管理功能時補充
return jsonify({'error': '故事不存在'}), 404
except Exception as e:
return jsonify({'error': f'獲取故事時發生錯誤: {str(e)}'}), 500
功能説明
Story數據模型
- 核心字段:id、title、content、keywords、age_group、theme、created_at等
- 擴展字段:image_url、audio_url(用於與後續實驗集成)
- 方法:to_dict() 用於對象序列化
故事生成服務
- 核心功能:使用阿里雲百鍊大模型API生成兒童故事
- 參數處理:接收關鍵詞、年齡段、故事長度、主題等參數
- 提示詞工程:根據參數動態構建提示詞,支持不同聲音類型的風格調整
- 文本處理:處理API返回結果,提取標題和內容,移除特殊字符,添加段落縮進
- 錯誤處理:完善的異常捕獲和錯誤信息返回
API路由
- 生成故事:POST /story/generate,接收故事生成參數
- 參數驗證:驗證必要參數,設置合理默認值
- 錯誤響應:統一的錯誤處理和狀態碼返回
技術棧
- Python 3.12
- Flask Web框架
- SQLAlchemy ORM
- 阿里雲百鍊大模型API
使用方法
- 確保配置了阿里雲API密鑰(在.env文件中)
- 調用 /story/generate 端點,傳入必要的故事生成參數
- API返回生成的故事標題、內容和相關信息
後續擴展
- 故事保存功能
- 故事列表和詳情查詢
- 與插圖生成和語音合成服務集成
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。