Files
922d092650 ♻️ refactor(core): 重构 AI 能力与定时任务调度系统 (#2148)
* ♻️ refactor(core): 重构 AI 能力与定时任务调度系统

- 【AI 能力与工具】重构 Capability 注册与管理机制,引入 CapabilityManager 统一管理
- 移除全局能力注册表,改用声明式装饰器 `@capability` 进行解耦注册
- 重构工具解析器链,使用统一的 BaseToolResolver 代替原有的多个特定解析器
- 增强工具查询过滤,支持通配符匹配、工具箱过滤和排除标签
- 【定时任务调度】重构定时任务管理器,引入 SchedulerRegistry 统一管理任务元数据
- 引入 JobConfig 聚合定时任务配置,支持用户维度的定时任务调度
- 重构执行分发器,支持并发限制、串行间隔和随机延迟打散
- 【运行上下文】引入 ScheduledDeps 以支持后台和定时任务环境下的依赖注入
- 优化 RunContext,支持从定时任务上下文快速构造,并提供 emit 辅助方法
- 【日志与监控】引入 AILoggerProxy,实现 AI 各模块的专属日志输出
- 将各模块的全局 logger 替换为对应的模块专属日志代理
- 【其他优化】修复 Pydantic V1 兼容层中 model_validator 的装饰器兼容性问题
- 在非交互式环境(如定时任务)中自动隐藏 HITL 交互工具以节省 Token

* ♻️ refactor(core): 优化内部导入路径并提升 Pydantic 兼容性

- 【重构】将 `services/ai` 模块内的绝对导入重构为相对导入,优化包结构
- 【重构】移除不必要的 `if TYPE_CHECKING` 保护,通过 `from __future__ import annotations` 直接导入类型
- 【清理】清理 `core/messages/types.py` 中未使用的 `AssistantContentUnion` 等联合类型定义
- 【优化】在 `utils/pydantic_compat.py` 中新增 `model_rebuild` 兼容函数,统一 Pydantic V1/V2 的模型重建逻辑
- 【优化】将部分函数内部的延迟导入提升至模块顶部,规范代码结构

* ♻️ refactor(imports): 优化导入路径为相对导入并清理冗余导入

- 【重构】将 AI 服务相关模块中的绝对导入路径修改为相对导入,提升模块内聚性与可移植性
- 【清理】移除多处函数内部或类方法中未使用的冗余导入,避免循环引用和资源浪费
- 【格式化】微调部分工具装饰器和返回语句的格式与尾随逗号

* 🚨 auto fix by pre-commit hooks

---------

Co-authored-by: webjoin111 <455457521@qq.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-10 09:14:06 +08:00

411 lines
14 KiB
Python

"""
AI 模块配置数据域类型定义
"""
from __future__ import annotations
from collections.abc import Callable
from enum import Enum
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar
from pydantic import BaseModel, ConfigDict, Field
from zhenxun.utils.pydantic_compat import model_copy, model_dump, model_validate
if TYPE_CHECKING:
from zhenxun.services.ai.llm.builder import IntentBuilder
T = TypeVar("T")
class ResponseFormat(Enum):
"""响应格式枚举"""
TEXT = "text"
JSON = "json"
MULTIMODAL = "multimodal"
class StructuredOutputStrategy(str, Enum):
"""结构化输出策略"""
NATIVE = "native"
"""使用原生 API (如 OpenAI json_object/json_schema, Gemini mime_type)"""
TOOL_CALL = "tool_call"
"""构造虚假工具调用来强制输出结构化数据 (适用于指令跟随弱但工具调用强的模型)"""
PROMPT = "prompt"
"""仅在 Prompt 中追加 Schema 说明,依赖文本补全"""
class EmbeddingTaskType(str, Enum):
"""
文本嵌入任务类型 (对应 Gemini embedding 模型的 task_type 参数)
"""
RETRIEVAL_QUERY = "RETRIEVAL_QUERY"
"""指定给定的文本是搜索/检索设置中的查询 (Query)。"""
RETRIEVAL_DOCUMENT = "RETRIEVAL_DOCUMENT"
"""指定给定的文本是被搜索语料库中的文档 (Document)。"""
SEMANTIC_SIMILARITY = "SEMANTIC_SIMILARITY"
"""指定文本将用于语义文本相似度 (STS) 计算。"""
CLASSIFICATION = "CLASSIFICATION"
"""指定嵌入向量将用于文本分类任务。"""
CLUSTERING = "CLUSTERING"
"""指定嵌入向量将用于聚类任务。"""
QUESTION_ANSWERING = "QUESTION_ANSWERING"
"""指定文本将用于问答任务。"""
FACT_VERIFICATION = "FACT_VERIFICATION"
"""指定文本将用于事实核查任务。"""
class BaseOutputDefinition(Generic[T]):
"""声明式结构化输出基类"""
type_: type[T]
class ToolOutput(BaseOutputDefinition[T]):
"""工具输出标记:使用强制工具调用 (Tool Call) 结束任务并返回指定结构"""
name: str | None = None
description: str | None = None
strict: bool | None = None
def __init__(
self,
type_: type[T],
name: str | None = None,
description: str | None = None,
strict: bool | None = None,
):
self.type_ = type_
self.name = name
self.description = description
self.strict = strict
class ReasoningEffort(str, Enum):
"""推理努力程度枚举"""
NONE = "NONE"
"""不开启推理思考"""
MINIMAL = "MINIMAL"
"""极低推理努力,追求最快响应"""
LOW = "LOW"
"""较低推理努力"""
MEDIUM = "MEDIUM"
"""中等推理努力(通常是默认值)"""
HIGH = "HIGH"
"""高推理努力"""
XHIGH = "XHIGH"
"""极高推理努力"""
MAX = "MAX"
"""最大推理努力 (如 GLM / DeepSeek)"""
class ImageAspectRatio(str, Enum):
"""图像宽高比枚举"""
SQUARE = "1:1"
"""正方形"""
LANDSCAPE_16_9 = "16:9"
"""横向宽屏 16:9"""
PORTRAIT_9_16 = "9:16"
"""竖向全屏 9:16"""
LANDSCAPE_4_3 = "4:3"
"""横向标准 4:3"""
PORTRAIT_3_4 = "3:4"
"""竖向标准 3:4"""
LANDSCAPE_3_2 = "3:2"
"""横向 3:2"""
PORTRAIT_2_3 = "2:3"
"""竖向 2:3"""
class ImageResolution(str, Enum):
"""图像分辨率/质量枚举"""
STANDARD = "STANDARD"
"""标准分辨率"""
HD = "HD"
"""高清分辨率"""
class CommonLLMConfig(BaseModel):
"""三大厂商通用基础生成参数"""
temperature: float | None = Field(default=None, ge=0.0, le=2.0)
"""采样温度。较高的值会使输出更加随机,较低的值会使其更加集中和确定。"""
max_tokens: int | None = Field(default=None, gt=0)
"""聊天完成时生成的最大 Token 数。"""
top_p: float | None = Field(default=None, ge=0.0, le=1.0)
"""核采样 (Nucleus sampling) 概率阈值。"""
top_k: int | None = Field(default=None, gt=0)
"""仅从概率最高的前 K 个 Token 中采样 (并非所有模型支持)。"""
frequency_penalty: float | None = Field(default=None, ge=-2.0, le=2.0)
"""频率惩罚。正值根据新 Token 在文本中的现有频率对其进行惩罚,
降低模型逐字重复同一行的可能性。"""
presence_penalty: float | None = Field(default=None, ge=-2.0, le=2.0)
"""存在惩罚。正值根据新 Token 到目前为止是否出现在文本中对其进行惩罚,
增加模型谈论新主题的可能性。"""
repetition_penalty: float | None = Field(default=None, ge=0.0, le=2.0)
"""重复惩罚系数 (部分非 OpenAI 兼容模型独有)。"""
stop: list[str] | str | None = Field(default=None)
"""API 停止生成后续 Token 的停止词序列。"""
reasoning_effort: ReasoningEffort | str | None = Field(default=None)
"""跨厂商统一的思考/推理等级意图(如 'low', 'high', 'max')。
具体映射由底层的 Adapter 执行。"""
class OutputFormatConfig(BaseModel):
"""输出格式与结构化控制"""
response_format: ResponseFormat | dict[str, Any] | None = Field(default=None)
"""响应格式类型 (枚举或字典形式的 json_schema 对象)。"""
response_mime_type: str | None = Field(default=None)
"""指定 MIME 类型 (如 application/json),主要用于 Gemini。"""
response_schema: dict[str, Any] | None = Field(default=None)
"""JSON Schema 字典,用于强制约束模型返回的 JSON 结构。"""
response_modalities: list[str] | None = Field(default=None)
"""允许的响应模态 (如 ["TEXT", "IMAGE"]),主要用于 Gemini。"""
structured_output_strategy: StructuredOutputStrategy | str | None = Field(
default=None
)
"""结构化输出所采用的内部策略。"""
class ToolCallConfig(BaseModel):
"""工具调用统一策略配置"""
mode: Literal["AUTO", "ANY", "NONE"] = Field(default="AUTO")
"""工具调用模式 (AUTO: 自动, ANY: 强制至少调一个, NONE: 禁用)。"""
allowed_function_names: list[str] | None = Field(default=None)
"""允许被调用的特定函数名称白名单。"""
include_server_side_tool_invocations: bool | None = Field(default=None)
"""是否包含服务端侧的工具调用日志流转 (主要用于 Gemini)。"""
class BaseProviderOption(BaseModel):
"""厂商配置逃生舱基类"""
model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") # type: ignore
class OpenAIOptions(BaseProviderOption):
"""OpenAI 专属特权参数 (适配 Responses API)"""
store: bool | None = Field(default=None)
"""是否允许服务端留存本次请求的选项记录"""
metadata: dict[str, str] | None = Field(default=None)
"""附加在请求上的自定义元数据"""
class GeminiOptions(BaseProviderOption):
"""Gemini 专属特权参数"""
include_thoughts: bool | None = Field(default=None)
"""是否在最终响应中包含模型的内部思考过程 (Thoughts)"""
safety_settings: dict[str, str] | None = Field(default=None)
"""Gemini 专有的各个维度的安全过滤阈值配置"""
retrieval_config: dict[str, Any] | None = Field(default=None)
"""检索定位配置,如 LBS 经纬度信息,配合 Google Maps 工具使用"""
class OpenAITTSOptions(BaseProviderOption):
"""OpenAI TTS 专属特权参数"""
voice_id: str | None = Field(default=None)
"""专属的音色 ID 配置"""
class GeminiTTSOptions(BaseProviderOption):
"""Gemini TTS 专属特权参数"""
voice_id: str | None = Field(default=None)
"""专属的音色 ID 配置"""
multi_speaker: bool | None = Field(default=None)
"""是否开启多说话人模式"""
second_voice: str | None = Field(default=None)
"""多说话人模式下的第二音色名称"""
class MiniMaxTTSOptions(BaseProviderOption):
"""MiniMax TTS 专属特权参数 (控制极度精细)"""
voice_id: str | None = Field(default=None)
"""专属的音色 ID 配置"""
vol: float | None = Field(default=None, gt=0.0, le=10.0)
"""音量,范围 (0, 10]"""
pitch: int | None = Field(default=None, ge=-12, le=12)
"""语调,范围 [-12, 12]"""
emotion: (
Literal[
"happy",
"sad",
"angry",
"fearful",
"disgusted",
"surprised",
"calm",
"fluent",
"whisper",
]
| None
) = Field(default=None)
"""情感控制"""
timbre_weights: list[dict[str, Any]] | None = Field(default=None)
"""音色混合权重 (最多4种)"""
pronunciation_dict: dict[str, list[str]] | None = Field(default=None)
"""自定义发音字典 (如: {"tone": ["处理/(chu3)(li3)"]})"""
class MiMoTTSOptions(BaseProviderOption):
"""MiMo TTS 专属特权参数"""
voice_id: str | None = Field(default=None)
"""专属的音色 ID 配置"""
class MediaGenerationConfig(BaseModel):
"""多模态媒体生成/优化的全局配置"""
aspect_ratio: ImageAspectRatio | str | None = Field(default=None)
"""生成的图像/视频宽高比 (如 '16:9')"""
resolution: str | None = Field(default=None)
"""生成的图像/视频分辨率 (如 '1K', '4K' 或 '1024x1024')"""
quality: Literal["low", "medium", "high", "standard", "hd"] | None = Field(
default=None
)
"""渲染质量及细节丰富水平"""
class TTSConfig(BaseModel):
"""文本转语音 (TTS) 全局配置"""
response_format: Literal["mp3", "wav", "pcm", "flac", "opus", "aac"] = Field(
default="mp3"
)
"""输出音频格式"""
speed: float = Field(default=1.0, ge=0.25, le=4.0)
"""语速 (通用映射)"""
openai_options: OpenAITTSOptions = Field(default_factory=OpenAITTSOptions)
"""OpenAI 厂商专属请求参数集"""
gemini_options: GeminiTTSOptions = Field(default_factory=GeminiTTSOptions)
"""Gemini 厂商专属请求参数集"""
minimax_options: MiniMaxTTSOptions = Field(default_factory=MiniMaxTTSOptions)
"""MiniMax 厂商专属请求参数集"""
mimo_options: MiMoTTSOptions = Field(default_factory=MiMoTTSOptions)
"""MiMo 厂商专属请求参数集"""
custom_kwargs: dict[str, Any] = Field(default_factory=dict)
"""兜底逃生舱,包含的键值对将直接透传至顶层请求体中 (可用于缓存 TTL)"""
model_config = ConfigDict(arbitrary_types_allowed=True)
class GenerationConfig(BaseModel):
"""
现代化 LLM 生成基座 (Intent-Driven Base)。
隔离通用参数与厂商私有参数,彻底终结"上帝类"。
"""
common: CommonLLMConfig = Field(default_factory=CommonLLMConfig)
"""大模型生成核心通用配置项"""
output: OutputFormatConfig = Field(default_factory=OutputFormatConfig)
"""输出格式与约束控制配置项"""
tools: ToolCallConfig = Field(default_factory=ToolCallConfig)
"""工具调用策略与函数声明配置项"""
media: MediaGenerationConfig = Field(default_factory=MediaGenerationConfig)
"""多媒体生成相关配置项"""
openai_options: OpenAIOptions = Field(default_factory=OpenAIOptions)
"""OpenAI 厂商专属请求参数集"""
gemini_options: GeminiOptions = Field(default_factory=GeminiOptions)
"""Gemini 厂商专属请求参数集"""
enable_caching: bool | None = Field(default=None)
"""是否在此次生成中开启上下文缓存 (Context Caching)"""
custom_kwargs: dict[str, Any] = Field(default_factory=dict)
"""兜底逃生舱,包含的键值对将直接透传至顶层请求体中"""
validation_policy: dict[str, Any] | None = Field(default=None)
"""自定义验证策略字典"""
response_validator: Callable[[Any], None] | None = Field(default=None)
"""针对原始返回对象的自定义回调验证器"""
model_config = ConfigDict(arbitrary_types_allowed=True)
@classmethod
def builder(cls) -> "IntentBuilder":
from zhenxun.services.ai.llm.builder import IntentBuilder
return IntentBuilder()
def to_dict(self) -> dict[str, Any]:
return model_dump(self, exclude_none=True)
def merge_with(self, other: "GenerationConfig | None") -> "GenerationConfig":
"""深度合并两个配置,实现配置的无损叠加"""
if not other:
return model_copy(self, deep=True)
base_dump = model_dump(self, exclude_none=True)
other_dump = model_dump(other, exclude_none=True)
def deep_merge(d1: dict, d2: dict) -> dict:
res = d1.copy()
for k, v in d2.items():
if isinstance(v, dict) and k in res and isinstance(res[k], dict):
res[k] = deep_merge(res[k], v)
else:
res[k] = v
return res
merged_dump = deep_merge(base_dump, other_dump)
return model_validate(GenerationConfig, merged_dump)
class LLMEmbeddingConfig(BaseModel):
"""Embedding 专用配置"""
task_type: str | None = Field(default=None)
"""生成意图的任务类型,参考 EmbeddingTaskType (主要用于 Gemini 和 Jina)"""
output_dimensionality: int | None = Field(default=None)
"""请求模型强制输出(或截断)的较低维度数,实现维度压缩"""
title: str | None = Field(default=None)
"""提供该文档的标题以供底层优化。仅在 task_type 为 RETRIEVAL_DOCUMENT 时有效。"""
encoding_format: str | None = Field(default="float")
"""向量数据在响应中的编码格式 (通常为 float 或 base64)"""
multimodal: bool | list[str] = Field(default=False)
"""是否允许多模态向量化。False 表示纯文本(极速安全);True 表示全部放行;
也可传入 ['image', 'text'] 细粒度控制。"""
custom_kwargs: dict[str, Any] = Field(default_factory=dict)
"""兜底逃生舱,包含的键值对将直接透传至顶层请求体中 (可用于缓存 TTL)"""
model_config = ConfigDict(arbitrary_types_allowed=True)
__all__ = [
"BaseProviderOption",
"CommonLLMConfig",
"EmbeddingTaskType",
"GeminiOptions",
"GeminiTTSOptions",
"GenerationConfig",
"ImageAspectRatio",
"ImageResolution",
"LLMEmbeddingConfig",
"MiMoTTSOptions",
"MiniMaxTTSOptions",
"OpenAIOptions",
"OpenAITTSOptions",
"OutputFormatConfig",
"ReasoningEffort",
"ResponseFormat",
"StructuredOutputStrategy",
"TTSConfig",
"ToolCallConfig",
"ToolOutput",
]