From c499b16c3b1bfc1d92b46e2c5dd8d2574b706045 Mon Sep 17 00:00:00 2001 From: liubiren Date: Fri, 3 Jul 2026 18:04:14 +0800 Subject: [PATCH] 1 --- 智能体/application/models.py | 20 ++- 智能体/application/state/conversation.py | 207 +++++++---------------- 2 files changed, 75 insertions(+), 152 deletions(-) diff --git a/智能体/application/models.py b/智能体/application/models.py index 9bc686b..ec5d15c 100644 --- a/智能体/application/models.py +++ b/智能体/application/models.py @@ -69,9 +69,23 @@ class PartKind(StrEnum): RUN_RETURN = "run-return" +class Kind(StrEnum): + """分段种类""" + + THINKING = "thinking" + TOOL_SEARCH = "tool-search" + CAPABILITY_LOAD = "capability-load" + TOOL_CALL = "tool-call" + TEXT = "text" + TOOL_RETURN = "tool-return" + RETRY_PROMPT = "retry-prompt" + RUN_RETURN = "run-return" + + class Reasoning(BaseModel): """推理类""" + kind: Kind = Field(..., description="推理类型") content: str = Field(default="", description="推理内容") @@ -84,11 +98,7 @@ class Run(BaseModel): ) is_reasoning: bool = Field( default=False, - description="推理状态,True 表示正在推理,False 表示非正在推理", - ) - is_collapsed: bool = Field( - default=False, - description="推理面板折叠状态,True 表示折叠,False 表示展开", + description="正在推理状态,True 表示正在推理,False 表示非正在推理", ) assistant_content: str = Field(default="", description="回复正文") is_streaming: bool = Field( diff --git a/智能体/application/state/conversation.py b/智能体/application/state/conversation.py index d229677..b6b9af4 100644 --- a/智能体/application/state/conversation.py +++ b/智能体/application/state/conversation.py @@ -25,7 +25,7 @@ from pydantic_ai.models.openai import OpenAIChatModel from pydantic_ai.providers.openai import OpenAIProvider import reflex as rx -from application.models import Conversation, EventKind, PartKind, Run, Event, EventKind, PartKind +from application.models import Conversation, EventKind, PartKind, Run, Reasoning, Kind from application.state.create_conversation_modal import CreateConversationModalState from application.state.database import DatabaseState @@ -52,15 +52,10 @@ class ConversationState(rx.State): 对话状态 """ - # 初始化当前对话唯一标识 - conversation_id: str = str(uuid7()) # 初始化对话字典 - conversations: Dict[str, Conversation] = {conversation_id: Conversation()} - - # 初始化当前运行唯一标识 - run_id: Optional[str] = None - # 初始化当前运行工具调用唯一标识与片段索引映射表 - run_mapping: dict[str, int] = {} + conversations: Dict[str, Conversation] = {str(uuid7()): Conversation()} + # 当前对话唯一标识 + conversation_id: str = next(reversed(conversations.keys())) # 初始化智能体 agent: Agent = Agent( @@ -97,12 +92,10 @@ class ConversationState(rx.State): if not description: description = "新对话" - # 生成并设置为当前对话唯一标识 - self.conversation_id = str(uuid7()) # 创建对话 - self.conversations[self.conversation_id] = Conversation(description=description) - # 初始化当前运行唯一标识 - self.run_id = None + self.conversations[str(uuid7())] = Conversation(description=description) + # 将末位对话唯一标识设置为当前对话唯一标识 + self.conversation_id = next(reversed(self.conversations.keys())) # 获取创建对话模态窗状态 create_conversation_modal_state = await self.get_state( @@ -122,24 +115,18 @@ class ConversationState(rx.State): return del self.conversations[conversation_id] + # 删除后,若对话字典为空则创建对话 if not self.conversations: - # 生成并设置为当前对话唯一标识 - self.conversation_id = str(uuid7()) - # 创建对话 - self.conversations[self.conversation_id] = Conversation() - # 初始化当前运行唯一标识 - self.run_id = None + self.conversations[str(uuid7())] = Conversation() + # 删除后,若当前对话唯一标识不存在则将末位对话唯一标识设置为当前对话唯一标识 if self.conversation_id not in self.conversations: - # 将末位对话唯一标识设置为当前对话唯一标识 self.conversation_id = next(reversed(self.conversations.keys())) - # 初始化当前运行唯一标识 - self.run_id = next(reversed(self.conversations[self.conversation_id].runs.keys()), None) @rx.event def switch_conversation(self, conversation_id: str) -> None: """ - 设置为当前对话唯一标识 + 将指定对话唯一标识设置为当前对话唯一标识 :param conversation_id: 指定对话唯一标识 :return: None """ @@ -194,9 +181,6 @@ class ConversationState(rx.State): if not conversation: return - # 更新当前运行唯一标识 - self.run_id = str(uuid7()) - # 获取用户提示词 user_prompt = form_data.get("user_prompt", "").strip() if not user_prompt: @@ -209,8 +193,16 @@ class ConversationState(rx.State): conversation_id=self.conversation_id ) - # 重置当前运行工具调用唯一标识与片段索引映射表 - self.run_mapping.clear() + # 初始化工具调用唯一标识与片段索引映射字典 + tool_call_ids : Dict[int, str] = {} + + # 创建运行 + conversation.runs[str(uuid7())] = Run(user_prompt=user_prompt) + # 将末位运行唯一标识、运行设置为当前运行唯一标识、运行 + run_id, run = next(reversed(conversation.runs.items())) + # 将流式输出状态设置为正在流式输出 + run.is_streaming = True + yield # 通知前端渲染流式输出状态 async for event in self.agent.run_stream_events( conversation_id=self.conversation_id, @@ -220,7 +212,6 @@ class ConversationState(rx.State): match event: # ========== 分片开始事件 ========== case PartStartEvent( - event_kind=event_kind, index=index, part=part, previous_part_kind=previous_part_kind, @@ -230,113 +221,56 @@ class ConversationState(rx.State): case ThinkingPart( part_kind=part_kind, content=content ): - yield Event( - event_kind=EventKind(event_kind), - event_content=content, - part_index=index, - previous_part_kind=PartKind( - previous_part_kind - ), - part_kind=PartKind(part_kind), - ) - # 检索工具分片开始事件 + # 若上一分片种类为空则将正在推理状态设置为正在推理 + if not previous_part_kind: + run.is_reasoning = True + + # 初始化推理 + run.reasonings[index] = Reasoning(kind=Kind.THINKING, content=content) + yield # 通知前端渲染推理 + case ToolSearchCallPart( - tool_kind=tool_kind, - tool_call_id=tool_call_id, - tool_name=tool_name, + tool_call_id=tool_call_id ): - # 记录工具调用唯一标识与片段索引映射 - self.tool_call_ids[tool_call_id] = index - yield Event( - event_kind=EventKind(event_kind), - part_index=index, - previous_part_kind=PartKind( - previous_part_kind - ), - part_kind=PartKind(tool_kind), - tool_name=tool_name, - run_id=current_run_id, - ) - # 加载能力分片开始事件 - case LoadCapabilityCallPart( - tool_kind=tool_kind, - tool_call_id=tool_call_id, - tool_name=tool_name, - ): - # 记录工具调用唯一标识与片段索引映射 - self.tool_call_ids[tool_call_id] = index - yield Event( - event_kind=EventKind(event_kind), - part_index=index, - previous_part_kind=PartKind( - previous_part_kind - ), - part_kind=PartKind(tool_kind), - tool_name=tool_name, - run_id=current_run_id, - ) - # 调用工具分片开始事件 - case ToolCallPart( - part_kind=part_kind, - tool_call_id=tool_call_id, - tool_name=tool_name, - ): - # 记录工具调用唯一标识与片段索引映射 - self.tool_call_ids[tool_call_id] = index - yield Event( - event_kind=EventKind(event_kind), - part_index=index, - previous_part_kind=PartKind( - previous_part_kind - ), - part_kind=PartKind(part_kind), - tool_name=tool_name, - run_id=current_run_id, - ) + # 创建工具调用唯一标识与片段索引映射 + tool_call_ids[index] = tool_call_id + + # 初始化推理 + run.reasonings[index] = Reasoning(kind=Kind.TOOL_SEARCH, content="正在检索") + yield # 通知前端渲染推理 + # 文本分片开始事件 case TextPart( part_kind=part_kind, content=content ): - yield Event( - event_kind=EventKind(event_kind), - event_content=content, - part_index=index, - previous_part_kind=PartKind( - previous_part_kind - ), - part_kind=PartKind(part_kind), - run_id=current_run_id, - ) + # 初始化回复正文 + run.assistant_content = content + yield # 通知前端渲染回复正文 + + case _: + continue # ========== 分片增量事件(前端仅就文本片段实现打字机效果) ========== case PartDeltaEvent( - event_kind=event_kind, index=index, delta=delta + index=index, delta=delta ): match delta: # 思考分片增量事件 case ThinkingPartDelta( - part_delta_kind=part_delta_kind, content_delta=content_delta, ): - yield Event( - event_kind=EventKind(event_kind), - event_content=content_delta or "", - part_index=index, - part_kind=PartKind(part_delta_kind), - run_id=current_run_id, - ) + run.reasonings[index].content += content_delta or "" + yield # 通知前端渲染推理 + # 文本分片增量事件 case TextPartDelta( - part_delta_kind=part_delta_kind, content_delta=content_delta, ): - yield Event( - event_kind=EventKind(event_kind), - event_content=content_delta, - part_index=index, - part_kind=PartKind(part_delta_kind), - run_id=current_run_id, - ) + run.assistant_content += content_delta + yield # 通知前端渲染回复正文 + + case _: + continue # ========== 分片结束事件 ========== case PartEndEvent( @@ -350,26 +284,17 @@ class ConversationState(rx.State): case ThinkingPart( part_kind=part_kind, content=content ): - yield Event( - event_kind=EventKind(event_kind), - event_content=content, - part_index=index, - part_kind=PartKind(part_kind), - next_part_kind=PartKind(next_part_kind), - run_id=current_run_id, - ) + # 若下一分片种类为文本则将正在推理状态设置为非正在推理 + if next_part_kind == Kind.TEXT: + run.is_reasoning = False + # 检索工具分片结束事件 case ToolSearchCallPart( - tool_kind=tool_kind, tool_name=tool_name + tool_call_id=tool_call_id ): - yield Event( - event_kind=EventKind(event_kind), - part_index=index, - part_kind=PartKind(tool_kind), - next_part_kind=PartKind(next_part_kind), - tool_name=tool_name, - run_id=current_run_id, - ) + # 创建工具调用唯一标识与片段索引映射 + tool_call_ids[index] = tool_call_id + # 加载能力分片结束事件 case LoadCapabilityCallPart( tool_kind=tool_kind, tool_name=tool_name @@ -394,18 +319,6 @@ class ConversationState(rx.State): tool_name=tool_name, run_id=current_run_id, ) - # 文本分片结束事件 - case TextPart( - part_kind=part_kind, content=content - ): - yield Event( - event_kind=EventKind(event_kind), - event_content=content, - part_index=index, - part_kind=PartKind(part_kind), - next_part_kind=PartKind(next_part_kind), - run_id=current_run_id, - ) # ========== 函数工具调用事件 ========== case FunctionToolCallEvent(