2025/12/8 每日总结 三大核心模块实现:故事生成+插图匹配+语音合成

三大核心模块实现:故事生成+插图匹配+语音合成

在上一篇博客中,我们完成了AI儿童故事平台的基础搭建,确定了数据模型与三大核心大模型选型。这一篇,我们聚焦技术落地——详细拆解故事生成、插图匹配、语音合成三大模块的具体实现,包括API调用、核心代码逻辑、异常处理与问题解决,让每个模块都能稳定跑通并精准联动。

一、故事生成模块:基于Kimi API的儿童故事创作

故事生成是平台的核心基础,目标是接收用户关键词,调用Kimi API生成符合6-12岁儿童认知的结构化故事(标题+梗概+正文)。

1. 核心准备:API配置与环境搭建

  • API密钥申请:访问Kimi API官方文档,注册账号后创建应用,获取API Key,并配置到.env文件中(KIMI_API_KEY=your_api_key)。

  • 依赖安装:核心依赖为requests(网络请求)、json(数据解析),直接通过pip install requests安装即可。

  • 配置文件关联:在config.py中读取环境变量,供kimi_api.py调用,确保密钥不硬编码,提升安全性。

    2. 核心代码实现:kimi_api.py

    (1)类初始化与基础配置

    import requests
    import json
    import os
    from config import KIMI_API_KEY
    class KimiAPI:
    def __init__(self):
    self.api_key = KIMI_API_KEY
    self.base_url = "https://api.moonshot.cn/v1" # Kimi API基础地址
    

    (2)提示词设计:精准约束儿童故事风格

    提示词是故事质量的关键,需明确风格、结构、长度与输出格式:

    def generate_story(self, keywords: str) -> dict:
    url = f"{self.base_url}/chat/completions"
    
    # 提示词:明确儿童故事要求+结构化输出约束
    
    prompt = f"""
    请根据以下关键词创作一个适合6-12岁儿童的精彩故事:
    关键词:{keywords}
    要求:
    
    
    
  1. 语言简单易懂,避免复杂词汇;

  2. 故事有完整起承转合,情节生动有趣;

  3. 长度控制在500-800字;

  4. 包含积极教育意义(如勇气、友谊、互助);

  5. 严格按照以下JSON格式返回,不添加任何额外文本:
    {{
    "title": "故事标题",
    "summary": "100字内梗概",
    "content": "完整故事内容"
    }}
    """

#### (3)API请求参数配置
```python
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}" # 身份认证
}
payload = {
"model": "moonshot-v1-8k", # 选用8k上下文模型,满足故事长度需求
"messages": [
{"role": "system", "content": "你是专业儿童故事作家,擅长想象力与教育意义结合的创作"},
{"role": "user", "content": prompt}
],
"temperature": 0.7, # 保留一定随机性,避免故事同质化
"max_tokens": 2000, # 足够覆盖800字故事+结构化字段
"response_format": {"type": "json_object"} # 强制返回JSON
}

(4)响应处理与异常兜底

针对网络超时、JSON解析失败等常见问题,设计容错机制:

try:
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status() # 抛出HTTP请求错误
result = response.json()
story_content = result.get("choices", [{}])[0].get("message", {}).get("content", "")
return self._parse_story_response(story_content, keywords)
except requests.exceptions.Timeout:
raise Exception("请求超时,请稍后重试")
except requests.exceptions.RequestException as e:
raise Exception(f"网络请求失败: {str(e)}")
except Exception as e:
raise Exception(f"故事生成失败: {str(e)}")
# 解析响应:JSON失败时手动提取信息
def _parse_story_response(self, story_content: str, keywords: str) -> dict:
try:
# 清理可能的Markdown格式(如```json包裹)
cleaned_content = story_content.strip().lstrip('```json').rstrip('```')
story_data = json.loads(cleaned_content)
# 验证必需字段
for field in ["title", "summary", "content"]:
if field not in story_data:
raise ValueError(f"缺少字段: {field}")
return story_data
except json.JSONDecodeError:
# 兜底方案:默认标题+截取梗概+原文内容
title = f"关于{keywords}的奇妙冒险"
content = story_content
summary = content[:100] + "..." if len(content) > 100 else content
return {"title": title, "summary": summary, "content": content}

3. 测试验证

调用generate_story("宇航员、小狗、月球"),成功返回结构化数据,故事符合儿童认知,长度与教育意义均达标,异常场景下能返回兜底结果,模块稳定性验证通过。

二、插图匹配模块:阿里云百炼API生成故事配图

插图模块需基于故事文本自动生成贴合意境的儿童插画,并与故事关联存储,核心挑战是“提示词精准度”与“资源管理”。

