update:1.新增统一的JSON清洗和重试方法,避免AI响应json格式错误 2.重构提示词模板命名,优化大纲章节初始化提示词 3.移除布冯冗余代码,提高代码复用性 4.优化系统默认写作风格预设提示词和规则

This commit is contained in:
xiamuceer
2025-12-14 15:21:52 +08:00
parent 86b73e85fb
commit 24b0a09b43
11 changed files with 633 additions and 1851 deletions
+5 -203
View File
@@ -429,199 +429,6 @@ async def remove_organization_member(
logger.info(f"移除成员成功:{member_id}")
return {"message": "成员移除成功", "id": member_id}
@router.post("/generate", response_model=CharacterResponse, summary="AI生成组织")
async def generate_organization(
gen_request: OrganizationGenerateRequest,
http_request: Request,
db: AsyncSession = Depends(get_db),
user_ai_service: AIService = Depends(get_user_ai_service)
):
"""
使用AI生成组织设定
根据用户输入的信息,结合项目的世界观、主题等背景,
AI会生成一个完整、详细的组织设定。
生成内容包括:组织名称、类型、特性、背景、目的、势力等级等
"""
# 验证用户权限
user_id = getattr(http_request.state, 'user_id', None)
project = await verify_project_access(gen_request.project_id, user_id, db)
try:
# 获取已存在的角色和组织列表
existing_chars_result = await db.execute(
select(Character)
.where(Character.project_id == gen_request.project_id)
.order_by(Character.created_at.desc())
)
existing_characters = existing_chars_result.scalars().all()
# 构建现有角色和组织信息摘要
existing_info = ""
character_list = []
organization_list = []
if existing_characters:
for c in existing_characters[:10]: # 最多显示10个
if c.is_organization:
organization_list.append(f"- {c.name} [{c.organization_type or '组织'}]")
else:
character_list.append(f"- {c.name}{c.role_type or '未知'}")
if character_list:
existing_info += "\n已有角色:\n" + "\n".join(character_list)
if organization_list:
existing_info += "\n\n已有组织:\n" + "\n".join(organization_list)
# 构建项目上下文信息
project_context = f"""
项目信息:
- 书名:{project.title}
- 主题:{project.theme or '未设定'}
- 类型:{project.genre or '未设定'}
- 时间背景:{project.world_time_period or '未设定'}
- 地理位置:{project.world_location or '未设定'}
- 氛围基调:{project.world_atmosphere or '未设定'}
- 世界规则:{project.world_rules or '未设定'}
{existing_info}
"""
# 构建用户输入信息
user_input = f"""
用户要求:
- 组织名称:{gen_request.name or '请AI生成'}
- 组织类型:{gen_request.organization_type or '请AI根据世界观决定'}
- 背景设定:{gen_request.background or '无特殊要求'}
- 其他要求:{gen_request.requirements or ''}
"""
# 获取自定义提示词模板
template = await PromptService.get_template("SINGLE_ORGANIZATION_GENERATION", user_id, db)
# 格式化提示词
prompt = PromptService.format_prompt(
template,
project_context=project_context,
user_input=user_input
)
# 调用AI生成组织
logger.info(f"🎯 开始为项目 {gen_request.project_id} 生成组织")
logger.info(f" - 组织名:{gen_request.name or 'AI生成'}")
logger.info(f" - 组织类型:{gen_request.organization_type or 'AI决定'}")
logger.info(f" - 背景设定:{gen_request.background or ''}")
logger.info(f" - AI提供商:{user_ai_service.api_provider}")
logger.info(f" - AI模型:{user_ai_service.default_model}")
logger.info(f" - Prompt长度:{len(prompt)} 字符")
try:
ai_response = await user_ai_service.generate_text(prompt=prompt)
logger.info(f"✅ AI响应接收完成")
except Exception as ai_error:
logger.error(f"❌ AI服务调用异常:{str(ai_error)}")
raise HTTPException(
status_code=500,
detail=f"AI服务调用失败:{str(ai_error)}"
)
# generate_text返回的是字典,需要提取content字段
ai_content = ai_response.get("content", "") if isinstance(ai_response, dict) else str(ai_response)
# 检查AI响应
if not ai_content or not ai_content.strip():
logger.error("❌ AI返回了空响应")
raise HTTPException(
status_code=500,
detail="AI服务返回空响应。请检查AI配置和网络连接。"
)
logger.info(f"📝 开始清理AI响应,长度:{len(ai_content)} 字符")
# 清理AI响应
cleaned_response = ai_content.strip()
if cleaned_response.startswith("```json"):
cleaned_response = cleaned_response[7:]
if cleaned_response.startswith("```"):
cleaned_response = cleaned_response[3:]
if cleaned_response.endswith("```"):
cleaned_response = cleaned_response[:-3]
cleaned_response = cleaned_response.strip()
logger.info(f" - 清理后长度:{len(cleaned_response)}")
# 解析AI响应
logger.info(f"🔍 开始解析JSON")
try:
organization_data = json.loads(cleaned_response)
logger.info(f"✅ JSON解析成功")
logger.info(f" - 解析后的字段:{list(organization_data.keys())}")
except json.JSONDecodeError as e:
logger.error(f"❌ JSON解析失败:{str(e)}")
raise HTTPException(
status_code=500,
detail=f"AI返回的内容无法解析为JSON。错误:{str(e)}"
)
# 创建角色记录(组织也是角色的一种)
character = Character(
project_id=gen_request.project_id,
name=organization_data.get("name", gen_request.name or "未命名组织"),
is_organization=True,
role_type="supporting", # 组织通常作为配角
personality=organization_data.get("personality", ""),
background=organization_data.get("background", ""),
appearance=organization_data.get("appearance", ""),
organization_type=organization_data.get("organization_type"),
organization_purpose=organization_data.get("organization_purpose"),
organization_members=json.dumps(
organization_data.get("organization_members", []),
ensure_ascii=False
),
traits=json.dumps(
organization_data.get("traits", []),
ensure_ascii=False
)
)
db.add(character)
await db.flush()
logger.info(f"✅ 组织角色创建成功:{character.name} (ID: {character.id})")
# 自动创建Organization详情记录
organization = Organization(
character_id=character.id,
project_id=gen_request.project_id,
member_count=0,
power_level=organization_data.get("power_level", 50),
location=organization_data.get("location"),
motto=organization_data.get("motto"),
color=organization_data.get("color")
)
db.add(organization)
await db.flush()
logger.info(f"✅ 组织详情创建成功:{character.name} (Org ID: {organization.id})")
# 记录生成历史
history = GenerationHistory(
project_id=gen_request.project_id,
prompt=prompt,
generated_content=ai_content,
model=user_ai_service.default_model
)
db.add(history)
await db.commit()
await db.refresh(character)
logger.info(f"🎉 成功为项目 {gen_request.project_id} 生成组织: {character.name}")
return character
except Exception as e:
logger.error(f"生成组织失败: {str(e)}")
raise HTTPException(status_code=500, detail=f"生成组织失败: {str(e)}")
@router.post("/generate-stream", summary="AI生成组织(流式)")
async def generate_organization_stream(
gen_request: OrganizationGenerateRequest,
@@ -718,19 +525,14 @@ async def generate_organization_stream(
yield await SSEResponse.send_progress("解析AI响应...", 60)
# 清理AI响应
cleaned_response = ai_content.strip()
if cleaned_response.startswith("```json"):
cleaned_response = cleaned_response[7:]
if cleaned_response.startswith("```"):
cleaned_response = cleaned_response[3:]
if cleaned_response.endswith("```"):
cleaned_response = cleaned_response[:-3]
cleaned_response = cleaned_response.strip()
# ✅ 使用统一的 JSON 清洗方法
try:
cleaned_response = user_ai_service._clean_json_response(ai_content)
organization_data = json.loads(cleaned_response)
logger.info(f"✅ 组织JSON解析成功")
except json.JSONDecodeError as e:
logger.error(f"❌ 组织JSON解析失败: {e}")
logger.error(f" 原始响应预览: {ai_content[:200]}")
yield await SSEResponse.send_error(f"AI返回的内容无法解析为JSON{str(e)}")
return