2025.10.20实验一_AI故事生成平台

实验一: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

使用方法

  1. 确保配置了阿里云API密钥(在.env文件中)
  2. 调用 /story/generate 端点,传入必要的故事生成参数
  3. API返回生成的故事标题、内容和相关信息

后续扩展

  • 故事保存功能
  • 故事列表和详情查询
  • 与插图生成和语音合成服务集成
posted @ 2025-12-28 23:15  ysd666  阅读(12)  评论(0)    收藏  举报