1. 核心准备:API配置与依赖

  • API密钥申请:登录阿里云百炼平台,创建API-Key,配置到.envALIYUN_ACCESS_KEY=your_key)。

  • 依赖安装pip install dashscope requests pillowdashscope是阿里云AI服务SDK)。

  • 存储目录创建:在项目根目录创建images文件夹,用于存储本地图片。

    2. 核心代码实现:image_api.py

    (1)类初始化与配置

    import requests
    import os
    import uuid
    import time
    import logging
    from dotenv import load_dotenv
    from dashscope import ImageSynthesis
    from http import HTTPStatus
    load_dotenv()
    logger = logging.getLogger(__name__)
    class ImageGenerationAPI:
    def __init__(self):
    self.images_dir = "images"
    os.makedirs(self.images_dir, exist_ok=True) # 确保目录存在
    self.api_config = {
    "aliyun": {
    "access_key": os.getenv("ALIYUN_ACCESS_KEY"),
    "model": "qwen-image-plus", # 儿童插画适配模型
    "size": "1024x1024" # 正方形配图,适配前端展示
    }
    }
    

    (2)提示词优化:从故事中提取核心元素

    为避免插图与故事脱节,设计自动提取关键元素的提示词生成逻辑:

    def generate_image_prompt(self, story_title: str, story_content: str, keywords: str) -> str:
    key_elements = []
    # 从关键词和正文中提取场景、角色、氛围
    if "魔法" in keywords or "魔法" in story_content:
    key_elements.append("魔法元素")
    if "森林" in keywords or "森林" in story_content:
    key_elements.append("森林场景")
    if any(animal in story_content for animal in ["小狗", "小猫", "小鹿"]):
    key_elements.append("可爱动物")
    if "冒险" in keywords or "冒险" in story_content:
    key_elements.append("冒险氛围")
    # 构建提示词
    prompt = f"儿童插画风格,{story_title},{','.join(key_elements)},色彩鲜艳,画面生动,适合儿童观看"
    return prompt if len(prompt) > 30 else prompt + ",充满童趣和想象力"
    

    (3)图片生成与本地存储

    def generate_image_from_text(self, prompt: str, story_title: str) -> dict:
    try:
    # 配置阿里云API密钥
    dashscope.api_key = self.api_config["aliyun"]["access_key"]
    # 调用文生图API
    response = ImageSynthesis.call(
    model=self.api_config["aliyun"]["model"],
    prompt=prompt,
    n=1,
    size=self.api_config["aliyun"]["size"]
    )
    if response.status_code != HTTPStatus.OK:
    raise Exception(f"API调用失败: {response.message}")
    # 下载图片
    image_url = response.output.results[0].url
    image_response = requests.get(image_url)
    image_response.raise_for_status()
    # 生成唯一文件名(避免重复)
    filename = self._generate_filename(story_title)
    image_path = os.path.join(self.images_dir, filename)
    # 保存图片到本地
    with open(image_path, "wb") as f:
    f.write(image_response.content)
    # 返回图片访问URL(适配前端静态文件服务)
    image_url = f"/images/{filename}"
    return {"image_url": image_url, "image_path": image_path, "status": "success"}
    except Exception as e:
    logger.error(f"图片生成失败: {str(e)}")
    return {"image_url": None, "status": "failed", "error": str(e)}
    # 生成唯一文件名:故事标题+时间戳+UUID
    def _generate_filename(self, story_title: str) -> str:
    safe_title = "".join(c for c in story_title if c.isalnum() or c in (' ', '-', '_')).replace(' ', '_')[:50]
    safe_title = safe_title if safe_title else "story"
    unique_id = str(uuid.uuid4())[:8]
    timestamp = int(time.time())
    return f"{safe_title}_{timestamp}_{unique_id}.png"
    

    3. 常见问题解决

  • 插图不贴合故事:通过generate_image_prompt自动提取故事核心元素,避免纯关键词提示的片面性;

  • 图片路径错误:采用相对路径存储,前端通过/images/文件名直接访问,确保前后端路径一致;

  • API调用失败:添加日志记录与状态返回,便于排查网络或密钥配置问题。

    三、语音合成模块:百度TTS生成故事朗读音频

    语音模块需将故事正文合成为MP3音频,支持长短文本适配,核心挑战是“文本长度限制”与“音频资源关联”。

    1. 核心准备:API配置与依赖

  • API密钥申请:登录百度AI开放平台,创建语音合成应用,获取API KeySecret Key,配置到.env

  • 依赖安装pip install requests(百度TTS无需额外SDK,直接通过HTTP调用)。

  • 存储目录创建:在项目根目录创建audio文件夹,用于存储音频文件。

    2. 核心代码实现:tts_api.py

    (1)类初始化与Token管理

    百度TTS需要通过API KeySecret Key获取临时访问Token,且Token有有效期,需自动刷新:

    import requests
    import os
    import uuid
    import time
    import logging
    from dotenv import load_dotenv
    load_dotenv()
    logger = logging.getLogger(__name__)
    class TextToSpeechAPI:
    def __init__(self):
    self.audio_dir = "audio"
    os.makedirs(self.audio_dir, exist_ok=True)
    self.api_config = {
    "baidu": {
    "api_key": os.getenv("BAIDU_API_KEY"),
    "secret_key": os.getenv("BAIDU_SECRET_KEY"),
    "access_token": None,
    "token_expire_time": 0 # Token过期时间
    }
    }
    

    (2)Token获取与刷新

    def _get_baidu_token(self, config: dict) -> str:
    now = time.time()
    # 若Token未过期,直接返回;否则重新获取
    if now < config["token_expire_time"] and config["access_token"]:
    return config["access_token"]
    # 调用Token获取接口
    token_url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={config['api_key']}&client_secret={config['secret_key']}"
    response = requests.get(token_url, timeout=30)
    response.raise_for_status()
    token_data = response.json()
    config["access_token"] = token_data["access_token"]
    # 提前5分钟刷新Token(避免过期)
    config["token_expire_time"] = now + token_data["expires_in"] - 300
    return config["access_token"]
    

    (3)音频生成与存储

    支持长短文本适配,短文本直接调用,长文本自动拆分(百度TTS短文本限制1024GBK字节):

    def generate_speech_from_text(self, text: str, story_title: str) -> dict:
    config = self.api_config["baidu"]
    token = self._get_baidu_token(config)
    tts_url = "https://tsn.baidu.com/text2audio"
    

