This commit is contained in:
parent
7df4e51bbb
commit
5abfc83d4c
|
|
@ -0,0 +1,36 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 3a74f94a7ad4
|
||||
Revises: d52f1ee0f9c4
|
||||
Create Date: 2026-09-03 12:30:13.538072
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '3a74f94a7ad4'
|
||||
down_revision: Union[str, Sequence[str], None] = 'd52f1ee0f9c4'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('conversationrecord', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('workflow', sqlmodel.sql.sqltypes.AutoString(), server_default=sa.text("('')"), nullable=False))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('conversationrecord', schema=None) as batch_op:
|
||||
batch_op.drop_column('workflow')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -418,9 +418,9 @@ def thinking(item: MessageHistoryItem) -> rx.Component:
|
|||
)
|
||||
|
||||
|
||||
def result_output(item: MessageHistoryItem) -> rx.Component:
|
||||
def output(item: MessageHistoryItem) -> rx.Component:
|
||||
"""
|
||||
结果输出
|
||||
输出
|
||||
:return: Component
|
||||
"""
|
||||
return rx.markdown(
|
||||
|
|
@ -550,7 +550,7 @@ def message_history_item(item: MessageHistoryItem) -> rx.Component:
|
|||
item.type,
|
||||
(MessageType.USER_PROMPT, user_prompt(item)),
|
||||
(MessageType.THINKING, thinking(item)),
|
||||
(MessageType.RESULT_OUTPUT, result_output(item)),
|
||||
(MessageType.OUTPUT, output(item)),
|
||||
),
|
||||
padding="0 16px",
|
||||
width="100%",
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@
|
|||
会话状态
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import AsyncGenerator
|
||||
from typing import AsyncGenerator, cast
|
||||
|
||||
from pydantic_ai.messages import (
|
||||
FunctionToolCallEvent,
|
||||
FunctionToolResultEvent,
|
||||
LoadCapabilityCallPart,
|
||||
ToolReturnPart,
|
||||
PartDeltaEvent,
|
||||
PartEndEvent,
|
||||
PartStartEvent,
|
||||
|
|
@ -24,7 +25,7 @@ import reflex as rx
|
|||
|
||||
from application.states.database import DatabaseState
|
||||
from application.states.models import (
|
||||
AgentRunWarmUpEvent,
|
||||
AgentRunEvent,
|
||||
Conversation,
|
||||
ConversationHistoryItem,
|
||||
Message,
|
||||
|
|
@ -32,9 +33,9 @@ from application.states.models import (
|
|||
MessageType,
|
||||
Workflow,
|
||||
WorkflowType,
|
||||
deps_dump_python,
|
||||
deps_validate_python,
|
||||
usage_validate_python,
|
||||
workflow_dump_json,
|
||||
workflow_validate_json,
|
||||
usage_validate_json,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -306,6 +307,8 @@ class ConversationState(rx.State):
|
|||
|
||||
# 初始化片段索引映射为消息实例唯一标识字典
|
||||
index_map_to_message_id: dict[int, str] = {}
|
||||
# 初始化工具调用片段工具名称映射为消息实例唯一标识字典
|
||||
tool_name_map_to_message_id: dict[str, str] = {}
|
||||
|
||||
# 初始化工作流
|
||||
if conversation.workflow:
|
||||
|
|
@ -317,12 +320,12 @@ class ConversationState(rx.State):
|
|||
|
||||
# 运行并流式输出事件
|
||||
stream_events = run_stream_events(
|
||||
deps=deps_validate_python(dict(conversation.workflow.deps)),
|
||||
deps=dict(conversation.workflow.deps),
|
||||
user_prompt=user_prompt,
|
||||
message_history=await db_state.get_message_history(
|
||||
conversation_id=self.actived_conversation_id
|
||||
),
|
||||
usage=usage_validate_python(dict(conversation.usage)),
|
||||
usage=conversation.usage,
|
||||
)
|
||||
else:
|
||||
from application.workshop.talk import (
|
||||
|
|
@ -335,7 +338,7 @@ class ConversationState(rx.State):
|
|||
message_history=await db_state.get_message_history(
|
||||
conversation_id=self.actived_conversation_id
|
||||
),
|
||||
usage=usage_validate_python(dict(conversation.usage)),
|
||||
usage=conversation.usage,
|
||||
)
|
||||
|
||||
# 消息列表
|
||||
|
|
@ -352,7 +355,7 @@ class ConversationState(rx.State):
|
|||
part=part,
|
||||
):
|
||||
match part:
|
||||
# 思考分片开始事件
|
||||
# 思考分片
|
||||
case ThinkingPart(content=content):
|
||||
# 构建消息实例
|
||||
message = Message(
|
||||
|
|
@ -367,10 +370,24 @@ class ConversationState(rx.State):
|
|||
index_map_to_message_id[index] = message.id
|
||||
yield # 通知前端更新渲染
|
||||
|
||||
# 文本分片开始事件
|
||||
# 工具调用分片
|
||||
case ToolCallPart(tool_name=tool_name):
|
||||
# 构建消息实例
|
||||
message = Message(
|
||||
type=MessageType.TOOL_CALL,
|
||||
title=f"正在{tool_name}",
|
||||
is_running=True,
|
||||
)
|
||||
# 添加至消息字典
|
||||
conversation.messages[message.id] = message
|
||||
# 将消息实例唯一标识与工具调用片段工具名称映射
|
||||
tool_name_map_to_message_id[tool_name] = message.id
|
||||
yield # 通知前端更新渲染
|
||||
|
||||
# 文本分片
|
||||
case TextPart(content=content):
|
||||
# 构建消息实例
|
||||
message = Message(type=MessageType.TEXT, content=content)
|
||||
message = Message(type=MessageType.OUTPUT, content=content)
|
||||
# 添加至消息字典
|
||||
conversation.messages[message.id] = message
|
||||
# 将消息实例唯一标识与片段索引映射
|
||||
|
|
@ -380,7 +397,7 @@ class ConversationState(rx.State):
|
|||
# ========== 增量事件 ==========
|
||||
case PartDeltaEvent(index=index, delta=delta):
|
||||
match delta:
|
||||
# 思考分片增量事件
|
||||
# 思考分片增量
|
||||
case ThinkingPartDelta(
|
||||
content_delta=content_delta,
|
||||
):
|
||||
|
|
@ -391,7 +408,7 @@ class ConversationState(rx.State):
|
|||
].content += content_delta
|
||||
yield # 通知前端更新渲染
|
||||
|
||||
# 文本分片增量事件
|
||||
# 文本分片增量
|
||||
case TextPartDelta(
|
||||
content_delta=content_delta,
|
||||
):
|
||||
|
|
@ -407,17 +424,18 @@ class ConversationState(rx.State):
|
|||
part=part,
|
||||
):
|
||||
match part:
|
||||
# 思考分片结束事件
|
||||
# 思考分片
|
||||
case ThinkingPart(content=content):
|
||||
# 获取消息实例
|
||||
message = conversation.messages[
|
||||
index_map_to_message_id[index]
|
||||
]
|
||||
message.is_running = False
|
||||
message.title = "思考完成"
|
||||
message.title = "思考已完成"
|
||||
yield # 通知前端更新渲染
|
||||
messages.append(message)
|
||||
|
||||
# 文本分片
|
||||
case TextPart(content=content):
|
||||
# 获取消息实例
|
||||
message = conversation.messages[
|
||||
|
|
@ -425,11 +443,27 @@ class ConversationState(rx.State):
|
|||
]
|
||||
messages.append(message)
|
||||
|
||||
# ========== 智能体运行预热事件 ==========
|
||||
case AgentRunWarmUpEvent(content=content):
|
||||
# ========== 工具调用结果事件 ==========
|
||||
case FunctionToolResultEvent(part=part):
|
||||
match part:
|
||||
# 工具返回分片
|
||||
case ToolReturnPart(tool_name=tool_name, content=content):
|
||||
# 获取消息实例
|
||||
message = conversation.messages[
|
||||
tool_name_map_to_message_id[tool_name]
|
||||
]
|
||||
message.title = f"{tool_name}已完成"
|
||||
message.content = cast(
|
||||
str, content
|
||||
) # 约定工具返回分片内容必为字符串
|
||||
yield # 通知前端更新渲染
|
||||
messages.append(message)
|
||||
|
||||
# ========== 智能体运行事件 ==========
|
||||
case AgentRunEvent(content=content):
|
||||
# 构建消息实例
|
||||
message = Message(
|
||||
type=MessageType.TEXT,
|
||||
type=MessageType.OUTPUT,
|
||||
content=content,
|
||||
)
|
||||
# 添加至消息字典
|
||||
|
|
@ -439,9 +473,11 @@ class ConversationState(rx.State):
|
|||
|
||||
# ========== 智能体运行结果事件 ==========
|
||||
case AgentRunResultEvent(result=result):
|
||||
# 更新使用量
|
||||
# 更新会话记录
|
||||
await db_state.update_conversation_record(
|
||||
conversation.id, usage=result.usage
|
||||
conversation.id,
|
||||
usage=result.usage,
|
||||
workflow=conversation.workflow,
|
||||
)
|
||||
# 创建运行记录
|
||||
await db_state.create_run_record(
|
||||
|
|
@ -477,7 +513,7 @@ class ConversationState(rx.State):
|
|||
from application.workshop.book_flight import deps
|
||||
|
||||
# 初始化预定航班工作流
|
||||
workflow = Workflow(type=type, deps=deps_dump_python(deps))
|
||||
workflow = Workflow(type=type, deps=deps)
|
||||
# 设置用户提示词
|
||||
self.set_user_prompt(
|
||||
f"帮我查询并预定在 {deps.date.strftime('%Y-%m-%d')} 从 {deps.origin_airport_code} 到 {deps.destination_airport_code} 的航班"
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@
|
|||
"""
|
||||
from datetime import datetime, timedelta
|
||||
from random import choices
|
||||
from typing import Any
|
||||
|
||||
from dataclasses import asdict
|
||||
from pydantic_ai import ModelMessage, ModelMessagesTypeAdapter, RunUsage
|
||||
from pydantic_ai._uuid import uuid7
|
||||
import reflex as rx
|
||||
|
|
@ -15,7 +14,11 @@ from application.states.models import (
|
|||
Conversation,
|
||||
Message,
|
||||
MessageType,
|
||||
Workflow,
|
||||
usage_dump_json,
|
||||
usage_validate_json,
|
||||
workflow_dump_json,
|
||||
workflow_validate_json,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -72,19 +75,19 @@ class ConversationRecord(SQLModel, table=True):
|
|||
)
|
||||
user_id: str = Field(..., index=True, description="用户唯一标识")
|
||||
description: str = Field(default="新会话", description="会话描述")
|
||||
usage: str = Field(default="", description="会话使用量")
|
||||
workflow: str = Field(
|
||||
default="",
|
||||
description="会话工作流",
|
||||
created_at: datetime = Field(
|
||||
default_factory=datetime.now, description="会话创建时间"
|
||||
)
|
||||
is_deleted: bool = Field(
|
||||
default=False,
|
||||
index=True,
|
||||
description="会话已删除:True 表示已删除,False 表示未删除",
|
||||
)
|
||||
created_at: datetime = Field(
|
||||
default_factory=datetime.now, description="会话创建时间"
|
||||
)
|
||||
usage: str = Field(default="", description="会话使用量")
|
||||
workflow: str = Field(
|
||||
default="",
|
||||
description="会话工作流",
|
||||
) # 原则上嵌套列表或字典若不涉及查询则以字符串储存
|
||||
|
||||
|
||||
class RunRecord(SQLModel, table=True):
|
||||
|
|
@ -224,7 +227,7 @@ class DatabaseState(rx.State):
|
|||
conversation_record.usage
|
||||
), # 将 conversation_record.usage 由 JSON 字符串反序列化为 RunUsage
|
||||
created_at=conversation_record.created_at,
|
||||
workflow=_validate_json(
|
||||
workflow=workflow_validate_json(
|
||||
conversation_record.workflow
|
||||
), # 将 conversation_record.workflow 由 JSON 字符串反序列化为 Workflow,
|
||||
),
|
||||
|
|
@ -257,7 +260,9 @@ class DatabaseState(rx.State):
|
|||
record.id: Conversation(
|
||||
id=record.id,
|
||||
description=record.description,
|
||||
usage=record.usage,
|
||||
usage=usage_validate_json(
|
||||
record.usage
|
||||
), # 将 record.usage 由 JSON 字符串反序列化为 RunUsage
|
||||
messages={}, # 新会话默认消息字典为空
|
||||
created_at=record.created_at,
|
||||
)
|
||||
|
|
@ -269,6 +274,7 @@ class DatabaseState(rx.State):
|
|||
description: str | None = None,
|
||||
is_deleted: bool | None = None,
|
||||
usage: RunUsage | None = None,
|
||||
workflow: Workflow | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
更新会话记录
|
||||
|
|
@ -276,6 +282,7 @@ class DatabaseState(rx.State):
|
|||
:param description: 会话描述
|
||||
:param is_deleted: 会话已删除
|
||||
:param usage: 使用量
|
||||
:param workflow: 工作流
|
||||
:return: None
|
||||
"""
|
||||
async with rx.asession() as session:
|
||||
|
|
@ -287,7 +294,13 @@ class DatabaseState(rx.State):
|
|||
if isinstance(is_deleted, bool):
|
||||
record.is_deleted = is_deleted
|
||||
if isinstance(usage, RunUsage):
|
||||
record.usage = usage_dump_python(usage)
|
||||
record.usage = usage_dump_json(
|
||||
RunUsage(**asdict(usage))
|
||||
) # 将 RunUsage 序列化为 JSON 字符串
|
||||
if isinstance(workflow, Workflow):
|
||||
record.workflow = workflow_dump_json(
|
||||
Workflow(**asdict(workflow))
|
||||
) # 将 Workflow 序列化为 JSON 字符串
|
||||
await session.commit()
|
||||
|
||||
async def create_message_record(
|
||||
|
|
@ -375,7 +388,7 @@ class DatabaseState(rx.State):
|
|||
conversation_id=conversation_id,
|
||||
new_messages=ModelMessagesTypeAdapter.dump_json(
|
||||
new_messages
|
||||
).decode(),
|
||||
).decode("utf-8"),
|
||||
# 将 messages 由 List[ModelMessage] 序列化为 JSON 字符串
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,17 +9,24 @@ from typing import Any
|
|||
from pydantic import BaseModel, Field, TypeAdapter
|
||||
from pydantic_ai import RunUsage
|
||||
from pydantic_ai._uuid import uuid7
|
||||
from pydantic_ai.usage import UsageLimits
|
||||
|
||||
|
||||
class AgentRunWarmUpEvent(BaseModel):
|
||||
|
||||
class NoResult(BaseModel):
|
||||
"""
|
||||
智能体运行预热事件类
|
||||
无结果类
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
class AgentRunEvent(BaseModel):
|
||||
"""
|
||||
智能体运行事件类
|
||||
"""
|
||||
|
||||
content: str = Field(
|
||||
default="",
|
||||
description="预热内容",
|
||||
description="智能体运行内容",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -30,9 +37,8 @@ class MessageType(StrEnum):
|
|||
|
||||
USER_PROMPT = "user_prompt"
|
||||
THINKING = "thinking"
|
||||
WORK = "work"
|
||||
RESULT_OUTPUT = "result_output"
|
||||
TEXT = "text"
|
||||
TOOL_CALL = "tool_call"
|
||||
OUTPUT = "output"
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
|
|
@ -66,7 +72,7 @@ class Workflow(BaseModel):
|
|||
"""
|
||||
|
||||
type: WorkflowType = Field(..., description="工作类型")
|
||||
deps: dict[str, Any] = Field(default_factory=dict, description="工作依赖项")
|
||||
deps: Any = Field(default=None, description="工作依赖项")
|
||||
|
||||
|
||||
class Conversation(BaseModel):
|
||||
|
|
@ -76,10 +82,11 @@ class Conversation(BaseModel):
|
|||
|
||||
id: str = Field(..., description="会话唯一标识")
|
||||
description: str = Field(..., description="会话描述")
|
||||
user_prompt: str = Field(default="", description="用户提示词")
|
||||
created_at: datetime = Field(..., description="会话创建日期时间")
|
||||
usage: RunUsage = Field(..., description="会话使用量")
|
||||
messages: dict[str, Message] = Field(default_factory=dict, description="消息字典")
|
||||
created_at: datetime = Field(..., description="会话创建日期时间")
|
||||
workflow: Workflow | None = Field(default=None, description="工作流")
|
||||
user_prompt: str = Field(default="", description="用户提示词")
|
||||
is_running: bool = Field(
|
||||
default=False, description="正在运行,True 表示正在运行, False 表示运行结束"
|
||||
)
|
||||
|
|
@ -87,7 +94,6 @@ class Conversation(BaseModel):
|
|||
default=False,
|
||||
description="等待流式输出,True 表示等待流式输出,False 表示已开始流式输出或已完成",
|
||||
)
|
||||
workflow: Workflow | None = Field(default=None, description="工作流")
|
||||
|
||||
|
||||
class ConversationHistoryItem(BaseModel):
|
||||
|
|
@ -106,26 +112,26 @@ class MessageHistoryItem(Message):
|
|||
"""
|
||||
|
||||
|
||||
# 依赖项适配器
|
||||
DepsAdapter = TypeAdapter(Any)
|
||||
# 工作流适配器
|
||||
WorkflowAdapter = TypeAdapter(Any)
|
||||
|
||||
|
||||
def deps_validate_python(deps: dict[str, Any]) -> Any:
|
||||
def workflow_validate_json(workflow: str) -> Any:
|
||||
"""
|
||||
将依赖项反序列化
|
||||
将工作流反序列化
|
||||
"""
|
||||
if not deps:
|
||||
if not workflow:
|
||||
return None
|
||||
return DepsAdapter.validate_python(deps)
|
||||
return WorkflowAdapter.validate_json(workflow)
|
||||
|
||||
|
||||
def deps_dump_python(deps: Any) -> dict[str, Any]:
|
||||
def workflow_dump_json(workflow: Any) -> str:
|
||||
"""
|
||||
将依赖项序列化
|
||||
:param deps: 依赖项
|
||||
:return: python 字典
|
||||
将工作流序列化
|
||||
:param workflow: 工作流
|
||||
:return: 字符串
|
||||
"""
|
||||
return DepsAdapter.dump_python(deps)
|
||||
return WorkflowAdapter.dump_json(workflow).decode("utf-8")
|
||||
|
||||
|
||||
# 使用量(RunUsage)适配器
|
||||
|
|
@ -141,10 +147,10 @@ def usage_validate_json(usage: str) -> RunUsage:
|
|||
return UsageAdapter.validate_json(usage)
|
||||
|
||||
|
||||
def usage_dump_json(usage: RunUsage) -> dict[str, Any]:
|
||||
def usage_dump_json(usage: RunUsage) -> str:
|
||||
"""
|
||||
将使用量序列化
|
||||
:param usage: 使用量
|
||||
:return: python 字典
|
||||
:return: 字符串
|
||||
"""
|
||||
return UsageAdapter.dump_python(usage)
|
||||
return UsageAdapter.dump_json(usage).decode("utf-8")
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
预定航班工作流(范式)
|
||||
"""
|
||||
import datetime
|
||||
from typing import AsyncGenerator, cast
|
||||
|
||||
from re import I
|
||||
from typing import AsyncGenerator, cast, Any
|
||||
from dataclasses import replace
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from pydantic_ai import (
|
||||
Agent,
|
||||
|
|
@ -26,10 +27,10 @@ from pydantic_ai.messages import (
|
|||
)
|
||||
from pydantic_ai.run import AgentRunResult, AgentRunResultEvent
|
||||
|
||||
from application.states.models import AgentRunWarmUpEvent
|
||||
from application.states.models import AgentRunEvent, NoResult
|
||||
from application.workshop.models import (
|
||||
DEEPSEEK_V4_FLASH_MODEL,
|
||||
DEEPSEEK_V4_FLASH_MODEL_DISABLED_THINKING,
|
||||
DEEPSEEK_V4_FLASH_MODEL_SETTINGS,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -57,14 +58,6 @@ class Flight(BaseModel):
|
|||
return airport_code
|
||||
|
||||
|
||||
class NoResult(BaseModel):
|
||||
"""
|
||||
无结果类
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
|
||||
class Deps(BaseModel):
|
||||
"""
|
||||
依赖类
|
||||
|
|
@ -76,16 +69,13 @@ class Deps(BaseModel):
|
|||
searched_flights: list[Flight] | None = Field(
|
||||
default=None, description="查询到的航班"
|
||||
)
|
||||
extracted_flight_number: str | None = Field(
|
||||
default=None, description="提取到的航班号"
|
||||
)
|
||||
booked_flight: Flight | None = Field(default=None, description="预定到的航班")
|
||||
|
||||
|
||||
# 主智能体
|
||||
agent = Agent[Deps, Flight | NoResult | DeferredToolRequests](
|
||||
model=DEEPSEEK_V4_FLASH_MODEL,
|
||||
model_settings=DEEPSEEK_V4_FLASH_MODEL_DISABLED_THINKING,
|
||||
model_settings=DEEPSEEK_V4_FLASH_MODEL_SETTINGS,
|
||||
deps_type=Deps,
|
||||
output_type=Flight | NoResult | DeferredToolRequests,
|
||||
system_prompt=(
|
||||
|
|
@ -99,7 +89,7 @@ agent = Agent[Deps, Flight | NoResult | DeferredToolRequests](
|
|||
# 提取所有航班信息智能体
|
||||
extraction_flights_agent = Agent[Deps, list[Flight]](
|
||||
model=DEEPSEEK_V4_FLASH_MODEL,
|
||||
model_settings=DEEPSEEK_V4_FLASH_MODEL_DISABLED_THINKING,
|
||||
model_settings=DEEPSEEK_V4_FLASH_MODEL_SETTINGS,
|
||||
deps_type=Deps,
|
||||
output_type=list[Flight],
|
||||
system_prompt=(
|
||||
|
|
@ -113,7 +103,7 @@ extraction_flights_agent = Agent[Deps, list[Flight]](
|
|||
# 提取航班号智能体
|
||||
extraction_flight_number_agent = Agent[Deps, str | None](
|
||||
model=DEEPSEEK_V4_FLASH_MODEL,
|
||||
model_settings=DEEPSEEK_V4_FLASH_MODEL_DISABLED_THINKING,
|
||||
model_settings=DEEPSEEK_V4_FLASH_MODEL_SETTINGS,
|
||||
deps_type=Deps,
|
||||
output_type=str | None,
|
||||
system_prompt=(
|
||||
|
|
@ -195,7 +185,7 @@ async def search_flights(ctx: RunContext[Deps]) -> list[Flight]:
|
|||
raise ModelRetry(f"提取到的所有航班信息不可能为空")
|
||||
|
||||
# 查询到的航班
|
||||
ctx.deps.searched_flights = sorted(
|
||||
searched_flights = sorted(
|
||||
[
|
||||
flight
|
||||
for flight in extracted_flights
|
||||
|
|
@ -205,10 +195,14 @@ async def search_flights(ctx: RunContext[Deps]) -> list[Flight]:
|
|||
],
|
||||
key=lambda flight: flight.airfare,
|
||||
)
|
||||
return ctx.deps.searched_flights
|
||||
if not searched_flights: # 模拟业务规则约束:查询到的航班不能为空
|
||||
raise ModelRetry("查询到的航班不能为空")
|
||||
|
||||
ctx.deps.searched_flights = searched_flights
|
||||
return searched_flights
|
||||
|
||||
|
||||
@agent.tool(name="预定航班")
|
||||
@agent.tool(name="预定航班", requires_approval=True)
|
||||
async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult:
|
||||
"""
|
||||
预定航班
|
||||
|
|
@ -220,25 +214,24 @@ async def book_flight(ctx: RunContext[Deps]) -> Flight | NoResult:
|
|||
raise ApprovalRequired()
|
||||
|
||||
# 提取到的航班号
|
||||
ctx.deps.extracted_flight_number = (
|
||||
extracted_flight_number = (
|
||||
await extraction_flight_number_agent.run(
|
||||
f"用户提示词:\n{ctx.prompt}\n查询到的航班:\n{(flight_numbers:= [flight.number for flight in searched_flights])}",
|
||||
f"用户提示词:\n{cast(str, ctx.prompt).upper()}\n查询到的航班:\n{(flight_numbers:= [flight.number for flight in searched_flights])}",
|
||||
deps=ctx.deps,
|
||||
usage=ctx.usage,
|
||||
usage_limits=UsageLimits(request_limit=10),
|
||||
)
|
||||
).output
|
||||
if not ctx.deps.extracted_flight_number:
|
||||
if not extracted_flight_number:
|
||||
return NoResult()
|
||||
if ctx.deps.extracted_flight_number not in flight_numbers:
|
||||
raise ModelRetry(
|
||||
f"提取到的航班号 {ctx.deps.extracted_flight_number} 不在查询到的航班中"
|
||||
)
|
||||
|
||||
if extracted_flight_number not in flight_numbers:
|
||||
raise ModelRetry(f"提取到的航班号 {extracted_flight_number} 不在查询到的航班中")
|
||||
# 预定到的航班
|
||||
ctx.deps.booked_flight = next(
|
||||
flight
|
||||
for flight in searched_flights
|
||||
if flight.number == ctx.deps.extracted_flight_number
|
||||
if flight.number == extracted_flight_number
|
||||
)
|
||||
return ctx.deps.booked_flight
|
||||
|
||||
|
|
@ -278,7 +271,7 @@ deps = Deps(
|
|||
|
||||
|
||||
async def run_stream_events(
|
||||
deps: Deps,
|
||||
deps: Any,
|
||||
user_prompt: str,
|
||||
message_history: list[ModelMessage],
|
||||
usage: RunUsage,
|
||||
|
|
@ -286,15 +279,15 @@ async def run_stream_events(
|
|||
deferred_tool_results: DeferredToolResults | None = None,
|
||||
) -> AsyncGenerator[
|
||||
AgentStreamEvent
|
||||
| AgentRunResultEvent[str | DeferredToolRequests]
|
||||
| AgentRunWarmUpEvent
|
||||
| AgentRunResultEvent[Flight | NoResult | DeferredToolRequests]
|
||||
| AgentRunEvent
|
||||
| None,
|
||||
]:
|
||||
"""
|
||||
运行并流式输出事件
|
||||
"""
|
||||
# 构建智能体运行预热事件
|
||||
yield AgentRunWarmUpEvent(
|
||||
# 构建智能体运行事件
|
||||
yield AgentRunEvent(
|
||||
content=f"正在预定 {deps.date.strftime('%Y-%m-%d')} 从 {deps.origin_airport_code} 到 {deps.destination_airport_code} 的航班"
|
||||
)
|
||||
tool_names = set()
|
||||
|
|
@ -351,9 +344,8 @@ async def run_stream_events(
|
|||
case AgentRunResult(output=output):
|
||||
match output:
|
||||
case Flight():
|
||||
yield AgentRunResultEvent(
|
||||
result=AgentRunResult(output="预定成功")
|
||||
)
|
||||
yield AgentRunEvent(content="预定成功")
|
||||
|
||||
case DeferredToolRequests(approvals=approvals):
|
||||
yield AgentRunResultEvent(
|
||||
result=AgentRunResult(
|
||||
|
|
@ -362,3 +354,4 @@ async def run_stream_events(
|
|||
)
|
||||
)
|
||||
)
|
||||
yield event
|
||||
|
|
|
|||
|
|
@ -14,6 +14,6 @@ DEEPSEEK_V4_FLASH_MODEL = OpenAIChatModel(
|
|||
),
|
||||
)
|
||||
|
||||
DEEPSEEK_V4_FLASH_MODEL_DISABLED_THINKING = ModelSettings(
|
||||
DEEPSEEK_V4_FLASH_MODEL_SETTINGS = ModelSettings(
|
||||
temperature=0, extra_body={"thinking": {"type": "disabled"}} # 温度控制
|
||||
) # 不启用思考模式
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue