给小朋友的 AI 绘本创作工具设计手记:让每个孩子都能成为故事的主角
给小朋友的 AI 绘本创作工具设计手记:让每个孩子都能成为故事的主角
一、为什么每个孩子都需要一个"会讲故事的 AI"?
傍晚六点半,夕阳把客厅染成暖橙色。白泠钰正蹲在地上,陪五岁的侄女小豆芽玩耍。小豆芽举着一本绘本跑过来:"小姨小姨,给我讲这个故事嘛!"白泠钰翻开书,发现是个关于小兔子冒险的故事——"可是小豆芽,故事里没有你呀。"
小豆芽眨眨眼睛:"那让小兔子去找一个小女孩,她叫小豆芽!"
那一刻,白泠钰突然萌生了一个温暖的想法:能不能用 AI,让每个孩子都成为自己故事里的主角?这个念头最终孵化成一个产品——AI 绘本创作工具,它能够根据孩子的描述,生成图文并茂的专属绘本故事。
儿童绘本创作看似简单,实则面临诸多独特挑战:如何理解孩子天马行空的描述?如何生成安全、适龄的内容?如何让 AI 生成的故事既有教育意义又有趣味性?更重要的是,如何保护儿童的隐私和安全?
本文将详细拆解 AI 绘本创作工具的设计思路与核心技术实现。
二、理解童言童语:儿童语言处理的特殊挑战
2.1 儿童语言的特征分析
与成人对话不同,儿童的语言表达有几个显著特征:
- 逻辑跳跃:可能上一句在说"大象",下一句跳到"我要吃冰淇淋"
- 语法不完整:经常出现主谓宾残缺、断句不清的情况
- 想象力丰富:会用成人难以理解的逻辑连接事物
- 情绪化表达:高兴时语速飞快,难过时可能哭诉不清
针对这些特点,白泠钰设计了一套"儿童语言理解管道":
from dataclasses import dataclass, field from typing import List, Dict, Optional, Set import re @dataclass class ChildUtterance: """儿童话语""" original_text: str tokens: List[str] entities: List[str] # 提取的实体(角色、物品) actions: List[str] # 动作描述 emotions: List[str] # 情绪关键词 relationships: Dict[str, str] # 关系映射 {角色A: 角色B: 关系} language_age_estimate: int # 语言年龄评估(岁) clarity_score: float # 清晰度评分 0-1 class ChildLanguageProcessor: """儿童语言处理器""" def __init__(self): # 儿童常用词汇表(按年龄段) self.vocabulary_by_age = self._load_vocabulary() # 角色类型词典 self.character_types = { 'animal': ['小狗', '小猫', '小兔子', '小熊', '小鸟', '小鱼', '大象', '老虎', '狮子'], 'fantasy': ['公主', '王子', '魔法师', '小精灵', '仙女', '龙', '独角兽'], 'object': ['机器人', '汽车', '飞机', '火车', '魔法棒', '城堡'], 'family': ['妈妈', '爸爸', '爷爷', '奶奶', '哥哥', '姐姐', '弟弟', '妹妹'], 'friend': ['好朋友', '小伙伴', '同学', '邻居'] } # 情绪词汇映射 self.emotion_words = { '开心': ['开心', '高兴', '快乐', '笑', '快乐', '最喜欢', '好开心'], '难过': ['伤心', '难过', '哭', '不高兴', '不开心', '生气'], '害怕': ['害怕', '怕黑', '怕高', '胆小', '担心'], '好奇': ['为什么', '想知道', '奇怪', '猜猜', '什么'] } # 安全过滤词库 self.safety_filter = self._load_safety_filter() def process(self, text: str) -> ChildUtterance: """ 处理儿童话语 Args: text: 原始输入 Returns: 结构化的儿童话语对象 """ # 分词 tokens = self._tokenize(text) # 实体识别 entities = self._extract_entities(tokens) # 动作识别 actions = self._extract_actions(tokens) # 情绪识别 emotions = self._extract_emotions(text) # 关系提取 relationships = self._extract_relationships(tokens) # 语言年龄评估 language_age = self._estimate_language_age(tokens) # 清晰度评估 clarity = self._assess_clarity(text, tokens, entities) return ChildUtterance( original_text=text, tokens=tokens, entities=entities, actions=actions, emotions=emotions, relationships=relationships, language_age_estimate=language_age, clarity_score=clarity ) def _tokenize(self, text: str) -> List[str]: """简单分词""" # 移除标点符号,按空格分词 text = re.sub(r'[,。!?、:;""''【】()]', ' ', text) return text.split() def _extract_entities(self, tokens: List[str]) -> List[str]: """提取实体""" entities = [] for token in tokens: for category, words in self.character_types.items(): if token in words: entities.append(token) break return list(set(entities)) def _extract_actions(self, tokens: List[str]) -> List[str]: """提取动作""" action_verbs = [ '去', '走', '跑', '跳', '飞', '游泳', '唱歌', '跳舞', '找', '遇到', '帮助', '救', '探险', '冒险', '发现', '吃', '喝', '睡觉', '起床', '回家', '上学', '玩耍', '变成', '变出', '施魔法', '说', '告诉', '问', '想' ] actions = [t for t in tokens if t in action_verbs] return actions def _extract_emotions(self, text: str) -> List[str]: """提取情绪""" emotions = [] for emotion, keywords in self.emotion_words.items(): for keyword in keywords: if keyword in text: emotions.append(emotion) break return list(set(emotions)) def _extract_relationships(self, tokens: List[str]) -> Dict[str, str]: """提取关系""" relationships = {} relationship_words = { '的朋友': 'friend', '的妈妈': 'mother', '的爸爸': 'father', '和': 'companion', '一起': 'companion', '喜欢': 'like' } text = ''.join(tokens) for pattern, rel_type in relationship_words.items(): if pattern in text: # 简化处理:记录关系类型 relationships[rel_type] = rel_type return relationships def _estimate_language_age(self, tokens: List[str]) -> int: """评估语言年龄""" # 简单启发式评估 if len(tokens) <= 5: return 3 elif len(tokens) <= 10: return 4 elif len(tokens) <= 20: return 5 elif len(tokens) <= 40: return 6 else: return 7 def _assess_clarity(self, text: str, tokens: List[str], entities: List[str]) -> float: """评估话语清晰度""" score = 0.5 # 实体越多越清晰 if len(entities) >= 1: score += 0.2 # 有明确动作 if any(k in text for k in ['去', '做', '变', '找']): score += 0.15 # 句子完整(有主语和谓语) if any(k in text for k in ['我想', '我要', '有一只']): score += 0.15 return min(score, 1.0)2.2 故事生成的安全性考量
儿童内容的安全是重中之重。白泠钰在系统中加入了多层安全过滤:
flowchart TD A[用户输入] --> B{内容安全检查} B -->|通过| C[角色安全检查] B -->|不通过| D[温和拒绝并引导] C -->|通过| E[情节合理性检查] C -->|不通过| E E -->|通过| F[生成故事] E -->|不通过| G[情节调整建议] F --> H{图片生成安全检查} H -->|通过| I[最终输出] H -->|不通过| J[图片重新生成] J --> Hclass ContentSafetyChecker: """内容安全检查器""" def __init__(self): # 禁止内容词库 self.banned_words = self._load_banned_words() # 适龄内容规则 self.age_appropriate_rules = self._load_age_rules() # 敏感主题检测 self.sensitive_topics = [ '死亡', '自杀', '暴力', '武器', '血液', '性', '裸露', '赌博', '毒品', '酒精' ] def check(self, text: str, age_range: tuple = (3, 8)) -> Dict: """ 检查内容安全性 Returns: { 'is_safe': bool, 'issues': List[str], 'suggestions': List[str] } """ issues = [] suggestions = [] # 检查禁用词 for word in self.banned_words: if word in text: issues.append(f"包含不适当内容: {word}") suggestions.append("试试换个角色或故事背景吧") # 检查敏感主题 for topic in self.sensitive_topics: if topic in text: issues.append(f"涉及敏感主题: {topic}") suggestions.append("这个话题可能不太适合这个年龄段的小朋友哦") # 年龄适性检查 age_text = self._check_age_appropriateness(text, age_range) if age_text: issues.append(age_text['issue']) suggestions.append(age_text['suggestion']) return { 'is_safe': len(issues) == 0, 'issues': issues, 'suggestions': suggestions } def _check_age_appropriateness( self, text: str, age_range: tuple ) -> Optional[Dict]: """检查年龄适性""" # 3-5岁:简单情节、可爱角色、正向主题 # 6-8岁:可以有一些小困难、冒险元素 if age_range[1] <= 5: # 检查是否太复杂 if any(word in text for word in ['打败', '危险', '恐怖', '受伤']): return { 'issue': '可能对小朋友来说有点吓人', 'suggestion': '试试用'帮助朋友'代替'打败敌人'这样的情节' } return None def generate_safe_response(self, check_result: Dict) -> str: """生成安全的拒绝/引导响应""" if check_result['is_safe']: return "" # 选择最友好的建议 suggestion = check_result['suggestions'][0] if check_result['suggestions'] else "" responses = [ f"哎呀,这个故事可能不太适合小朋友听呢。{suggestion}", f"小主人公可能遇到了一点小麻烦,{suggestion}", f"故事里的情节有点太复杂了,{suggestion}" ] import random return random.choice(responses)三、角色一致性:让 AI 记住"小豆芽的大象朋友"
3.1 跨模态角色追踪系统
在绘本故事中,角色的外观、性格、行为需要保持一致。白泠钰设计了一个角色追踪系统,确保在多轮对话中角色特征不丢失:
from dataclasses import dataclass from typing import Dict, List, Optional import json @dataclass class CharacterProfile: """角色档案""" name: str species: str # 物种 appearance: Dict[str, str] # 外观描述 personality: List[str] # 性格特点 color: str # 主要颜色 special_features: List[str] # 特殊特征(如:有一条彩虹尾巴) relationships: Dict[str, str] # 与其他角色的关系 story_count: int = 0 # 出现次数 def to_image_prompt(self, style: str = "children_book") -> str: """生成用于图片生成的提示词""" features = ", ".join(self.special_features) appearance_desc = ", ".join([ f"{k}: {v}" for k, v in self.appearance.items() ]) # 儿童绘本风格提示词模板 prompt = f""" {self.species} named {self.name}, {features}, {appearance_desc}, colorful, cute, friendly expression, children's book illustration style, soft colors, adorable, heartwarming, high quality, detailed, clean background """ return prompt class CharacterTracker: """角色追踪器""" def __init__(self): # 全局角色库 self.global_characters: Dict[str, CharacterProfile] = {} # 当前故事角色 self.story_characters: Dict[str, CharacterProfile] = {} # 用户角色偏好 self.user_preferences: Dict[str, List[str]] = {} def register_character( self, character: CharacterProfile, scope: str = "story" ): """注册角色""" if scope == "story": self.story_characters[character.name] = character else: self.global_characters[character.name] = character def get_character(self, name: str) -> Optional[CharacterProfile]: """获取角色信息""" # 优先从故事角色中获取 if name in self.story_characters: return self.story_characters[name] # 从全局角色中获取 if name in self.global_characters: return self.global_characters[name] return None def find_similar_character(self, description: str) -> Optional[CharacterProfile]: """根据描述查找相似角色""" # 简化的相似度匹配 for char in list(self.story_characters.values()) + list(self.global_characters.values()): # 检查外观描述是否匹配 if any(desc in description for desc in char.special_features): return char # 检查物种是否匹配 if char.species in description: return char return None def update_from_user_input( self, utterance: ChildUtterance, story_context: Dict ) -> List[CharacterProfile]: """ 从用户输入中提取并更新角色信息 Args: utterance: 处理后的儿童话语 story_context: 故事上下文 Returns: 新识别或更新的角色列表 """ new_characters = [] for entity in utterance.entities: # 检查是否已存在 existing = self.get_character(entity) if existing: # 更新现有角色 existing.story_count += 1 else: # 创建新角色 new_char = self._create_character_from_entity( entity, utterance, story_context ) self.register_character(new_char) new_characters.append(new_char) return new_characters def _create_character_from_entity( self, entity: str, utterance: ChildUtterance, context: Dict ) -> CharacterProfile: """从实体创建角色档案""" # 根据实体推断基本信息 species = self._infer_species(entity) # 提取或推断外观 appearance = self._infer_appearance(entity, utterance) # 推断性格 personality = self._infer_personality(utterance) # 推断颜色 color = self._infer_color(utterance) return CharacterProfile( name=entity, species=species, appearance=appearance, personality=personality, color=color, special_features=[], relationships={} ) def _infer_species(self, entity: str) -> str: """推断物种""" animal_keywords = { '兔子': 'rabbit', '小狗': 'dog', '小猫': 'cat', '小熊': 'bear', '小鸟': 'bird', '小鱼': 'fish', '大象': 'elephant', '老虎': 'tiger', '狮子': 'lion', '公主': 'princess', '王子': 'prince', '精灵': 'fairy' } for keyword, species in animal_keywords.items(): if keyword in entity: return species return 'fantasy_creature' def _infer_appearance(self, entity: str, utterance: ChildUtterance) -> Dict[str, str]: """推断外观""" appearance = {'expression': 'friendly'} # 根据情绪推断表情 if '开心' in utterance.emotions: appearance['expression'] = 'happy' elif '难过' in utterance.emotions: appearance['expression'] = 'sad' return appearance def _infer_personality(self, utterance: ChildUtterance) -> List[str]: """推断性格""" # 简化处理 return ['friendly', 'kind'] def _infer_color(self, utterance: ChildUtterance) -> str: """推断颜色""" color_keywords = ['粉色', '蓝色', '绿色', '黄色', '彩虹'] for color in color_keywords: if color in utterance.original_text: return color return 'colorful'四、从故事到绘本:多模态内容生成
4.1 图文生成的协同策略
sequenceDiagram participant User as 小朋友 participant NLP as 语言理解 participant Story as 故事生成 participant Image as 图片生成 participant Layout as 版面编排 participant Output as 绘本输出 User->>NLP: 描述故事元素 NLP->>Story: 结构化故事元素 Story->>Story: 生成故事情节 Story->>Image: 角色描述 + 场景 Image->>Layout: 生成图片 Story->>Layout: 故事文本 Layout->>Output: 排版优化 Output->>User: 可爱的小绘本4.2 适龄内容生成策略
class AgeAppropriateStoryGenerator: """适龄故事生成器""" def __init__(self): # 各年龄段的故事模板 self.story_templates = { 3: { 'length': 5, # 5句话 'sentences_per_page': 1, 'conflict_level': 'minimal', 'themes': ['友情', '分享', '探索', '家庭'], 'forbidden': ['死亡', '严重冲突', '恐惧'] }, 5: { 'length': 8, 'sentences_per_page': 1-2, 'conflict_level': 'mild', 'themes': ['勇气', '解决问题', '成长', '帮助他人'], 'forbidden': ['暴力', '恐怖'] }, 7: { 'length': 12, 'sentences_per_page': 2, 'conflict_level': 'moderate', 'themes': ['冒险', '团队合作', '坚持', '自我发现'], 'forbidden': [] } } # 正向主题词汇 self.positive_themes = { 'friendship': ['帮助', '分享', '一起', '合作', '关心'], 'growth': ['学习', '练习', '努力', '进步', '学会'], 'kindness': ['善良', '温柔', '友好', '爱护', '照顾'], 'courage': ['勇敢', '不怕', '尝试', '挑战', '面对'] } def generate_story( self, user_input: ChildUtterance, main_character: CharacterProfile, age: int ) -> Dict: """ 生成适龄故事 Returns: { 'pages': List[Page], 'characters': List[CharacterProfile], 'theme': str } """ template = self._get_template_for_age(age) # 确定故事主题 theme = self._determine_theme(user_input, template) # 生成故事结构 story_structure = self._generate_structure( main_character, user_input, template, theme ) # 生成分页内容 pages = self._create_pages(story_structure, template) return { 'pages': pages, 'characters': self._get_involved_characters(user_input), 'theme': theme } def _get_template_for_age(self, age: int) -> Dict: """获取年龄对应的模板""" if age <= 4: return self.story_templates[3] elif age <= 6: return self.story_templates[5] else: return self.story_templates[7] def _determine_theme( self, input: ChildUtterance, template: Dict ) -> str: """确定故事主题""" # 优先使用用户输入中的情绪关联主题 emotion_theme_map = { '开心': 'friendship', '难过': 'growth', '害怕': 'courage', '好奇': 'exploration' } for emotion, theme in emotion_theme_map.items(): if emotion in input.emotions: return theme # 默认使用第一个允许的主题 return template['themes'][0] def _generate_structure( self, character: CharacterProfile, input: ChildUtterance, template: Dict, theme: str ) -> List[Dict]: """生成故事结构""" structure = [] # 开端:介绍角色和环境 structure.append({ 'type': 'introduction', 'content': f"{character.name}住在{self._random_setting()},{self._positive_action(theme)}。" }) # 发展:引入小困难或小挑战 if template['conflict_level'] != 'minimal': structure.append({ 'type': 'challenge', 'content': f"有一天,{character.name}遇到了一个小问题——{self._age_appropriate_challenge(template)}。" }) # 高潮:角色如何解决问题 structure.append({ 'type': 'resolution', 'content': f"勇敢的{character.name}{self._theme_action(theme)},{self._positive_outcome(theme)}。" }) # 结尾:温馨的结局 structure.append({ 'type': 'ending', 'content': f"从那以后,{character.name}更加{self._positive_growth(theme)},大家都很开心!" }) return structure def _age_appropriate_challenge(self, template: Dict) -> str: """生成适龄的挑战""" challenges = { 'minimal': [ '想要找到最好的朋友一起玩', '想学一个很棒的技能', '想帮妈妈做一件特别的事' ], 'mild': [ '遇到了一个小困难,需要想办法', '想要完成一个有点难的任务', '需要朋友的帮助才能成功' ], 'moderate': [ '需要鼓起勇气去尝试新事物', '面对选择,需要思考什么是对的', '遇到挑战,需要和朋友合作解决' ] } import random return random.choice(challenges[template['conflict_level']]) def _positive_action(self, theme: str) -> str: """生成正向动作""" actions = { 'friendship': '每天和朋友们一起玩耍', 'growth': '正在学习新本领', 'kindness': '温柔地对待每一个人', 'courage': '勇敢地探索世界' } return actions.get(theme, '快乐地生活着')五、总结
看着小豆芽抱着刚生成的绘本,眼睛亮晶晶地指着故事里的小豆芽说"这是我!"的那一刻,白泠钰觉得所有的技术挑战都值得了。AI 绘本创作工具不仅仅是生成一张图、一段文字,它是在为每个孩子创造一个"我是主角"的小小世界。
在设计这个产品的过程中,白泠钰始终遵循一个核心原则:技术越强大,越要保持童心。当 AI 能够生成越来越逼真的图片、写越来越流畅的文字时,我们更应该思考:这些技术如何才能真正服务于孩子们的成长,而不是取代父母讲故事时的那种温暖。
未来的 AI 绘本工具,或许能够记住孩子成长过程中的每一个重要时刻,生成越来越个性化、越来越有意义的成长相册。那时候,每个孩子都将拥有专属于自己的"故事时光机"——记录他们的想象力、记录他们的成长、记录那些被 AI 和父母共同守护的童年魔法时刻。