配置音频参数(儿童友好音色)

params = {
"tex": text,
"tok": token,
"cuid": "story_platform", # 用户唯一标识
"ctp": "1", # 客户端类型(web端固定为1)
"lan": "zh", # 语言(中文)
"per": "0", # 音色(0=度小美,女声,适合儿童)
"spd": "5", # 语速(5为中速)
"vol": "5", # 音量(5为中音量)
"aue": "3" # 音频格式(3=MP3)
}

try:

调用语音合成API

response = requests.post(tts_url, data=params, timeout=60)
if response.status_code == 200 and 'audio' in response.headers.get('Content-Type', ''):

生成唯一文件名

filename = self._generate_filename(story_title)
audio_path = os.path.join(self.audio_dir, filename)

保存音频文件

with open(audio_path, "wb") as f:
f.write(response.content)

返回音频访问URL

audio_url = f"/audio/{filename}"
return {"audio_url": audio_url, "audio_path": audio_path, "status": "success"}
else:
error_msg = response.text if response.text else "未知错误"
raise Exception(f"音频生成失败: {error_msg}")
except Exception as e:
logger.error(f"语音合成失败: {str(e)}")
return {"audio_url": None, "status": "failed", "error": str(e)}

生成唯一音频文件名(同图片命名逻辑,保持一致性)

def generate_filename(self, story_title: str) -> str:
safe_title = "".join(c for c in story_title if c.isalnum() or c in (' ', '-', '
')).replace(' ', '')[:50]
safe_title = safe_title if safe_title else "audio"
unique_id = str(uuid.uuid4())[:8]
timestamp = int(time.time())
return f"{safe_title}
_{unique_id}.mp3"

### 3. 长短文本适配优化
针对百度TTS短文本长度限制,新增文本拆分逻辑(核心思路:按标点符号拆分,每段不超过120字):
```python
def _split_long_text(self, text: str) -> list:
 import re
 # 按句号、感叹号、问号拆分,避免拆分到完整语义
 sentences = re.split(r'([。!?])', text)
 chunks = []
 current_chunk = ""
 for sent in sentences:
 if len(current_chunk + sent) < 120: # 每段不超过120字(约1024GBK字节)
 current_chunk += sent
 else:
 if current_chunk:
 chunks.append(current_chunk)
 current_chunk = sent
 if current_chunk:
 chunks.append(current_chunk)
 return chunks

generate_speech_from_text中调用拆分逻辑,长文本分段合成后拼接(需引入pydub库处理音频拼接,pip install pydub)。

四、三大模块联动逻辑

单个模块跑通后,需实现“故事生成→插图生成→音频生成”的自动联动,核心逻辑如下:

# 主流程联动示例(后续整合到app.py)
def generate_story_package(keywords: str) -> dict:
 # 1. 生成故事文本
 kimi_api = KimiAPI()
 story_data = kimi_api.generate_story(keywords)

# 2. 生成配套插图(使用故事标题、正文、关键词)
 image_api = ImageGenerationAPI()
 image_prompt = image_api.generate_image_prompt(
 story_title=story_data["title"],
 story_content=story_data["content"],
 keywords=keywords
 )
 image_data = image_api.generate_image_from_text(image_prompt, story_data["title"])

# 3. 生成朗读音频
 tts_api = TextToSpeechAPI()
 audio_data = tts_api.generate_speech_from_text(story_data["content"], story_data["title"])

# 4. 整合结果(关联故事ID,存入数据库)
 return {
 "story": story_data,
 "image": image_data,
 "audio": audio_data,
 "keywords": keywords,
 "created_time": time.strftime("%Y-%m-%d %H:%M:%S")
 }

五、模块实现总结

三大核心模块的实现,关键在于“精准选型+细节容错+联动适配”:

  • 故事生成模块:通过结构化提示词与异常兜底,确保故事质量与稳定性;
  • 插图匹配模块:自动提取故事核心元素优化提示词,解决“图文脱节”问题;
  • 语音合成模块:处理Token过期与文本长度限制,适配不同篇幅故事。
posted @ 2026-01-06 04:15  Moonbeamsc  阅读(6)  评论(0)    收藏  举报
返回顶端