update:1.更新mcp插件功能,目前只支持remote调用

This commit is contained in:
xiamuceer
2025-11-07 22:14:20 +08:00
parent 1e998920e3
commit 88115a45c5
26 changed files with 4088 additions and 138 deletions
+255 -23
View File
@@ -404,8 +404,8 @@ async def _generate_new_outline(
db: AsyncSession,
user_ai_service: AIService
) -> OutlineListResponse:
"""全新生成大纲"""
logger.info(f"全新生成大纲 - 项目: {project.id}, keep_existing: {request.keep_existing}")
"""全新生成大纲MCP增强版)"""
logger.info(f"全新生成大纲 - 项目: {project.id}, enable_mcp: {request.enable_mcp}")
# 获取角色信息
characters_result = await db.execute(
@@ -418,7 +418,59 @@ async def _generate_new_outline(
for char in characters
])
# 使用完整提示词
# 🔍 MCP工具增强:收集情节设计参考资料
mcp_reference_materials = ""
if request.enable_mcp:
try:
logger.info(f"🔍 尝试使用MCP工具收集大纲设计参考资料...")
# 构建资料收集查询
planning_query = f"""你正在为小说《{project.title}》设计完整大纲。
项目信息:
- 主题:{request.theme or project.theme}
- 类型:{request.genre or project.genre}
- 章节数:{request.chapter_count}
- 叙事视角:{request.narrative_perspective}
- 目标字数:{request.target_words}
世界观设定:
- 时间背景:{project.world_time_period or '未设定'}
- 地理位置:{project.world_location or '未设定'}
- 氛围基调:{project.world_atmosphere or '未设定'}
角色信息:
{characters_info or '暂无角色'}
请搜索:
1. 该类型小说的经典情节结构和套路
2. 适合该主题的冲突设计思路
3. 符合世界观的情节元素和场景设计灵感
请有针对性地查询1-2个最关键的问题。"""
# 调用MCP增强的AI(非流式,最多2轮工具调用)
planning_result = await user_ai_service.generate_text_with_mcp(
prompt=planning_query,
user_id="system", # 全新生成时可能没有用户上下文
db_session=db,
enable_mcp=True,
max_tool_rounds=2,
tool_choice="auto",
provider=None,
model=None
)
# 提取参考资料
if planning_result.get("tool_calls_made", 0) > 0:
mcp_reference_materials = planning_result.get("content", "")
logger.info(f"📚 MCP工具收集参考资料:{len(mcp_reference_materials)} 字符")
else:
logger.info(f"ℹ️ MCP工具未进行调用,继续正常生成")
except Exception as e:
logger.warning(f"⚠️ MCP工具调用失败,继续使用常规模式: {str(e)}")
mcp_reference_materials = ""
# 使用完整提示词(插入MCP参考资料)
prompt = prompt_service.get_complete_outline_prompt(
title=project.title,
theme=request.theme or project.theme or "未设定",
@@ -431,18 +483,22 @@ async def _generate_new_outline(
atmosphere=project.world_atmosphere or "未设定",
rules=project.world_rules or "未设定",
characters_info=characters_info or "暂无角色信息",
requirements=request.requirements or ""
requirements=request.requirements or "",
mcp_references=mcp_reference_materials
)
# 调用AI
# 调用AI生成大纲
ai_response = await user_ai_service.generate_text(
prompt=prompt,
provider=request.provider,
model=request.model
)
# 提取内容(generate_text返回字典)
ai_content = ai_response.get("content", "") if isinstance(ai_response, dict) else ai_response
# 解析响应
outline_data = _parse_ai_response(ai_response)
outline_data = _parse_ai_response(ai_content)
# 全新生成模式:必须删除旧大纲和章节
# 注意:这是"new"模式的核心逻辑,应该始终删除旧数据
@@ -463,7 +519,7 @@ async def _generate_new_outline(
history = GenerationHistory(
project_id=project.id,
prompt=prompt,
generated_content=ai_response,
generated_content=json.dumps(ai_response, ensure_ascii=False) if isinstance(ai_response, dict) else ai_response,
model=request.model or "default"
)
db.add(history)
@@ -571,8 +627,8 @@ async def _continue_outline(
user_ai_service: AIService,
user_id: str = "system"
) -> OutlineListResponse:
"""续写大纲 - 分批生成,每批5章(记忆增强版)"""
logger.info(f"续写大纲 - 项目: {project.id}, 已有: {len(existing_outlines)}")
"""续写大纲 - 分批生成,每批5章(记忆+MCP增强版)"""
logger.info(f"续写大纲 - 项目: {project.id}, 已有: {len(existing_outlines)}, enable_mcp: {request.enable_mcp}")
# 分析已有大纲
current_chapter_count = len(existing_outlines)
@@ -664,7 +720,57 @@ async def _continue_outline(
logger.warning(f"⚠️ 记忆上下文构建失败,继续不使用记忆: {str(e)}")
memory_context = None
# 使用标准续写提示词模板(支持记忆增强)
# 🔍 MCP工具增强:收集续写参考资料
mcp_reference_materials = ""
if request.enable_mcp:
try:
logger.info(f"🔍 第{batch_num + 1}批:尝试使用MCP工具收集续写参考资料...")
# 构建资料收集查询
latest_summary = latest_outlines[-1].content if latest_outlines else ""
planning_query = f"""你正在为小说《{project.title}》续写大纲。
当前进度:已有{len(latest_outlines)}章,即将续写第{current_start_chapter}-{current_start_chapter + current_batch_size - 1}
项目信息:
- 主题:{request.theme or project.theme}
- 类型:{request.genre or project.genre}
- 叙事视角:{request.narrative_perspective}
- 情节阶段:{request.plot_stage}
- 故事发展方向:{request.story_direction or '自然延续'}
最近章节概要:
{latest_summary[:200]}
请搜索:
1. 该情节阶段的经典处理手法和技巧
2. 适合该发展方向的情节转折和冲突设计
3. 符合类型特点的场景设计和剧情元素
请有针对性地查询1-2个最关键的问题。"""
# 调用MCP增强的AI(非流式,最多2轮工具调用)
planning_result = await user_ai_service.generate_text_with_mcp(
prompt=planning_query,
user_id=user_id,
db_session=db,
enable_mcp=True,
max_tool_rounds=2,
tool_choice="auto",
provider=None,
model=None
)
# 提取参考资料
if planning_result.get("tool_calls_made", 0) > 0:
mcp_reference_materials = planning_result.get("content", "")
logger.info(f"📚 第{batch_num + 1}批MCP工具收集参考资料:{len(mcp_reference_materials)} 字符")
else:
logger.info(f"️ 第{batch_num + 1}批MCP工具未进行调用,继续正常生成")
except Exception as e:
logger.warning(f"⚠️ 第{batch_num + 1}批MCP工具调用失败,继续使用常规模式: {str(e)}")
mcp_reference_materials = ""
# 使用标准续写提示词模板(支持记忆+MCP增强)
prompt = prompt_service.get_outline_continue_prompt(
title=project.title,
theme=request.theme or project.theme or "未设定",
@@ -683,7 +789,8 @@ async def _continue_outline(
start_chapter=current_start_chapter,
story_direction=request.story_direction or "自然延续",
requirements=request.requirements or "",
memory_context=memory_context
memory_context=memory_context,
mcp_references=mcp_reference_materials
)
# 调用AI生成当前批次
@@ -694,8 +801,11 @@ async def _continue_outline(
model=request.model
)
# 提取内容(generate_text返回字典)
ai_content = ai_response.get("content", "") if isinstance(ai_response, dict) else ai_response
# 解析响应
outline_data = _parse_ai_response(ai_response)
outline_data = _parse_ai_response(ai_content)
# 保存当前批次的大纲
batch_outlines = await _save_outlines(
@@ -706,7 +816,7 @@ async def _continue_outline(
history = GenerationHistory(
project_id=project.id,
prompt=f"[批次{batch_num + 1}/{total_batches}] {str(prompt)[:500]}",
generated_content=ai_response,
generated_content=json.dumps(ai_response, ensure_ascii=False) if isinstance(ai_response, dict) else ai_response,
model=request.model or "default"
)
db.add(history)
@@ -820,7 +930,7 @@ async def new_outline_generator(
db: AsyncSession,
user_ai_service: AIService
) -> AsyncGenerator[str, None]:
"""全新生成大纲SSE生成器"""
"""全新生成大纲SSE生成器MCP增强版)"""
db_committed = False
try:
yield await SSEResponse.send_progress("开始生成大纲...", 5)
@@ -828,6 +938,7 @@ async def new_outline_generator(
project_id = data.get("project_id")
# 确保chapter_count是整数(前端可能传字符串)
chapter_count = int(data.get("chapter_count", 10))
enable_mcp = data.get("enable_mcp", True)
# 验证项目
yield await SSEResponse.send_progress("加载项目信息...", 10)
@@ -852,7 +963,61 @@ async def new_outline_generator(
for char in characters
])
# 使用完整提示词
# 🔍 MCP工具增强:收集情节设计参考资料
mcp_reference_materials = ""
if enable_mcp:
try:
yield await SSEResponse.send_progress("🔍 使用MCP工具收集参考资料...", 18)
logger.info(f"🔍 尝试使用MCP工具收集大纲设计参考资料...")
# 构建资料收集查询
planning_query = f"""你正在为小说《{project.title}》设计完整大纲。
项目信息:
- 主题:{data.get('theme') or project.theme}
- 类型:{data.get('genre') or project.genre}
- 章节数:{chapter_count}
- 叙事视角:{data.get('narrative_perspective') or '第三人称'}
- 目标字数:{data.get('target_words') or project.target_words or 100000}
世界观设定:
- 时间背景:{project.world_time_period or '未设定'}
- 地理位置:{project.world_location or '未设定'}
- 氛围基调:{project.world_atmosphere or '未设定'}
角色信息:
{characters_info or '暂无角色'}
请搜索:
1. 该类型小说的经典情节结构和套路
2. 适合该主题的冲突设计思路
3. 符合世界观的情节元素和场景设计灵感
请有针对性地查询1-2个最关键的问题。"""
# 调用MCP增强的AI(非流式,最多2轮工具调用)
planning_result = await user_ai_service.generate_text_with_mcp(
prompt=planning_query,
user_id="system",
db_session=db,
enable_mcp=True,
max_tool_rounds=2,
tool_choice="auto",
provider=None,
model=None
)
# 提取参考资料
if planning_result.get("tool_calls_made", 0) > 0:
mcp_reference_materials = planning_result.get("content", "")
logger.info(f"📚 MCP工具收集参考资料:{len(mcp_reference_materials)} 字符")
yield await SSEResponse.send_progress(f"📚 MCP收集到参考资料 ({len(mcp_reference_materials)}字符)", 19)
else:
logger.info(f"ℹ️ MCP工具未进行调用,继续正常生成")
except Exception as e:
logger.warning(f"⚠️ MCP工具调用失败,继续使用常规模式: {str(e)}")
mcp_reference_materials = ""
# 使用完整提示词(插入MCP参考资料)
yield await SSEResponse.send_progress("准备AI提示词...", 20)
prompt = prompt_service.get_complete_outline_prompt(
title=project.title,
@@ -866,7 +1031,8 @@ async def new_outline_generator(
atmosphere=project.world_atmosphere or "未设定",
rules=project.world_rules or "未设定",
characters_info=characters_info or "暂无角色信息",
requirements=data.get("requirements") or ""
requirements=data.get("requirements") or "",
mcp_references=mcp_reference_materials
)
# 调用AI
@@ -879,8 +1045,11 @@ async def new_outline_generator(
yield await SSEResponse.send_progress("✅ AI生成完成,正在解析...", 70)
# 提取内容(generate_text返回字典)
ai_content = ai_response.get("content", "") if isinstance(ai_response, dict) else ai_response
# 解析响应
outline_data = _parse_ai_response(ai_response)
outline_data = _parse_ai_response(ai_content)
# 删除旧大纲和章节
yield await SSEResponse.send_progress("清理旧数据...", 75)
@@ -902,7 +1071,7 @@ async def new_outline_generator(
history = GenerationHistory(
project_id=project_id,
prompt=prompt,
generated_content=ai_response,
generated_content=json.dumps(ai_response, ensure_ascii=False) if isinstance(ai_response, dict) else ai_response,
model=data.get("model") or "default"
)
db.add(history)
@@ -957,7 +1126,7 @@ async def continue_outline_generator(
user_ai_service: AIService,
user_id: str = "system"
) -> AsyncGenerator[str, None]:
"""大纲续写SSE生成器 - 分批生成,推送进度(记忆增强版)"""
"""大纲续写SSE生成器 - 分批生成,推送进度(记忆+MCP增强版)"""
db_committed = False
try:
yield await SSEResponse.send_progress("开始续写大纲...", 5)
@@ -1090,13 +1259,72 @@ async def continue_outline_generator(
except Exception as e:
logger.warning(f"⚠️ 记忆上下文构建失败: {str(e)}")
memory_context = None
# 🔍 MCP工具增强:收集续写参考资料
mcp_reference_materials = ""
enable_mcp = data.get("enable_mcp", True)
if enable_mcp:
try:
yield await SSEResponse.send_progress(
f"🔍 第{str(batch_num + 1)}批:使用MCP工具收集参考资料...",
batch_progress + 4
)
logger.info(f"🔍 第{batch_num + 1}批:尝试使用MCP工具收集续写参考资料...")
# 构建资料收集查询
latest_summary = latest_outlines[-1].content if latest_outlines else ""
planning_query = f"""你正在为小说《{project.title}》续写大纲。
当前进度:已有{len(latest_outlines)}章,即将续写第{current_start_chapter}-{current_start_chapter + current_batch_size - 1}
项目信息:
- 主题:{data.get('theme') or project.theme}
- 类型:{data.get('genre') or project.genre}
- 叙事视角:{data.get('narrative_perspective') or project.narrative_perspective or '第三人称'}
- 情节阶段:{data.get('plot_stage', 'development')}
- 故事发展方向:{data.get('story_direction', '自然延续')}
最近章节概要:
{latest_summary[:200]}
请搜索:
1. 该情节阶段的经典处理手法和技巧
2. 适合该发展方向的情节转折和冲突设计
3. 符合类型特点的场景设计和剧情元素
请有针对性地查询1-2个最关键的问题。"""
# 调用MCP增强的AI(非流式,最多2轮工具调用)
planning_result = await user_ai_service.generate_text_with_mcp(
prompt=planning_query,
user_id=user_id,
db_session=db,
enable_mcp=True,
max_tool_rounds=2,
tool_choice="auto",
provider=None,
model=None
)
# 提取参考资料
if planning_result.get("tool_calls_made", 0) > 0:
mcp_reference_materials = planning_result.get("content", "")
logger.info(f"📚 第{batch_num + 1}批MCP工具收集参考资料:{len(mcp_reference_materials)} 字符")
yield await SSEResponse.send_progress(
f"📚 第{str(batch_num + 1)}批收集到参考资料 ({len(mcp_reference_materials)}字符)",
batch_progress + 4.5
)
else:
logger.info(f"️ 第{batch_num + 1}批MCP工具未进行调用,继续正常生成")
except Exception as e:
logger.warning(f"⚠️ 第{batch_num + 1}批MCP工具调用失败,继续使用常规模式: {str(e)}")
mcp_reference_materials = ""
yield await SSEResponse.send_progress(
f" 调用AI生成第{str(batch_num + 1)}批...",
batch_progress + 5
)
# 使用标准续写提示词模板(支持记忆增强)
# 使用标准续写提示词模板(支持记忆+MCP增强)
prompt = prompt_service.get_outline_continue_prompt(
title=project.title,
theme=data.get("theme") or project.theme or "未设定",
@@ -1115,7 +1343,8 @@ async def continue_outline_generator(
start_chapter=current_start_chapter,
story_direction=data.get("story_direction", "自然延续"),
requirements=data.get("requirements", ""),
memory_context=memory_context
memory_context=memory_context,
mcp_references=mcp_reference_materials
)
# 调用AI生成当前批次
@@ -1130,8 +1359,11 @@ async def continue_outline_generator(
batch_progress + 10
)
# 提取内容(generate_text返回字典)
ai_content = ai_response.get("content", "") if isinstance(ai_response, dict) else ai_response
# 解析响应
outline_data = _parse_ai_response(ai_response)
outline_data = _parse_ai_response(ai_content)
# 保存当前批次的大纲
batch_outlines = await _save_outlines(
@@ -1142,7 +1374,7 @@ async def continue_outline_generator(
history = GenerationHistory(
project_id=project_id,
prompt=f"[续写批次{batch_num + 1}/{total_batches}] {str(prompt)[:500]}",
generated_content=ai_response,
generated_content=json.dumps(ai_response, ensure_ascii=False) if isinstance(ai_response, dict) else ai_response,
model=data.get("model") or "default"
)
db.add(history)