diff --git a/agent/application/states/conversation.py b/agent/application/states/conversation.py index 206a022..e7e93d2 100644 --- a/agent/application/states/conversation.py +++ b/agent/application/states/conversation.py @@ -25,7 +25,7 @@ import reflex as rx from application.states.database import DatabaseState from application.states.models import ( - AgentRunEvent, + AgentRunOutputEvent, Conversation, ConversationHistoryItem, Message, @@ -461,8 +461,8 @@ class ConversationState(rx.State): yield # 通知前端更新渲染 messages.append(message) - # ========== 智能体运行事件 ========== - case AgentRunEvent(content=content): + # ========== 智能体运行输出事件 ========== + case AgentRunOutputEvent(content=content): # 构建消息实例 message = Message( type=MessageType.TEXT, diff --git a/agent/application/states/models.py b/agent/application/states/models.py index aaa459c..c21a930 100644 --- a/agent/application/states/models.py +++ b/agent/application/states/models.py @@ -11,7 +11,6 @@ from pydantic_ai import RunUsage from pydantic_ai._uuid import uuid7 - class NoResult(BaseModel): """ 无结果类 @@ -19,15 +18,35 @@ class NoResult(BaseModel): ... -class AgentRunEvent(BaseModel): + +class InteractionType(StrEnum): """ - 智能体运行事件类 + 交互类型枚举 + """ + + INPUT = "input" + + +class Approval(BaseModel): + """ + 审批类 + """ + + tool_call_id: str = Field(..., description="工具调用唯一标识") + tool_name: str = Field(..., description="工具名称") + interaction_type: InteractionType = Field(..., description="交互类型") + + +class AgentRunOutputEvent(BaseModel): + """ + 智能体运行输出事件类 """ content: str = Field( default="", description="智能体运行内容", ) + approvals: list[Approval] = Field(default_factory=list, description="审批列表") class MessageType(StrEnum): diff --git a/agent/application/workshop/book_flight.py b/agent/application/workshop/book_flight.py index 6c75110..571abae 100644 --- a/agent/application/workshop/book_flight.py +++ b/agent/application/workshop/book_flight.py @@ -3,9 +3,8 @@ 预定航班工作流(范式) """ import datetime -from re import I -from typing import AsyncGenerator, cast, Any -from dataclasses import replace +from typing import Any, AsyncGenerator, cast + from pydantic import BaseModel, Field, field_validator from pydantic_ai import ( Agent, @@ -18,7 +17,6 @@ from pydantic_ai import ( RunUsage, UsageLimits, ) -from pydantic_ai.agent.abstract import S from pydantic_ai.messages import ( AgentStreamEvent, FunctionToolResultEvent, @@ -27,9 +25,13 @@ from pydantic_ai.messages import ( ToolReturnPart, ) from pydantic_ai.run import AgentRunResult, AgentRunResultEvent -from reflex.event import T -from application.states.models import AgentRunEvent, NoResult +from application.states.models import ( + AgentRunOutputEvent, + NoResult, + InteractionType, + Approval, +) from application.workshop.models import ( DEEPSEEK_V4_FLASH_MODEL, DEEPSEEK_V4_FLASH_MODEL_SETTINGS, @@ -204,7 +206,10 @@ async def search_flights(ctx: RunContext[Deps]) -> list[Flight]: return searched_flights -@agent.tool(name="预定航班") +@agent.tool( + name="预定航班", + requires_approval=True, +) async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult: """ 预定航班 @@ -212,9 +217,6 @@ async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult: if (searched_flights := ctx.deps.searched_flights) is None: raise ModelRetry("必须先使用 search_flights 查询航班") - if not ctx.tool_call_approved: - raise ApprovalRequired(metadata={"content": "您要预定哪班航班?"}) - # 提取到的航班号 extracted_flight_number = ( await extraction_flight_number_agent.run( @@ -282,7 +284,7 @@ async def run_stream_events( ) -> AsyncGenerator[ AgentStreamEvent | AgentRunResultEvent[Flight | NoResult | DeferredToolRequests] - | AgentRunEvent + | AgentRunOutputEvent | None, ]: """ @@ -330,7 +332,6 @@ async def run_stream_events( event.part.content = ( "未提取到航班号,请检查后重试" ) - if isinstance(content, Flight): event.part.content = f"已预定航班:\n{content.number} {content.airfare}$ 于 {content.date.strftime('%Y-%m-%d')} 从 {content.origin_airport_code} 到 {content.destination_airport_code}\n" yield event @@ -341,9 +342,20 @@ async def run_stream_events( match result: case AgentRunResult(output=output): match output: - case Flight(): - yield AgentRunEvent(content="预定成功") + case Flight() | NoResult(): + yield event - case DeferredToolRequests(approvals=approvals) as t: - t.metadata = {"1": {"Success": "1"}} - yield event + case DeferredToolRequests(approvals=approvals): + yield AgentRunOutputEvent( + content="请输入航班号", + approvals=[ + Approval( + tool_call_id=approval.tool_call_id, + tool_name=approval.tool_name, + interaction_type=InteractionType.INPUT, + ) + for approval in approvals + if approval.tool_name == "预定航班" + ], + ) + yield event