From 7f490914d5b7696a33d97bb2877617625b89334f Mon Sep 17 00:00:00 2001 From: DrSmoothl <1787882683@qq.com> Date: Tue, 24 Mar 2026 16:17:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=EF=BC=8C=E6=B7=BB=E5=8A=A0=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E7=9B=AE=E6=A0=87=E4=BF=A1=E6=81=AF=E5=92=8C=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=BB=93=E6=9E=9C=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat_target_info_data_model.py | 77 ++++++++++ src/common/data_models/info_data_model.py | 2 +- src/common/data_models/llm_data_model.py | 2 +- .../reply_generation_data_models.py | 142 ++++++++++++++++++ 4 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 src/common/data_models/chat_target_info_data_model.py create mode 100644 src/common/data_models/reply_generation_data_models.py diff --git a/src/common/data_models/chat_target_info_data_model.py b/src/common/data_models/chat_target_info_data_model.py new file mode 100644 index 00000000..0397d55f --- /dev/null +++ b/src/common/data_models/chat_target_info_data_model.py @@ -0,0 +1,77 @@ +from dataclasses import dataclass, field +from typing import Optional, TYPE_CHECKING + +from . import BaseDataModel + +if TYPE_CHECKING: + from src.common.data_models.person_info_data_model import MaiPersonInfo + + +@dataclass +class ChatTargetInfo(BaseDataModel): + """当前聊天目标的轻量摘要信息。 + + 该模型只服务于 Planner、Replyer 和 HFC 等聊天编排层, + 用于描述“这一轮对话里我面对的是谁”。 + 它不是人物档案模型,不承载记忆点、关系历史或统计信息。 + + Attributes: + platform: 目标所在的平台标识。 + user_id: 目标在平台上的原始用户 ID。 + session_nickname: 当前会话里观测到的昵称,优先使用消息现场值。 + person_id: 主程序内部稳定人物 ID;未建档时为空。 + person_name: 主程序内部维护的人物名称;未建档时为空。 + is_known: 该目标是否已经建立人物档案。 + """ + + platform: str = field(default_factory=str) + user_id: str = field(default_factory=str) + session_nickname: str = field(default_factory=str) + person_id: Optional[str] = None + person_name: Optional[str] = None + is_known: bool = False + + @property + def display_name(self) -> str: + """返回用于 Prompt、日志和界面展示的目标名称。""" + if self.person_name: + return self.person_name + if self.session_nickname: + return self.session_nickname + return self.user_id + + @classmethod + def from_person_info( + cls, + platform: str, + user_id: str, + session_nickname: str = "", + person_info: Optional["MaiPersonInfo"] = None, + ) -> "ChatTargetInfo": + """根据当前会话信息和人物档案生成聊天目标摘要。 + + Args: + platform: 当前聊天平台。 + user_id: 当前聊天目标的原始用户 ID。 + session_nickname: 当前会话里观察到的昵称。 + person_info: 可选的人物档案对象。 + + Returns: + ChatTargetInfo: 生成后的轻量聊天目标信息。 + """ + if person_info is None: + return cls( + platform=platform, + user_id=user_id, + session_nickname=session_nickname, + is_known=False, + ) + + return cls( + platform=platform, + user_id=user_id, + session_nickname=session_nickname or person_info.user_nickname, + person_id=person_info.person_id, + person_name=person_info.person_name, + is_known=person_info.is_known, + ) diff --git a/src/common/data_models/info_data_model.py b/src/common/data_models/info_data_model.py index 16de45ba..2860386a 100644 --- a/src/common/data_models/info_data_model.py +++ b/src/common/data_models/info_data_model.py @@ -14,7 +14,7 @@ # # user_nickname: str = field(default_factory=str) # # person_id: Optional[str] = None # # person_name: Optional[str] = None - +# 已重构,见src/common/data_models/chat_target_info_data_model.py # @dataclass # class ActionPlannerInfo(BaseDataModel): diff --git a/src/common/data_models/llm_data_model.py b/src/common/data_models/llm_data_model.py index cb32702e..62632592 100644 --- a/src/common/data_models/llm_data_model.py +++ b/src/common/data_models/llm_data_model.py @@ -20,4 +20,4 @@ # timing: Optional[Dict[str, Any]] = None # processed_output: Optional[List[str]] = None # timing_logs: Optional[List[str]] = None -# TODO: 重构 +# 已重构,见src/common/data_models/reply_generation_data_models.py diff --git a/src/common/data_models/reply_generation_data_models.py b/src/common/data_models/reply_generation_data_models.py new file mode 100644 index 00000000..0f394094 --- /dev/null +++ b/src/common/data_models/reply_generation_data_models.py @@ -0,0 +1,142 @@ +"""回复生成结果相关数据模型。 + +该模块用于描述新版本回复链中的三个层次: + +1. LLM 原始完成结果。 +2. 生成过程中的耗时与调试信息。 +3. 回复链最终返回给上层的结构化结果。 +""" + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Dict, List, Optional + +from . import BaseDataModel + +if TYPE_CHECKING: + from src.common.data_models.message_component_data_model import MessageSequence + from src.llm_models.payload_content.tool_option import ToolCall + + +@dataclass +class LLMCompletionResult(BaseDataModel): + """一次 LLM 调用的原始完成结果。 + + 该模型只描述模型调用本身的输入与输出,不承载回复切分、 + 消息序列拼装或表达方式选择等后处理结果。 + + Attributes: + request_prompt: 实际发送给模型的 Prompt 文本。 + response_text: 模型返回的主文本内容。 + reasoning_text: 模型返回的推理内容。 + model_name: 本次请求实际使用的模型名称。 + tool_calls: 模型返回的工具调用列表。 + """ + + request_prompt: str = field( + default_factory=str, + metadata={"description": "实际发送给模型的 Prompt 文本。"}, + ) + response_text: str = field( + default_factory=str, + metadata={"description": "模型返回的主文本内容。"}, + ) + reasoning_text: str = field( + default_factory=str, + metadata={"description": "模型返回的推理内容。"}, + ) + model_name: str = field( + default_factory=str, + metadata={"description": "本次请求实际使用的模型名称。"}, + ) + tool_calls: List["ToolCall"] = field( + default_factory=list, + metadata={"description": "模型返回的工具调用列表。"}, + ) + + +@dataclass +class GenerationMetrics(BaseDataModel): + """一次生成流程的耗时与调试指标。 + + Attributes: + prompt_ms: Prompt 构建耗时,单位为毫秒。 + llm_ms: LLM 调用耗时,单位为毫秒。 + overall_ms: 整个生成流程总耗时,单位为毫秒。 + stage_logs: 各阶段的简短耗时日志列表。 + extra: 额外指标字典,用于承载不适合单独升格为字段的监控信息。 + """ + + prompt_ms: Optional[float] = field( + default=None, + metadata={"description": "Prompt 构建耗时,单位为毫秒。"}, + ) + llm_ms: Optional[float] = field( + default=None, + metadata={"description": "LLM 调用耗时,单位为毫秒。"}, + ) + overall_ms: Optional[float] = field( + default=None, + metadata={"description": "整个生成流程总耗时,单位为毫秒。"}, + ) + stage_logs: List[str] = field( + default_factory=list, + metadata={"description": "各阶段的简短耗时日志列表。"}, + ) + extra: Dict[str, Any] = field( + default_factory=dict, + metadata={"description": "额外指标字典,用于承载动态监控信息。"}, + ) + + +@dataclass +class ReplyGenerationResult(BaseDataModel): + """回复链的最终结构化结果。 + + 该模型用于承接回复器和生成服务合并后的最终产物,供 HFC、 + BrainChat、发送服务和日志系统继续消费。 + + Attributes: + success: 本次回复生成是否成功。 + completion: LLM 原始完成结果。 + metrics: 本次生成的耗时与调试指标。 + selected_expression_ids: 本次选中的表达方式 ID 列表。 + text_fragments: 对模型输出进行切分、规范化后的文本片段列表。 + message_sequence: 最终可直接发送的消息序列。 + error_message: 失败时的错误描述;成功时为空。 + """ + + success: bool = field( + default=False, + metadata={"description": "本次回复生成是否成功。"}, + ) + completion: LLMCompletionResult = field( + default_factory=LLMCompletionResult, + metadata={"description": "一次 LLM 调用的原始完成结果。"}, + ) + metrics: GenerationMetrics = field( + default_factory=GenerationMetrics, + metadata={"description": "本次生成的耗时与调试指标。"}, + ) + selected_expression_ids: List[int] = field( + default_factory=list, + metadata={"description": "本次选中的表达方式 ID 列表。"}, + ) + text_fragments: List[str] = field( + default_factory=list, + metadata={"description": "对模型输出进行切分、规范化后的文本片段列表。"}, + ) + message_sequence: Optional["MessageSequence"] = field( + default=None, + metadata={"description": "最终可直接发送的消息序列。"}, + ) + error_message: str = field( + default_factory=str, + metadata={"description": "失败时的错误描述;成功时通常为空字符串。"}, + ) + + +__all__ = [ + "GenerationMetrics", + "LLMCompletionResult", + "ReplyGenerationResult", +]