refactor(schedule): 重构月度计划生成器以增强稳定性和可维护性

将计划生成逻辑重构为使用统一的 `LLMRequest` 类,以简化模型调用流程。此更改消除了原有的手动选择模型、构建客户端和处理响应的复杂逻辑。

引入了 Pydantic 模型 `PlanResponse` 对 LLM 的 JSON 输出进行严格的验证和解析,并集成了 `json_repair` 库来自动修复格式错误的 JSON。这些措施显著提高了计划生成功能的健壮性和对 LLM 异常输出的容错能力。
This commit is contained in:
minecraft1024a
2025-08-21 19:41:59 +08:00
parent 09b36585b3
commit 6527a43bba

View File

@@ -1,16 +1,22 @@
# mmc/src/schedule/plan_generator.py
import json
import random
from typing import List
from pydantic import BaseModel, ValidationError
from json_repair import repair_json
from src.config.config import global_config, model_config
from src.llm_models.model_client.base_client import client_registry
from src.llm_models.payload_content.message import Message, RoleType
from src.llm_models.payload_content.resp_format import RespFormat, RespFormatType
from src.llm_models.utils_model import LLMRequest
from src.common.logger import get_logger
logger = get_logger("plan_generator")
class PlanResponse(BaseModel):
"""
用于验证月度计划LLM响应的Pydantic模型。
"""
plans: List[str]
class PlanGenerator:
"""
负责生成月度计划。
@@ -18,6 +24,8 @@ class PlanGenerator:
def __init__(self):
self.bot_personality = self._get_bot_personality()
task_config = model_config.model_task_config.get_task("monthly_plan_generator")
self.llm_request = LLMRequest(model_set=task_config, request_type="monthly_plan_generator")
def _get_bot_personality(self) -> str:
"""
@@ -61,56 +69,46 @@ class PlanGenerator:
:return: 生成的计划文本列表
"""
try:
# 1. 获取模型任务配置
task_config = model_config.model_task_config.get_task("monthly_plan_generator")
# 2. 随机选择一个模型
model_name = random.choice(task_config.model_list)
model_info = model_config.get_model_info(model_name)
api_provider = model_config.get_provider(model_info.api_provider)
# 3. 获取客户端实例
llm_client = client_registry.get_client_class_instance(api_provider)
# 4. 构建Prompt和消息体
# 1. 构建Prompt
prompt = self._build_prompt(year, month, count)
message_list = [Message(role=RoleType.User, content=prompt)]
logger.info(f"正在为 {year}-{month} 生成 {count} 个月度计划...")
# 2. 调用LLM
llm_content, (reasoning, model_name, _) = await self.llm_request.generate_response_async(prompt=prompt)
logger.info(f"正在使用模型 '{model_name}' {year}-{month} 生成 {count} 个月度计划...")
# 5. 调用LLM
response = await llm_client.get_response(
model_info=model_info,
message_list=message_list,
temperature=task_config.temperature,
max_tokens=task_config.max_tokens,
response_format=RespFormat(format_type=RespFormatType.JSON_OBJ) # 请求JSON输出
)
if not response or not response.content:
logger.info(f"使用模型 '{model_name}' 生成完成。")
if reasoning:
logger.debug(f"模型推理过程: {reasoning}")
if not llm_content:
logger.error("LLM未能返回有效的计划内容。")
return []
# 6. 解析LLM返回的JSON
# 3. 解析并验证LLM返回的JSON
try:
# 移除可能的Markdown代码块标记
clean_content = response.content.strip()
clean_content = llm_content.strip()
if clean_content.startswith("```json"):
clean_content = clean_content[7:]
if clean_content.endswith("```"):
clean_content = clean_content[:-3]
data = json.loads(clean_content.strip())
plans = data.get("plans", [])
if isinstance(plans, list) and all(isinstance(p, str) for p in plans):
logger.info(f"成功生成并解析了 {len(plans)} 个月度计划。")
return plans
else:
logger.error(f"LLM返回的JSON格式不正确或'plans'键不是字符串列表: {response.content}")
return []
# 修复并解析JSON
repaired_json_str = repair_json(clean_content)
data = json.loads(repaired_json_str)
# 使用Pydantic进行验证
validated_response = PlanResponse.model_validate(data)
plans = validated_response.plans
logger.info(f"成功生成并验证了 {len(plans)} 个月度计划。")
return plans
except json.JSONDecodeError:
logger.error(f"无法解析LLM返回的JSON: {response.content}")
logger.error(f"修复后仍然无法解析LLM返回的JSON: {llm_content}")
return []
except ValidationError as e:
logger.error(f"LLM返回的JSON格式不符合预期: {e}\n原始响应: {llm_content}")
return []
except Exception as e: