在生产环境运行 Agent
查看 MarkdownAgent Session 和其他所有 Flow 运行在同一个 Dex Server 上。把 Session 交给用户使用之前,逐项检查下面的内容。每一行都链接到包含详情的页面。
| 事项 | 需要知道什么 | 详情 |
|---|---|---|
| Dex Server | Dex Server 运行在 Dex Cloud 上,或者运行在你自己带对象存储的基础设施上。一个集群由一个活动节点提供服务。 | 选择 Dex 的运行方式、在生产环境运行 Dex |
| 认证 | Public API 认证默认关闭。在 server 能被其他机器访问之前打开它。 | Authentication(英文) |
| 保留期 | 已关闭的 Session 默认在 7 天内仍可读取,之后 server 可能删除它。需要保存更久的对话记录,请放在你自己的存储中。 | Retention(英文) |
| Session 大小 | Session 的 active state 达到 state 上限(默认 64 MiB)时,Session 结束。Message 保留策略,以及 harness 支持时的 compaction,会让它保持在上限以下。 | Session limits(英文) |
| 模型花费 | 在每次模型调用前后,按账号或 API key 检查花费;Dex 把花费上限留给你的代码。 | Spend limits(英文) |
| Credential | 把模型 API key 和 tool credential 放在 Worker 的环境或 secret store 中,永远不要放进 Session input 或 message。 | Credentials(英文) |
| 审批期限 | 调用不能无限期等待时,设置一个期限。没有期限的审批会一直等到有人答复,turn 也随之等待。 | Approval deadlines(英文) |
| 升级 | 部署在下一次模型调用或 Tool Call 时生效。有些版本不能与上一个版本同时运行:启动新 Worker 之前,先停掉所有旧 Worker。这样的停止会让正在运行的单次尝试 tool 变成 outcome_unknown,并让 RPC 以 Unavailable 失败,直到 Worker 恢复;请先优雅地停止 Worker。 | Upgrades(英文) |
| Worker | 每个正在运行的模型调用、Tool Call 和 RPC 都占用一个 Worker task slot。按并行 Tool Call 的数量确定 Worker 规格。 | Workers and timeouts(英文) |
| 流式传输 | AgentEvents Stream 把实时文本送到你的 UI。按你的流量确定它的规格。 | AgentEvents Stream(英文) |
| Tracing | Session 可以为每次模型调用和 Tool Call 输出 OpenTelemetry span。内容采集默认关闭。 | Tracing(英文) |
| 部署形态 | Web replica 持有 Client。Worker 进程运行 Session Flow。 | 部署形态 |
| 部署期间的调用 | send_user_message 和 resolve_interrupt 是 RPC。它们需要正在运行的 Worker,而重试的 send_user_message 可能把同一条消息排队两次。 | 部署期间的 RPC |
| 未知结果 | 被崩溃打断的写操作会记录为 outcome_unknown,并且不会再次运行。决定由谁来核对它。 | 核对 outcome_unknown |
部署形态
生产环境有三类进程:
持有 Client。
Session 状态保存在对象存储中。
运行 Session Flow。
- Web app replica 持有 Client。它们启动 Session、发送消息并流式返回回复。它们需要 Session Flow 的定义来编码调用,但从不运行 Step。
- Worker 进程 运行 Session Flow:每次模型调用、Tool Call 和审批等待。对小应用来说,web 进程也可以同时托管 Worker,Agent 快速开始程序就是这样做的。
- Dex Server 保存每个 Session。Worker 主动向外连接它,所以不需要入站端口。
滚动部署期间,每个 Step 运行的是接手它的那个 Worker 的代码。正在进行的 turn 会用新代码完成它的下一次模型调用或 Tool Call。缺少某个 Step 类型的 Worker 会拒绝接手,由另一个 Worker 接手。少数版本不能与上一个版本同时运行,需要先停掉所有旧 Worker;Upgrades release notes(英文)列出了这些版本。
部署期间的 RPC
应用通过 RPC 与 Session 通信,例如 send_user_message 和 resolve_interrupt。RPC 在 Worker 上运行。它从不在队列中等待:如果 10 秒内没有 Worker 接手,调用就会以 Unavailable 失败。在停掉所有 Worker 的部署期间,这些调用会一直失败,直到新的 Worker 连上。Session 本身不会丢失任何东西。
- 遇到 Unavailable 时重试。 用 backoff 重试,直到 Worker 恢复。以 Unavailable 失败的调用什么都没有改变。
- resolve_interrupt 可以安全重试。 已经生效的审批第二次会返回 False,什么都不会再次运行。
- send_user_message 可能发送两次。 Dex 会再次运行重复的 RPC。如果响应丢失,重试可能把同一条消息排队两次。重新发送之前,用
get_agent_snapshot读取 Session,在 messages 和queued_messages中查找这条消息。如果它排队了两次,用delete_queued_message删除多出的副本。
下面这个辅助函数就是这样做的。每个标签页都展示完整模块。它重试可能没有送达的发送,每次重试前检查 Session,并删除多出的排队副本。它按文本区分消息,所以用户每发一条消息就调用它一次。
"""Sends a user message to a session once, even when a send must be retried."""
from __future__ import annotations
import asyncio
from typing import Protocol
import grpc # type: ignore[import-untyped]
from dex import DexServiceError
from dex_agents import AgentSnapshot, MessageRole
# A lost response leaves a send's outcome unknown; these codes mean "check first".
UNCERTAIN_CODES = (grpc.StatusCode.UNAVAILABLE, grpc.StatusCode.DEADLINE_EXCEEDED)
class MessageSessions(Protocol):
async def get_agent_snapshot(self, session_id: str) -> AgentSnapshot: ...
async def send_user_message(self, session_id: str, text: str) -> bool: ...
async def delete_queued_message(self, session_id: str, message_id: str) -> bool: ...
async def send_user_message_once(
sessions: MessageSessions,
session_id: str,
text: str,
*,
attempts: int = 5,
retry_delay_seconds: float = 1.0,
) -> None:
"""Queues one user message, retrying a failed send without queuing it twice.
例子: examples/python/agent-harnesses/agent_harness_examples/quick_start/safe_send.py
// gRPC codes UNAVAILABLE and DEADLINE_EXCEEDED: the send may or may not have arrived.
const UNCERTAIN_CODES: readonly number[] = [14, 4];
export interface MessageSessions {
getAgentSnapshot(sessionId: string): Promise<AgentSnapshot>;
sendUserMessage(sessionId: string, text: string): Promise<boolean>;
deleteQueuedMessage(sessionId: string, messageId: string): Promise<boolean>;
}
export function messageSessions(client: Client, flow: AgentSessionFlow): MessageSessions {
return {
getAgentSnapshot: (sessionId) => readAgentSnapshot(client, flow, sessionId),
sendUserMessage: (sessionId, text) => sendAgentUserMessage(client, flow, sessionId, { text }),
deleteQueuedMessage: (sessionId, messageId) =>
client.invokeRPC(flow.deleteQueuedMessage, sessionId, { messageId }),
};
}
/**
* Queues one user message, retrying a failed send without queuing it twice.
例子: examples/typescript/src/products/agent-harnesses/quick-start/quick-start-safe-send.ts
其他可能重复的请求,参见 限制。