#!/usr/bin/env python3 """ 人物档案生成脚本 根据模板生成结构化的人物档案markdown文件 """ import os import sys import json from datetime import datetime from pathlib import Path import logging from typing import Optional, Dict, List, Tuple # 配置日志 logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') logger = logging.getLogger(__name__) class CharacterProfileGenerator: """人物档案生成器""" def __init__(self, template_type="standard", workspace=None): """初始化生成器 Args: template_type: 模板类型,可选值: 'protagonist', 'antagonist', 'supporting', 'standard' workspace: 工作目录路径,启用LoreBible管理功能 """ self.template_type = template_type self.templates = self._load_templates() self.workspace = workspace self.enable_enhanced_features = workspace is not None if self.enable_enhanced_features: logger.info(f"启用增强功能,工作目录: {workspace}") # 延迟导入,避免循环依赖 self.lore_bible_manager = None self.conflict_detector = None self.profile_session = None def _init_enhanced_components(self): """初始化增强组件""" if not self.enable_enhanced_features: return try: # 延迟导入 from lore_bible_manager import LoreBibleManager from conflict_detector import ConflictDetector from profile_session import ProfileSession, SessionConfig # 初始化管理器 self.lore_bible_manager = LoreBibleManager(self.workspace) # 验证并创建目录结构 is_valid, missing_dirs = self.lore_bible_manager.validate_directory_structure() if not is_valid: logger.warning(f"目录结构不完整,缺失: {missing_dirs}") logger.info("正在创建缺失目录...") if self.lore_bible_manager.create_directory_structure(): logger.info("目录结构创建成功") else: logger.error("创建目录结构失败") # 初始化冲突检测器 self.conflict_detector = ConflictDetector() # 配置会话(稍后在generate_markdown中创建) self.session_config = SessionConfig( workspace=self.workspace, character_name="", # 稍后设置 template_type=self.template_type, enable_validation=True, enable_conflict_check=True, require_confirmation=True ) logger.info("增强组件初始化完成") except ImportError as e: logger.error(f"导入增强组件失败: {e}") logger.error("请确保lore_bible_manager.py、conflict_detector.py、profile_session.py在脚本目录中") self.enable_enhanced_features = False except Exception as e: logger.error(f"初始化增强组件失败: {e}") self.enable_enhanced_features = False def _load_templates(self): """加载模板配置""" templates = { "protagonist": { "name": "主角模板", "sections": [ {"id": "basic", "title": "基本信息", "required": True}, {"id": "appearance", "title": "外貌特征", "required": True}, {"id": "personality", "title": "性格特点", "required": True}, {"id": "background", "title": "背景故事", "required": True}, {"id": "motivation", "title": "动机层次", "required": True}, {"id": "relationships", "title": "人物关系", "required": True}, {"id": "development", "title": "故事发展", "required": True}, {"id": "core_identity", "title": "核心身份", "required": False}, {"id": "notes", "title": "创作笔记", "required": False} ] }, "antagonist": { "name": "反派模板", "sections": [ {"id": "basic", "title": "基本信息", "required": True}, {"id": "appearance", "title": "外貌特征", "required": True}, {"id": "personality", "title": "性格特点", "required": True}, {"id": "core_belief", "title": "核心理念", "required": True}, {"id": "motivation", "title": "动机发展", "required": True}, {"id": "mirror", "title": "镜像对比", "required": True}, {"id": "resources", "title": "资源能力", "required": True}, {"id": "development", "title": "故事发展", "required": True} ] }, "supporting": { "name": "配角模板", "sections": [ {"id": "basic", "title": "基本定位", "required": True}, {"id": "identity", "title": "独立身份", "required": True}, {"id": "function", "title": "功能性设计", "required": True}, {"id": "relationships", "title": "关系发展", "required": True}, {"id": "development", "title": "发展可能性", "required": True} ] }, "standard": { "name": "标准模板", "sections": [ {"id": "basic", "title": "基本信息", "required": True}, {"id": "appearance", "title": "外貌特征", "required": True}, {"id": "personality", "title": "性格特点", "required": True}, {"id": "background", "title": "背景故事", "required": True}, {"id": "relationships", "title": "人物关系", "required": True}, {"id": "development", "title": "故事发展", "required": True}, {"id": "notes", "title": "创作笔记", "required": False} ] } } return templates def _get_section_content(self, section_id, character_data): """获取章节内容 Args: section_id: 章节ID character_data: 角色数据字典 Returns: 章节内容字符串 """ section_templates = { "basic": """- **姓名**:{name} - **年龄**:{age} - **性别**:{gender} - **职业/身份**:{occupation} - **故事中的角色**:{role}""", "appearance": """- **整体印象**:{overall_impression} - **面部特征**:{facial_features} - **身材体型**:{body_type} - **着装风格**:{clothing_style} - **标志性特征**:{distinctive_features}""", "personality": """- **核心性格**:{core_personality} - **优点**:{strengths} - **缺点**:{weaknesses} - **价值观**:{values} - **恐惧**:{fears} - **渴望**:{desires}""", "background": """- **出身背景**:{origin} - **关键经历**:{key_experiences} - **转折点**:{turning_points} - **未解之谜**:{unsolved_mysteries}""", "relationships": """- **与主角关系**:{relationship_with_protagonist} - **重要关系人**:{important_relationships} - **敌对关系**:{enemy_relationships} - **情感羁绊**:{emotional_bonds}""", "development": """- **角色目标**:{goals} - **内在冲突**:{internal_conflicts} - **外在冲突**:{external_conflicts} - **发展弧线**:{development_arc} - **可能的结局**:{possible_endings}""", "notes": """- **灵感来源**:{inspiration_sources} - **象征意义**:{symbolic_meanings} - **潜在发展**:{potential_developments}""", "core_identity": """- **本质自我**:{true_self} - **社会面具**:{social_mask} - **理想自我**:{ideal_self} - **恐惧自我**:{feared_self}""", "motivation": """- **表层目标**:{surface_goals} - **情感需求**:{emotional_needs} - **存在需求**:{existential_needs} - **未意识需求**:{unconscious_needs}""", "core_belief": """- **世界观**:{worldview} - **核心信念**:{core_beliefs} - **正义观**:{justice_view} - **变革愿景**:{change_vision}""", "mirror": """- **相似点**:{similarities} - **分歧点**:{divergences} - **对立逻辑**:{opposition_logic} - **潜在转化**:{potential_transformation}""", "resources": """- **核心团队**:{core_team} - **盟友势力**:{ally_forces} - **影响范围**:{influence_scope} - **弱点环节**:{weakness_points}""", "function": """- **推动剧情的方式**:{plot_push_methods} - **服务主角的方式**:{protagonist_service_methods} - **独特价值**:{unique_value} - **退出时机**:{exit_timing}""" } template = section_templates.get(section_id, "") if not template: return "" # 从character_data中获取数据,如果不存在则使用占位符 content = template for key in character_data: placeholder = "{" + key + "}" if placeholder in content: content = content.replace(placeholder, character_data.get(key, f"[待填写{key}]")) # 清理未替换的占位符 import re content = re.sub(r'\{[^}]*\}', '[待填写]', content) return content def generate_markdown(self, character_data, output_path=None): """生成markdown档案 Args: character_data: 角色数据字典,必须包含'name'字段 output_path: 输出文件路径,如果为None则返回字符串 Returns: 如果output_path为None,返回markdown字符串;否则写入文件 """ if 'name' not in character_data: raise ValueError("角色数据必须包含'name'字段") template = self.templates.get(self.template_type, self.templates["standard"]) # 生成markdown内容 lines = [] lines.append(f"# {character_data['name']} - 角色档案") lines.append("") lines.append(f"> 生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") lines.append(f"> 模板类型:{template['name']}") lines.append("") for section in template['sections']: section_id = section['id'] section_title = section['title'] lines.append(f"## {section_title}") lines.append("") content = self._get_section_content(section_id, character_data) if content: lines.append(content) else: lines.append(f"*{section_title}内容待填写*") lines.append("") # 添加元数据部分 lines.append("---") lines.append("") lines.append("## 档案元数据") lines.append("") lines.append(f"- **角色类型**:{template['name']}") lines.append(f"- **创建时间**:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") lines.append(f"- **状态**:{'草稿' if character_data.get('status') != 'final' else '完成'}") lines.append(f"- **版本**:{character_data.get('version', '1.0')}") lines.append("") markdown_content = "\n".join(lines) if output_path: # 确保目录存在 output_dir = os.path.dirname(output_path) if output_dir: os.makedirs(output_dir, exist_ok=True) with open(output_path, 'w', encoding='utf-8') as f: f.write(markdown_content) print(f"档案已生成:{output_path}") return output_path else: return markdown_content def generate_enhanced_profile(self, character_data, require_confirmation=True): """使用增强功能生成角色档案 Args: character_data: 角色数据字典,必须包含'name'字段 require_confirmation: 是否需要用户确认 Returns: (是否成功, 最终文件路径, 冲突列表) """ if not self.enable_enhanced_features: logger.error("增强功能未启用,请指定workspace参数初始化生成器") return False, None, [] if 'name' not in character_data: raise ValueError("角色数据必须包含'name'字段") # 初始化增强组件 if self.lore_bible_manager is None: self._init_enhanced_components() if not self.enable_enhanced_features: return False, None, [] try: from profile_session import ProfileSession, SessionConfig # 更新会话配置 self.session_config.character_name = character_data['name'] self.session_config.require_confirmation = require_confirmation # 创建会话 session = ProfileSession(self.session_config) # 生成基础markdown内容 markdown_content = self.generate_markdown(character_data, output_path=None) # 保存临时档案 temp_path = session.save_temp_profile(markdown_content) if not temp_path: logger.error("保存临时档案失败") return False, None, [] # 验证和冲突检测 is_valid, conflicts = session.validate_and_check_conflicts(character_data) # 展示给用户 if require_confirmation: user_confirmed = session.present_to_user(conflicts) if not user_confirmed: logger.info("用户取消创建") session.cancel() return False, None, conflicts else: logger.info("跳过用户确认") # 确认并移动 final_path = session.confirm_and_move() if not final_path: logger.error("移动档案失败") return False, None, conflicts # 清理会话 session.cleanup() logger.info(f"角色档案创建成功: {final_path}") return True, final_path, conflicts except Exception as e: logger.error(f"增强生成失败: {e}") return False, None, [] def create_from_cli(self): """从命令行交互创建""" print("=== 人物档案生成器 ===") print("") # 选择模板 print("请选择角色类型:") print("1. 主角") print("2. 反派") print("3. 配角") print("4. 标准") choice = input("请输入选择 (1-4,默认4): ").strip() type_map = {"1": "protagonist", "2": "antagonist", "3": "supporting", "4": "standard"} template_type = type_map.get(choice, "standard") self.template_type = template_type print(f"使用模板:{self.templates[template_type]['name']}") print("") # 收集基本信息 character_data = {} print("请输入角色基本信息:") character_data['name'] = input("角色姓名: ").strip() or "未命名角色" character_data['age'] = input("年龄: ").strip() or "未知" character_data['gender'] = input("性别: ").strip() or "未指定" character_data['occupation'] = input("职业/身份: ").strip() or "未指定" character_data['role'] = input("故事中的角色: ").strip() or "未指定" print("") print("其他信息将在生成的档案中以占位符形式出现,请后续编辑完善。") # 生成文件名 safe_name = "".join(c for c in character_data['name'] if c.isalnum() or c in (' ', '-', '_')).rstrip() filename = f"character_profile_{safe_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md" # 选择输出路径 default_output = os.path.join(os.getcwd(), filename) output_path = input(f"输出文件路径 (默认: {default_output}): ").strip() or default_output # 生成档案 try: result = self.generate_markdown(character_data, output_path) print("") print("✓ 人物档案生成成功!") print(f"文件位置: {result if isinstance(result, str) else output_path}") print("") print("下一步:") print("1. 使用文本编辑器打开生成的markdown文件") print("2. 根据模板提示填写详细信息") print("3. 保存并用于创作参考") except Exception as e: print(f"生成失败: {e}") return 1 return 0 def main(): """主函数""" if len(sys.argv) > 1: # 命令行参数模式 import argparse parser = argparse.ArgumentParser(description='生成人物档案markdown文件') parser.add_argument('--type', '-t', choices=['protagonist', 'antagonist', 'supporting', 'standard'], default='standard', help='角色类型') parser.add_argument('--name', '-n', required=True, help='角色姓名') parser.add_argument('--age', help='年龄') parser.add_argument('--gender', help='性别') parser.add_argument('--occupation', help='职业/身份') parser.add_argument('--role', help='故事中的角色') parser.add_argument('--output', '-o', help='输出文件路径') parser.add_argument('--workspace', '-w', help='工作目录路径,启用LoreBible管理功能') parser.add_argument('--no-confirm', action='store_true', help='跳过用户确认(仅增强模式)') parser.add_argument('--interactive', '-i', action='store_true', help='交互模式') args = parser.parse_args() if args.interactive: # 交互模式,暂时不支持增强功能 generator = CharacterProfileGenerator(args.type) return generator.create_from_cli() else: character_data = { 'name': args.name, 'age': args.age or '未知', 'gender': args.gender or '未指定', 'occupation': args.occupation or '未指定', 'role': args.role or '未指定' } # 检查是否启用增强模式 if args.workspace: # 增强模式 generator = CharacterProfileGenerator(args.type, workspace=args.workspace) # 使用增强生成 success, final_path, conflicts = generator.generate_enhanced_profile( character_data, require_confirmation=not args.no_confirm ) if success: print(f"✓ 角色档案创建成功: {final_path}") if conflicts: print(f" 检测到 {len(conflicts)} 个问题,已处理") return 0 else: print(f"✗ 角色档案创建失败") if conflicts: print(f" 存在 {len(conflicts)} 个问题需要解决") return 1 else: # 传统模式 generator = CharacterProfileGenerator(args.type) if args.output: output_path = args.output else: safe_name = "".join(c for c in args.name if c.isalnum() or c in (' ', '-', '_')).rstrip() filename = f"character_profile_{safe_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md" output_path = os.path.join(os.getcwd(), filename) result = generator.generate_markdown(character_data, output_path) print(f"档案已生成: {output_path}") return 0 else: # 交互模式 generator = CharacterProfileGenerator() return generator.create_from_cli() if __name__ == "__main__": sys.exit(main())