AI Agent
这个 Python application 是一个长期运行的通用 AI Agent。它可以先制定计划供用户审阅,也可以与用户聊天、调用受信任的 MCP tool、在修改外部系统前等待审批,并通过 durable Timer 暂停。
会话状态由 Dex 管理。Agent 每次调用模型时都会从 durable state 重新构造输入,因此不依赖 LLM provider 的 conversation API。
Flow 设计
这个交互式 definition graph 由可运行的 Python Flow 生成。
Durable context
Application 会把每条 user、assistant 与 tool message 分别保存在 AttributeMap instance 中。一个较小的 Attribute 保存 sequence range、pending tool 与当前 context summary。
当模型 context 达到配置窗口的 85% 时,Step 会摘要较早的 message。模型收到 system prompt、累计 summary 和最近尚未摘要的 message。默认情况下,当前 AttributeMap 保留最近 2,000 条 message。
config = Attribute("AgentConfig", AgentConfig)
state = Attribute("AgentState", AgentState)
summary = Attribute("ContextSummary", ContextSummary)
messages = AttributeMap("AgentMessages", AgentMessage)
plan = Attribute("AgentPlan", AgentPlan)
pending_approval = Attribute("PendingApproval", PendingApproval)
pending_timer = Attribute("PendingTimer", PendingTimer)
queued_user_messages = Channel("QueuedUserMessages", UserMessage)
steered_user_messages = Channel("SteeredUserMessages", UserMessage)
tool_approvals = ChannelMap("ToolApprovals", ToolApproval)
plan_executions = ChannelMap("PlanExecutions", PlanExecutionRequest)
reasoning_summary = Stream("ReasoningSummary", str, 10 * 1024 * 1024)
assistant_text = Stream("AssistantText", str, 10 * 1024 * 1024)
agent_activity = Stream("AgentActivity", AgentEvent, 10 * 1024 * 1024)
例子: examples/python/dex_examples/products/ai-agent/ai_agent_flow.py
每个 map instance 都可以独立加载和更新,避免每次追加消息时重写不断增长的 conversation value。Dex blob storage 与 Worker BlobCache 会处理较大的 message 和 MCP result。
排队消息与 Steer
User message 会进入 durable QueuedUserMessages FIFO queue。Agent loop 运行期间,这些 message 会保持 pending,不会中断当前 model 或 tool call。Server 接受后,UI 会立即显示它们。只要 message 尚未被消费,用户就能 Edit、Delete 或选择 Steer。Edit 会先删除 pending message;重新提交时,它会带着新的 ID 加到 queue 尾部。
Steer 只发送选中的 message ID。Transactional RPC 会显式加载 QueuedUserMessages,找到原始 Value,暂存 deletion,并把该 Value 发布到 SteeredUserMessages。如果另一个 operation 先消费了 message,Dex 不会 commit 任何一项 effect。Agent 会在下一次 model call、tool、approval wait 或 Timer continuation 前检查 steered queue。它不会取消已经开始的 LLM 或 MCP request。到达下一个 safe boundary 后,Agent 会清除尚未执行的 tool call 与过期 approval 或 Timer state,记录结构化 cancellation result,再根据 Steer message 重新规划。
Message queue 不是 chat history。Pending Channel message 尚未被 Agent 消费,因此可以修改。Message 被消费后会成为 durable conversation history,原 message ID 也随即失效。
Browser 通过一个 snapshot endpoint 加载 application state。一个 read-only RPC 会显式加载 AgentMessages、QueuedUserMessages 和 SteeredUserMessages,再从同一次 invocation 返回 conversation、Agent description、run ID 与两个 queue。每次 mutation 成功、live event 到达、页面恢复 focus 或 connectivity 时,browser 都会刷新;另外每八秒执行一次低频兜底刷新。
Durable plan
为一条 message 开启 Plan mode 后,Agent 只会创建或修订 plan,不会执行它。Planning call 只能使用内置 write_todos tool,不能调用 MCP tool 或启动 durable wait。
当前 plan 保存在普通 Attribute 中。每次更新都会原子替换有序 task list,并增加 revision。Task 状态包括 pending、in progress 和 completed。Plan 独立于 conversation message,因此 context compaction 不会删除它。
UI 会展示 draft,只有用户选择 Execute plan 后才开始执行。如果 active plan 在仍有未完成 task 时停止,它会继续显示 incomplete,并允许用户继续执行。Agent 正在等待下一次输入,不代表所有 plan task 都已完成。
MCP tool 与审批
Worker 从本地配置加载受信任的 stdio 或 Streamable HTTP MCP server。Agent Portal 可以为新会话启用已注册的 server 和 tool,但不能注册 MCP command、URL 或 credential。
Read-only tool 可以直接运行。Write、destructive 和未分类 tool 会等待 durable approval Channel。Tool timeout 与 retry policy 可以按 server 和 tool 配置。最终失败会成为 tool result,让模型和用户决定下一步。
Example 支持 tool、resource、resource template、prompt、progress 与 logging。暂不启用 server-initiated sampling、elicitation 和 roots。
Durable wait
内置 durable_wait tool 会把 Flow 移动到同时等待 Timer 与 SteeredUserMessages 的 Step。Timer 可以跨 Worker restart 继续等待。Queued message 会继续排队;选择 Steer 才会中断等待并让 Agent 重新规划。
实时 event
Reasoning summary 与 assistant text 使用两条独立的 buffered Stream。Step 把两个 buffered writer 传给 model adapter。SDK 会在一秒或 16 KiB 内合并较小的 chunk,并在 Step 返回 result 或 error 前分别 flush 最后的内容。
OpenAI model 使用 LiteLLM 的 Responses adapter。每次 request 都会请求自动 reasoning summary,但不会使用 provider 端的 conversation storage。Dex 会把加密的 OpenAI reasoning item 与 durable assistant message 一起保存,并在 stateless model context 中重放。UI 把官方 reasoning-summary delta 显示在 Thinking,把可见输出显示在 Response,并把 tool call 与 lifecycle progress 显示在 Agent activity。其他 provider 只有在 adapter 提供真实 reasoning-summary event 时才显示 Thinking panel。
Tool progress、timer state 与 compaction status 使用另一个 Stream。Stream delivery 是 best effort。Durable chat history 始终来自 Attribute,因此浏览器刷新不依赖重放每条 progress event。
运行 example
Flow 与 HTTP route 位于 examples/python/dex_examples/products/ai-agent。React UI 与 MCP 配置示例位于 examples/python/ai-agent。
Application 默认先进入 Agent Portal。启动 Agent 前,先选择已配置的 LiteLLM provider 和 model,再选择已注册的 MCP server 与 tool。缺少所需 environment variable 的 provider 仍会显示,但无法选择。请把 credential 加到 examples/.env,然后重启 Python examples。Portal 不会把 credential value 发送到 browser,也不会写入 Dex state 或 Flow history。
Local playground 会启动无需 credential 的 search、Slack 和 Google Docs demo MCP server。Model call 运行时,chat page 会分别展示 Thinking、Response 与 Agent activity。可以使用 Command/Ctrl+Enter 或 Alt+Enter 发送 message。当 work 需要用户输入时,内置 request_user_input tool 会在 conversation 中展开 durable input panel。已知答案会显示为选择按钮,开放式问题会显示文本框。Execution 会等待用户提交答案后再继续。
Browser page 是 conversation 的 scroll surface。Queue、user-input request 与 composer 固定在窗口底部,新 activity 会持续显示在它们上方。Queue panel 为空时会折叠成紧凑的状态行;出现 pending message 时会自动展开。Active work 期间提交的 message 会在其中显示为 Queued。在 Server 报告它已 pending,或 Flow 已把它消费到 history 之前,UI 会一直保留已提交的 item。消费前可以用 Edit 或 Delete,也可以用 Steer 在下一个 safe boundary 让 Agent 处理它。