update:更新智能引入角色/组织sse推送逻辑,优化进度展示

This commit is contained in:
xiamuceer
2026-01-07 14:25:40 +08:00
parent 609426fd7d
commit dff2834c4f
3 changed files with 202 additions and 263 deletions
+143 -237
View File
@@ -75,6 +75,48 @@ async def verify_project_access(project_id: str, user_id: str, db: AsyncSession)
return project
def _build_chapters_brief(outlines: List[Outline], max_recent: int = 20) -> str:
"""构建章节概览字符串"""
target = outlines[-max_recent:] if len(outlines) > max_recent else outlines
return "\n".join([f"{o.order_index}章《{o.title}" for o in target])
def _build_characters_info(characters: List[Character]) -> str:
"""构建角色信息字符串"""
return "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
async def _get_existing_organizations(project_id: str, db: AsyncSession) -> List[dict]:
"""获取项目现有组织列表"""
from app.models.relationship import Organization
organizations_result = await db.execute(
select(Character, Organization)
.join(Organization, Character.id == Organization.character_id)
.where(
Character.project_id == project_id,
Character.is_organization == True
)
)
organizations_raw = organizations_result.all()
return [
{
"id": org.id,
"name": char.name,
"organization_type": char.organization_type,
"organization_purpose": char.organization_purpose,
"power_level": org.power_level,
"location": org.location,
"motto": org.motto
}
for char, org in organizations_raw
]
@router.post("", response_model=OutlineResponse, summary="创建大纲")
async def create_outline(
outline: OutlineCreate,
@@ -145,26 +187,8 @@ async def get_project_outlines(
request: Request,
db: AsyncSession = Depends(get_db)
):
"""获取指定项目的所有大纲(路径参数版本)"""
# 验证用户权限
user_id = getattr(request.state, 'user_id', None)
await verify_project_access(project_id, user_id, db)
# 获取总数
count_result = await db.execute(
select(func.count(Outline.id)).where(Outline.project_id == project_id)
)
total = count_result.scalar_one()
# 获取大纲列表
result = await db.execute(
select(Outline)
.where(Outline.project_id == project_id)
.order_by(Outline.order_index)
)
outlines = result.scalars().all()
return OutlineListResponse(total=total, items=outlines)
"""获取指定项目的所有大纲(路径参数版本,兼容旧API"""
return await get_outlines(project_id, request, db)
@router.get("/{outline_id}", response_model=OutlineResponse, summary="获取大纲详情")
@@ -406,18 +430,7 @@ async def predict_characters(
characters = characters_result.scalars().all()
# 构建已有章节概览
all_chapters_brief = ""
if len(existing_outlines) > 20:
recent_20 = existing_outlines[-20:]
all_chapters_brief = "\n".join([
f"{o.order_index}章《{o.title}"
for o in recent_20
])
else:
all_chapters_brief = "\n".join([
f"{o.order_index}章《{o.title}"
for o in existing_outlines
])
all_chapters_brief = _build_chapters_brief(existing_outlines)
# 调用自动角色服务进行预测
from app.services.auto_character_service import get_auto_character_service
@@ -479,7 +492,6 @@ async def predict_organizations(
用于组织确认机制的第一步:在生成大纲前预测组织需求
"""
from app.schemas.outline import OrganizationPredictionResponse, PredictedOrganization
from app.models.relationship import Organization
# 验证用户权限
@@ -510,40 +522,10 @@ async def predict_organizations(
characters = characters_result.scalars().all()
# 获取现有组织
organizations_result = await db.execute(
select(Character, Organization)
.join(Organization, Character.id == Organization.character_id)
.where(
Character.project_id == request_data.project_id,
Character.is_organization == True
)
)
organizations_raw = organizations_result.all()
existing_organizations = []
for char, org in organizations_raw:
existing_organizations.append({
"id": org.id,
"name": char.name,
"organization_type": char.organization_type,
"organization_purpose": char.organization_purpose,
"power_level": org.power_level,
"location": org.location,
"motto": org.motto
})
existing_organizations = await _get_existing_organizations(request_data.project_id, db)
# 构建已有章节概览
all_chapters_brief = ""
if len(existing_outlines) > 20:
recent_20 = existing_outlines[-20:]
all_chapters_brief = "\n".join([
f"{o.order_index}章《{o.title}"
for o in recent_20
])
else:
all_chapters_brief = "\n".join([
f"{o.order_index}章《{o.title}"
for o in existing_outlines
])
all_chapters_brief = _build_chapters_brief(existing_outlines)
# 调用自动组织服务进行预测
from app.services.auto_organization_service import get_auto_organization_service
@@ -613,11 +595,7 @@ async def _generate_new_outline(
select(Character).where(Character.project_id == project.id)
)
characters = characters_result.scalars().all()
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
# 🔍 MCP工具增强:收集情节设计参考资料(优化版)
mcp_reference_materials = ""
@@ -894,11 +872,7 @@ async def _continue_outline(
select(Character).where(Character.project_id == project.id)
)
characters = characters_result.scalars().all()
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
# 情节阶段指导
stage_instructions = {
@@ -978,11 +952,7 @@ async def _continue_outline(
logger.info(f"ℹ️ 【确认模式】所有角色均已存在,无需创建")
# 更新角色信息(供后续大纲生成使用)
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
except Exception as e:
logger.error(f"⚠️ 【确认模式】创建确认角色失败: {e}", exc_info=True)
@@ -992,18 +962,7 @@ async def _continue_outline(
from app.services.auto_character_service import get_auto_character_service
# 构建已有章节概览
all_chapters_brief_for_analysis = ""
if len(existing_outlines) > 20:
recent_20 = existing_outlines[-20:]
all_chapters_brief_for_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in recent_20
])
else:
all_chapters_brief_for_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in existing_outlines
])
all_chapters_brief_for_analysis = _build_chapters_brief(existing_outlines)
auto_char_service = get_auto_character_service(user_ai_service)
@@ -1075,11 +1034,7 @@ async def _continue_outline(
# 更新角色信息(供后续大纲生成使用)
characters.extend(auto_result["new_characters"])
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
else:
logger.info(f"✅ 【直接创建模式】AI判断无需引入新角色,继续生成大纲")
@@ -1091,29 +1046,8 @@ async def _continue_outline(
# 🏛️ 【组织引入】在生成大纲前预测并创建组织
if request.enable_auto_organizations:
from app.models.relationship import Organization
# 获取现有组织
organizations_result = await db.execute(
select(Character, Organization)
.join(Organization, Character.id == Organization.character_id)
.where(
Character.project_id == project.id,
Character.is_organization == True
)
)
organizations_raw = organizations_result.all()
existing_organizations = []
for char, org in organizations_raw:
existing_organizations.append({
"id": org.id,
"name": char.name,
"organization_type": char.organization_type,
"organization_purpose": char.organization_purpose,
"power_level": org.power_level,
"location": org.location,
"motto": org.motto
})
existing_organizations = await _get_existing_organizations(project.id, db)
# 检查是否有用户确认的组织列表
if request.confirmed_organizations:
@@ -1177,11 +1111,7 @@ async def _continue_outline(
await db.commit()
# 更新角色信息(供后续大纲生成使用)
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
logger.info(f"✅ 【确认模式】成功创建 {len(request.confirmed_organizations)} 个用户确认的组织")
@@ -1193,18 +1123,7 @@ async def _continue_outline(
from app.services.auto_organization_service import get_auto_organization_service
# 构建已有章节概览
all_chapters_brief_for_org_analysis = ""
if len(existing_outlines) > 20:
recent_20 = existing_outlines[-20:]
all_chapters_brief_for_org_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in recent_20
])
else:
all_chapters_brief_for_org_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in existing_outlines
])
all_chapters_brief_for_org_analysis = _build_chapters_brief(existing_outlines)
auto_org_service = get_auto_organization_service(user_ai_service)
@@ -1281,11 +1200,7 @@ async def _continue_outline(
org_char = org_item.get("character")
if org_char:
characters.append(org_char)
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
else:
logger.info(f"✅ 【直接创建模式】AI判断无需引入新组织,继续生成大纲")
@@ -1711,11 +1626,7 @@ async def new_outline_generator(
select(Character).where(Character.project_id == project_id)
)
characters = characters_result.scalars().all()
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
# 🔍 MCP工具增强:收集情节设计参考资料(优化版)
mcp_reference_materials = ""
@@ -2049,11 +1960,7 @@ async def continue_outline_generator(
select(Character).where(Character.project_id == project_id)
)
characters = characters_result.scalars().all()
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
# 分批配置
batch_size = 5
@@ -2195,35 +2102,19 @@ async def continue_outline_generator(
from app.services.auto_character_service import get_auto_character_service
# 构建已有章节概览
all_chapters_brief_for_analysis = ""
if len(existing_outlines) > 20:
recent_20 = existing_outlines[-20:]
all_chapters_brief_for_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in recent_20
])
else:
all_chapters_brief_for_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in existing_outlines
])
all_chapters_brief_for_analysis = _build_chapters_brief(existing_outlines)
auto_char_service = get_auto_character_service(user_ai_service)
if require_confirmation:
# 🔮 预测模式:仅预测角色,不自动创建,需要用户确认
yield await SSEResponse.send_progress(
"🔮 【预测模式】AI分析角色需求...",
11
"🔮 【预测模式】开始分析角色需求...",
12
)
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新角色")
yield await SSEResponse.send_progress(
"🤖 【预测模式】AI智能预测新角色...",
15
)
# 进度消息不使用回调,因为在async generator中无法嵌套yield
auto_result = await auto_char_service.analyze_and_create_characters(
project_id=project_id,
outline_content="", # 预测模式不需要大纲内容
@@ -2239,6 +2130,11 @@ async def continue_outline_generator(
preview_only=True # ✅ 仅预测不创建
)
yield await SSEResponse.send_progress(
"✅ 【预测模式】角色需求分析完成",
18
)
# 检查是否需要新角色
if auto_result.get("needs_new_characters") and auto_result.get("predicted_characters"):
predicted_count = len(auto_result["predicted_characters"])
@@ -2266,13 +2162,21 @@ async def continue_outline_generator(
else:
# 🚀 直接创建模式:预测后自动创建,无需用户确认
yield await SSEResponse.send_progress(
"🚀 【直接创建模式】检测并自动创建角色(无需确认)...",
13
"🚀 【直接创建模式】开始分析并创建角色...",
14
)
logger.info(f"🚀 【直接创建模式】在生成大纲前预测并直接创建新角色")
auto_result = await auto_char_service.analyze_and_create_characters(
# 使用队列桥接回调和generator
import asyncio
progress_queue = asyncio.Queue()
async def char_progress_callback(message):
await progress_queue.put(message)
# 启动服务任务
char_task = asyncio.create_task(
auto_char_service.analyze_and_create_characters(
project_id=project_id,
outline_content="",
existing_characters=list(characters),
@@ -2284,7 +2188,27 @@ async def continue_outline_generator(
chapter_count=total_chapters_to_generate,
plot_stage=data.get("plot_stage", "development"),
story_direction=data.get("story_direction", "自然延续"),
preview_only=False # ✅ 直接创建角色
preview_only=False,
progress_callback=char_progress_callback
)
)
# 在等待任务完成的同时,消费队列中的进度消息
char_progress_base = 14
while not char_task.done():
try:
message = await asyncio.wait_for(progress_queue.get(), timeout=0.1)
char_progress_base = min(char_progress_base + 1, 17)
yield await SSEResponse.send_progress(message, char_progress_base)
except asyncio.TimeoutError:
pass
# 获取结果
auto_result = await char_task
yield await SSEResponse.send_progress(
"✅ 【直接创建模式】角色分析和创建完成",
18
)
# 如果创建了新角色,更新角色列表
@@ -2302,11 +2226,7 @@ async def continue_outline_generator(
# 更新角色信息(供后续大纲生成使用)
characters.extend(auto_result["new_characters"])
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
else:
yield await SSEResponse.send_progress(
"✅ 【直接创建模式】无需引入新角色,继续生成大纲",
@@ -2329,29 +2249,8 @@ async def continue_outline_generator(
# confirmed_organizations = data.get("confirmed_organizations")
if enable_auto_organizations:
from app.models.relationship import Organization
# 获取现有组织
organizations_result = await db.execute(
select(Character, Organization)
.join(Organization, Character.id == Organization.character_id)
.where(
Character.project_id == project_id,
Character.is_organization == True
)
)
organizations_raw = organizations_result.all()
existing_organizations = []
for char, org in organizations_raw:
existing_organizations.append({
"id": org.id,
"name": char.name,
"organization_type": char.organization_type,
"organization_purpose": char.organization_purpose,
"power_level": org.power_level,
"location": org.location,
"motto": org.motto
})
existing_organizations = await _get_existing_organizations(project_id, db)
# 检查是否有用户确认的组织列表
if confirmed_organizations:
@@ -2465,34 +2364,17 @@ async def continue_outline_generator(
from app.services.auto_organization_service import get_auto_organization_service
# 构建已有章节概览
all_chapters_brief_for_org_analysis = ""
if len(existing_outlines) > 20:
recent_20 = existing_outlines[-20:]
all_chapters_brief_for_org_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in recent_20
])
else:
all_chapters_brief_for_org_analysis = "\n".join([
f"{o.order_index}章《{o.title}"
for o in existing_outlines
])
all_chapters_brief_for_org_analysis = _build_chapters_brief(existing_outlines)
auto_org_service = get_auto_organization_service(user_ai_service)
if require_org_confirmation:
# 🔮 预测模式:仅预测组织,不自动创建,需要用户确认
yield await SSEResponse.send_progress(
"🔮 【预测模式】AI分析组织需求...",
21
)
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新组织")
yield await SSEResponse.send_progress(
"🤖 【预测模式】AI智能预测新组织...",
"🔮 【预测模式】开始分析组织需求...",
22
)
logger.info(f"🔮 【预测模式】在生成大纲前预测是否需要新组织")
auto_result = await auto_org_service.analyze_and_create_organizations(
project_id=project_id,
@@ -2510,6 +2392,11 @@ async def continue_outline_generator(
preview_only=True # ✅ 仅预测不创建
)
yield await SSEResponse.send_progress(
"✅ 【预测模式】组织需求分析完成",
28
)
# 检查是否需要新组织
if auto_result.get("needs_new_organizations") and auto_result.get("predicted_organizations"):
predicted_count = len(auto_result["predicted_organizations"])
@@ -2537,18 +2424,21 @@ async def continue_outline_generator(
else:
# 🚀 直接创建模式:预测后自动创建,无需用户确认
yield await SSEResponse.send_progress(
"🚀 【直接创建模式】AI分析组织需求...",
23
"🚀 【直接创建模式】开始分析并创建组织...",
24
)
logger.info(f"🚀 【直接创建模式】在生成大纲前预测并直接创建新组织")
yield await SSEResponse.send_progress(
"🤖 【直接创建模式】AI预测并生成组织详情...",
25
)
# 使用队列桥接回调和generator
import asyncio
org_progress_queue = asyncio.Queue()
auto_result = await auto_org_service.analyze_and_create_organizations(
async def org_progress_callback(message):
await org_progress_queue.put(message)
# 启动服务任务
org_task = asyncio.create_task(
auto_org_service.analyze_and_create_organizations(
project_id=project_id,
outline_content="",
existing_characters=list(characters),
@@ -2561,7 +2451,27 @@ async def continue_outline_generator(
chapter_count=total_chapters_to_generate,
plot_stage=data.get("plot_stage", "development"),
story_direction=data.get("story_direction", "自然延续"),
preview_only=False # ✅ 直接创建组织
preview_only=False,
progress_callback=org_progress_callback
)
)
# 在等待任务完成的同时,消费队列中的进度消息
org_progress_base = 24
while not org_task.done():
try:
message = await asyncio.wait_for(org_progress_queue.get(), timeout=0.1)
org_progress_base = min(org_progress_base + 1, 27)
yield await SSEResponse.send_progress(message, org_progress_base)
except asyncio.TimeoutError:
pass
# 获取结果
auto_result = await org_task
yield await SSEResponse.send_progress(
"✅ 【直接创建模式】组织分析和创建完成",
28
)
# 如果创建了新组织,更新角色列表
@@ -2587,11 +2497,7 @@ async def continue_outline_generator(
org_char = org_item.get("character")
if org_char:
characters.append(org_char)
characters_info = "\n".join([
f"- {char.name} ({'组织' if char.is_organization else '角色'}, {char.role_type}): "
f"{char.personality[:100] if char.personality else '暂无描述'}"
for char in characters
])
characters_info = _build_characters_info(characters)
else:
yield await SSEResponse.send_progress(
"✅ 【直接创建模式】无需引入新组织,继续生成大纲",
+15 -2
View File
@@ -1,5 +1,5 @@
"""自动角色引入服务 - 在续写大纲时根据剧情推进自动引入新角色"""
from typing import List, Dict, Any, Optional
from typing import List, Dict, Any, Optional, Callable, Awaitable
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
import json
@@ -33,7 +33,8 @@ class AutoCharacterService:
chapter_count: int = 3,
plot_stage: str = "发展",
story_direction: str = "继续推进主线剧情",
preview_only: bool = False
preview_only: bool = False,
progress_callback: Optional[Callable[[str], Awaitable[None]]] = None
) -> Dict[str, Any]:
"""
预测性分析并创建需要的新角色(方案A:先角色后大纲)
@@ -136,6 +137,9 @@ class AutoCharacterService:
logger.info(f" [{idx+1}/{len(character_specs)}] 生成角色规格: {spec_name}")
logger.debug(f" 角色规格内容: {json.dumps(spec, ensure_ascii=False)}")
if progress_callback:
await progress_callback(f"🎨 [{idx+1}/{len(character_specs)}] 生成角色详情: {spec_name}")
# 生成角色详细信息
character_data = await self._generate_character_details(
spec=spec,
@@ -148,6 +152,9 @@ class AutoCharacterService:
logger.debug(f" AI生成的角色数据: {json.dumps(character_data, ensure_ascii=False)[:200]}")
if progress_callback:
await progress_callback(f"💾 [{idx+1}/{len(character_specs)}] 保存角色: {character_data.get('name', spec_name)}")
# 创建角色记录
character = await self._create_character_record(
project_id=project_id,
@@ -158,6 +165,9 @@ class AutoCharacterService:
new_characters.append(character)
logger.info(f" ✅ 创建新角色: {character.name} ({character.role_type}), ID: {character.id}")
if progress_callback:
await progress_callback(f"✅ [{idx+1}/{len(character_specs)}] 角色创建成功: {character.name}")
# 建立关系(兼容两种字段名)
relationships_data = character_data.get("relationships") or character_data.get("relationships_array", [])
logger.info(f" 🔍 检查关系数据:")
@@ -170,6 +180,9 @@ class AutoCharacterService:
logger.info(f" 🔗 开始创建 {len(relationships_data)} 条关系...")
for idx, rel in enumerate(relationships_data):
logger.info(f" [{idx+1}] {rel.get('target_character_name')} - {rel.get('relationship_type')}")
if progress_callback:
await progress_callback(f"🔗 [{idx+1}/{len(character_specs)}] 建立 {len(relationships_data)} 个关系")
else:
logger.warning(f" ⚠️ AI返回的角色数据中没有关系信息!")
logger.warning(f" 完整的character_data keys: {list(character_data.keys())}")
@@ -1,5 +1,5 @@
"""自动组织引入服务 - 在续写大纲时根据剧情推进自动引入新组织"""
from typing import List, Dict, Any, Optional
from typing import List, Dict, Any, Optional, Callable, Awaitable
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
import json
@@ -34,7 +34,8 @@ class AutoOrganizationService:
chapter_count: int = 3,
plot_stage: str = "发展",
story_direction: str = "继续推进主线剧情",
preview_only: bool = False
preview_only: bool = False,
progress_callback: Optional[Callable[[str], Awaitable[None]]] = None
) -> Dict[str, Any]:
"""
预测性分析并创建需要的新组织
@@ -86,6 +87,9 @@ class AutoOrganizationService:
existing_chars_summary = self._build_character_summary(existing_characters)
# 3. AI预测性分析是否需要新组织
if progress_callback:
await progress_callback("🤖 AI分析组织需求...")
analysis_result = await self._analyze_organization_needs(
project=project,
outline_content=outline_content,
@@ -101,6 +105,9 @@ class AutoOrganizationService:
story_direction=story_direction
)
if progress_callback:
await progress_callback("✅ 组织需求分析完成")
# 4. 判断是否需要创建组织
if not analysis_result or not analysis_result.get("needs_new_organizations"):
logger.info("✅ AI判断:当前剧情不需要引入新组织")
@@ -141,6 +148,9 @@ class AutoOrganizationService:
logger.info(f" [{idx+1}/{len(organization_specs)}] 生成组织规格: {spec_name}")
logger.debug(f" 组织规格内容: {json.dumps(spec, ensure_ascii=False)}")
if progress_callback:
await progress_callback(f"🏛️ [{idx+1}/{len(organization_specs)}] 生成组织详情: {spec_name}")
# 生成组织详细信息
organization_data = await self._generate_organization_details(
spec=spec,
@@ -154,6 +164,9 @@ class AutoOrganizationService:
logger.debug(f" AI生成的组织数据: {json.dumps(organization_data, ensure_ascii=False)[:200]}")
if progress_callback:
await progress_callback(f"💾 [{idx+1}/{len(organization_specs)}] 保存组织: {organization_data.get('name', spec_name)}")
# 创建组织记录(先创建Character记录,再创建Organization记录)
character, organization = await self._create_organization_record(
project_id=project_id,
@@ -167,10 +180,17 @@ class AutoOrganizationService:
})
logger.info(f" ✅ 创建新组织: {character.name}, ID: {organization.id}")
if progress_callback:
await progress_callback(f"✅ [{idx+1}/{len(organization_specs)}] 组织创建成功: {character.name}")
# 建立成员关系
members_data = organization_data.get("initial_members", [])
if members_data:
logger.info(f" 🔗 开始创建 {len(members_data)} 个成员关系...")
if progress_callback:
await progress_callback(f"🔗 [{idx+1}/{len(organization_specs)}] 建立 {len(members_data)} 个成员关系")
members = await self._create_member_relationships(
organization=organization,
member_specs=members_data,