使用 ADK 构建长期运行的 AI 代理

TL;DR · AI 摘要
本文介绍了一种使用 ADK 构建长期运行的 AI 代理的方法,该代理可以在暂停和恢复后仍然保持上下文。
核心要点
- ADK 提供了持久内存模式、事件驱动的休眠门和多代理委托三种架构转变。
- 通过 ADK 构建的新员工入职协调代理可以在多个阶段保持上下文。
- 完整的源代码可在 GitHub 上找到。
结构提纲
按章节快速跳转。
无状态代理在长时间工作流中会遇到上下文污染、令牌成本爆炸和空闲时间推理幻觉等问题。
新员工入职流程包括发送欢迎包、等待员工签署文件、IT 配置和发送个性化第一天日程。
使用 Agents CLI 和编码代理来生成项目结构并设置持久会话。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- ADK 构建长期运行 AI 代理
- 无状态代理问题
- 上下文污染
- 令牌成本爆炸
- 空闲时间推理幻觉
- ADK 架构转变
- 持久内存模式
- 事件驱动的休眠门
- 多代理委托
- 新员工入职案例
- 发送欢迎包
- 等待签署文件
- IT 配置
- 发送个性化日程
金句 / Highlights
值得收藏与分享的关键句。
无状态代理在长时间工作流中会遇到上下文污染、令牌成本爆炸和空闲时间推理幻觉等问题。
ADK 提供了持久内存模式、事件驱动的休眠门和多代理委托三种架构转变。
通过 ADK 构建的新员工入职协调代理可以在多个阶段保持上下文。
标题:使用 ADK 构建可暂停、恢复且永不丢失上下文的长期运行 AI 代理
来源 URL:https://developers.googleblog.com/build-long-running-ai-agents-that-pause-resume-and-never-lose-context-with-adk/
发布时间:2026-05-12
Markdown 内容: 2026年5月12日
Eric Dong 开发者关系工程师
大多数代理教程都以无状态聊天机器人为终点——一个对话循环,在容器重启时会忘记所有内容。真实的业务工作流不会在一个 API 调用中结束。
人力资源入职培训持续两周。发票争议可能需要等待供应商回复几天。销售线索序列跨越一个月内的多个接触点。这些过程主要由“空闲时间”主导——长时间的停顿,等待人工签名、发货确认或审批。无状态聊天机器人无法在这种情况下生存。
本教程将引导您构建一个使用 Agent Development Kit (ADK) 的 新员工入职协调代理,该代理可以可靠地运行数周。该代理发送欢迎包,在员工签署文件期间暂停数天,将 IT 配置委托给专门的子代理,再次等待硬件交付,最后发送个性化的第一天日程安排——整个过程中不会丢失任何上下文。
在此过程中,您将学习区分生产代理与演示聊天机器人的三个架构转变:
- 使用持久化内存模式而不是将原始 JSON 数据存储到向量数据库中
- 使用事件驱动的休眠门控机制而不是主动轮询或阻塞线程
- 使用多代理委托而不是单个代理提示
完整的源代码可以在 GitHub 上找到。

为什么无状态代理在真实工作流中失效
标准的无状态模式将每个用户消息和模型响应追加到不断增长的对话历史记录中,然后将整个数据块反馈到下一个 LLM 调用中。这适用于五分钟左右的问答环节。但在几天或几周内,它会在三个方面崩溃:
提示上下文污染——经过两周入职流程中的数百次交互后,对话历史记录充满了无关的闲聊、旧工具输出和重复的指令。模型开始混淆当前步骤。
令牌成本爆炸——每次推理调用时重播完整的两周对话历史记录会迅速消耗令牌预算。一次入职流程可能会生成数千次交互——其中大部分已不再与当前决策相关。
空闲时间中的推理幻觉——当代理暂停三天等待文件签名,然后通过大量上下文数据恢复时,模型经常产生从未发生的中间步骤。它“记住”了未给予的批准或跳过了假设已完成的步骤。
解决方法不是更大的上下文窗口。而是根本不同的架构——一种代理状态明确、持久且独立于原始聊天历史的架构。
使用案例:新员工入职
考虑公司招聘新员工时会发生什么:
- 人力资源部门发送欢迎包和文档链接
- 空闲时间——员工签署文件期间的几天
- IT 部门配置企业邮箱和 Slack 账户
- 空闲时间——笔记本电脑运送到员工家中期间的几天
- 人力资源部门发送个性化的第一天日程安排

这不是单一的对话。而是一个具有多个暂停和恢复周期、人工审批节点和跨团队交接的后台进程。同样的模式出现在发票争议解决(暂停等待供应商回复,恢复进行应付账款路由)、销售线索开发(暂停在不同触点之间)以及数十种其他运营工作流中。
使用编码代理和 Agents CLI 启动项目
Agents CLI 是 Gemini 企业代理平台的官方命令行接口。本教程的工作流使用编码代理来完成繁重的工作,而不是手动运行 CLI 命令。提供一个高层次、意图驱动的提示,它会为您处理框架搭建。首先,全局安装 CLI:
uv tool install google-agents-cli Shell
已复制
然后给您的编码代理这个提示:
创建一个使用 ADK 的人力资源入职代理。它需要作为一个具有持久会话的长期运行后台进程运行。 Shell
已复制
编码代理运行适当的 agents-cli 命令,生成项目结构,并从一开始就设置持久会话和内存库设置。这种迭代的提示驱动方法贯穿整个教程:描述您需要的内容,编码代理将在下面各节中生成相应的代码。

将代理基于持久化状态机
不要依赖对话历史记录来跟踪进度,而是定义一个显式的状态模式,告诉代理在任何时候它在工作流中的位置。给您的编码代理这个提示:
添加一个状态机来跟踪入职进度。我需要像 START、WELCOME_SENT、DOCUMENTS_SIGNED、IT_PROVISIONED、HARDWARE_DELIVERED 和 COMPLETED 这样的步骤。代理应从会话状态读取其当前步骤,而不是从聊天历史记录。 Shell
已复制
定义状态模式
创建一个简单的类,为入职流程中的每个检查点命名常量:
# app/state_schema.pyclass OnboardingStep:
START = "START"
WELCOME_SENT = "WELCOME_SENT"
DOCUMENTS_SIGNED = "DOCUMENTS_SIGNED"
IT_PROVISIONED = "IT_PROVISIONED"
HARDWARE_DELIVERED = "HARDWARE_DELIVERED"
COMPLETED = "COMPLETED"六个状态。没有歧义。代理不能跳过步骤或虚构进度,因为状态机强制执行顺序。
将状态连接到系统指令
代理的系统提示直接从会话状态变量中读取当前位置——而不是通过重放旧消息:
# app/agent.py
from google.adk.agents import Agent
from google.adk.agents.callback_context import CallbackContext
from google.adk.models import Gemini
from app.state_schema import OnboardingStep
from app.tools import (
send_welcome_packet,
check_hardware_delivery,
send_day_one_schedule,
)
async def initialize_onboarding_state(callback_context: CallbackContext) -> None:
"""确保所有状态机键已初始化以防止错误。"""
state = callback_context.state
if "current_step" not in state:
state["current_step"] = OnboardingStep.START
if "new_hire_details" not in state:
state["new_hire_details"] = {}
if "pending_signals" not in state:
state["pending_signals"] = []
instruction = """您是一名 HR 入职协调员代理。
当前步骤: {current_step}
新员工详情: {new_hire_details}
待处理信号: {pending_signals}
严格遵循此状态机流程:
1. 如果当前步骤是 'START':询问姓名、电子邮件和入职日期。然后调用 'send_welcome_packet'。
2. 如果当前步骤是 'WELCOME_SENT':告知用户正在等待文档签名。不要调用其他工具。
3. 如果当前步骤是 'DOCUMENTS_SIGNED':委托 IT 配置给 'it_agent'。
4. 如果当前步骤是 'IT_PROVISIONED':询问硬件追踪 ID,然后调用 'check_hardware_delivery'。
5. 如果当前步骤是 'HARDWARE_DELIVERED':调用 'send_day_one_schedule'。
6. 如果当前步骤是 'COMPLETED':确认入职完成。
始终保持在工具和当前状态中。不要跳过步骤。"""通过将 {current_step}、{new_hire_details} 和 {pending_signals} 直接放入指令中,Python 每次运行代理时都会自动填充这些空白。这确保模型始终看到入职工作流的确切状态,而无需猜测或翻阅旧聊天消息。
工具推进状态机
每个工具函数通过 ADK 的 ToolContext.state 原子地更新检查点:
# app/tools.py
from google.adk.tools import ToolContext
from app.state_schema import OnboardingStep
def send_welcome_packet(
name: str, email: str, start_date: str, tool_context: ToolContext
) -> dict:
"""发送欢迎包并转换到 WELCOME_SENT。"""
state = tool_context.state
state["new_hire_details"] = {
"name": name, "email": email, "start_date": start_date
}
state["current_step"] = OnboardingStep.WELCOME_SENT
state["pending_signals"] = ["document_signed"]
return {
"status": "success",
"message": f"欢迎包已发送至 {name} ({email})。文档待签名。",
}每次工具调用都会创建一个自动检查点。如果容器在 send_welcome_packet 运行后立即崩溃,状态已经写入。当代理重启时,它会读取 current_step = WELCOME_SENT 并从停止的地方继续。
使用持久会话实现检查点恢复
只有当底层会话存储在重启后仍然存在时,状态机才是持久的。在一个容器化环境中,如 Cloud Run,容器冷启动,在空闲期间缩放至零,并且可能意外重启。如果会话存在于易失性内存中,每个正在进行的入职运行都会丢失。给您的编码代理这个提示:
"将我们的会话存储切换到持久 SQLite,以便代理在服务器重启后存活。"
# app/fast_api_app.py
from fastapi import FastAPI
from google.adk.cli.fast_api import get_fast_api_app
from google.adk.sessions.database_session_service import DatabaseSessionService
# 持久 SQLite 会话配置
session_service_uri = "sqlite+aiosqlite:///sessions.db"
app: FastAPI = get_fast_api_app(
agents_dir=AGENT_DIR,
web=True,
session_service_uri=session_service_uri,
)就这样。一个配置更改,每个 ToolContext.state 写入都持久保存到磁盘。在入职中途杀死服务器,重新启动它,代理将从正确的检查点恢复,并且所有新员工详细信息完整无缺。
对于生产部署,请用 Cloud SQL 连接字符串替换 SQLite URI——API 是相同的。
处理空闲时间与事件驱动恢复
空闲时间是长期运行代理的主要挑战。在发送欢迎包后,代理进入休眠状态,可能会持续数天,直到员工签署文档。主动轮询浪费计算资源。阻塞线程无法扩展。代理需要真正睡眠,并仅在外部事件到达时唤醒。给您的编码代理这个提示:
"为文档签名和硬件交付添加 webhook 端点。当 webhook 触发时,代理应唤醒,恢复其会话,并从中断处继续。"
Webhook 端点
暴露 FastAPI 端点,供外部系统(或演示 UI)在实际事件完成后调用:
# app/fast_api_app.py
from pydantic import BaseModel
from app.resume_handler import OnboardingResumeHandlerdb_session_service = DatabaseSessionService(db_url=session_service_uri)
webhook_runner = Runner(app=agent_app, session_service=db_session_service)
resume_handler = OnboardingResumeHandler(runner=webhook_runner)
class WebhookPayload(BaseModel):
user_id: str
session_id: str
@app.post("/webhooks/document_signed")
async def trigger_document_signed_webhook(payload: WebhookPayload) -> dict[str, str]:
"""当员工签署合同时唤醒入职代理。"""
await resume_handler.receive_signed_documents_callback(
user_id=payload.user_id, session_id=payload.session_id
)
return {"status": "success", "message": "文档签名已处理,代理已恢复。"}Python
已复制
**恢复处理器**
OnboardingResumeHandler 通过使用 runner.run_async 和 state_delta 来重新激活持久化的会话、转换状态机并程序化地唤醒代理:
# app/resume_handler.py
import json
import logging
from google.adk.runners import Runner
from google.genai import types
from app.state_schema import OnboardingStep
logger = logging.getLogger(__name__)
class OnboardingResumeHandler:
def __init__(self, runner: Runner):
self.runner = runner
async def receive_signed_documents_callback(
self, user_id: str, session_id: str
) -> None:
"""重新激活会话,转换到 DOCUMENTS_SIGNED,并恢复执行。"""
async for event in self.runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=types.Content(
role="user",
parts=[types.Part.from_text(
text="恢复入职:合同已签署。"
)],
),
state_delta={
"current_step": OnboardingStep.DOCUMENTS_SIGNED,
"pending_signals": [],
},
):
logger.info(json.dumps({
"severity": "INFO",
"message": f"唤醒执行事件:{event}",
"event": "runner_event",
"session_id": session_id,
}))Python
已复制
关键机制是 state_delta。当 webhook 触发时,run_async 在代理的下一次推理调用之前原子性地应用状态转换。模型在其系统提示中看到 current_step = DOCUMENTS_SIGNED 并立即知道要委派 IT 配置——无需重播旧的对话历史,也无须生成中间步骤。
相同的模式适用于硬件交付 webhook。容器可以在整个空闲期间缩放至零。当 webhook 到达时,容器启动,从 SQLite 中重新激活会话,并且代理从暂停的地方继续其推理链。
**通过多代理协调进行委托**
将所有工具塞入单个代理的系统提示会降低推理质量,特别是在长运行上下文中,提示已经加载了状态变量和工作流指令。ADK 的多代理架构允许您将专门的任务委托给专注的子代理。给您的编码代理这个提示:
"不要在主代理中包含 IT 配置。创建一个单独的 it_agent 子代理来处理设置公司账户,并在文档签署后由协调器委托给它。" Shell
已复制
入职协调器将 IT 配置委托给专用的 it_agent:
# app/agent.py
from app.tools import provision_software_accounts
it_agent = Agent(
name="it_agent",
model=Gemini(model="gemini-3.1-flash-lite"),
instruction="""您是一个 IT 配置代理。为新员工配置公司软件账户(电子邮件、Slack)。
当前步骤:{current_step}
新员工详情:{new_hire_details}
1. 收集所需的公司用户名前缀。
2. 调用 'provision_software_accounts'。
3. 配置完成后,将控制权交还给协调器。""",
tools=[provision_software_accounts],
)
root_agent = Agent(
name="hr_onboarding_coordinator",
model=Gemini(model="gemini-3.1-flash-lite"),
instruction=instruction,
tools=[send_welcome_packet, check_hardware_delivery, send_day_one_schedule],
sub_agents=[it_agent],
before_agent_callback=initialize_onboarding_state,
)Python
已复制
当协调器达到 DOCUMENTS_SIGNED 时,它将执行传递给 it_agent。子代理独立处理账户配置,更新共享状态为 IT_PROVISIONED,并将控制权交还。每个代理都有一个专注的提示和狭窄的工具集,即使在积累了数周的状态之后,也能保持推理的准确性。
请注意,在创建 root_agent 时,我们将 initialize_onboarding_state 传递给 before_agent_callback 参数。这告诉应用程序在用户首次与代理交互时运行我们的初始化函数,确保所有跟踪变量都准备就绪。因为代理每次唤醒时都会动态填充这些变量到其提示中,所以无论步骤之间经过多少天,它都知道自己的确切位置。
**使用黄金评估验证多日流程**
您不能等待两周才发现代理跳过了某个步骤。ADK 评估集让您通过预先播种会话状态,在几秒钟内模拟空闲时间延迟和 webhook 触发。给您的编码代理这个提示:
"编写模拟空闲时间的评估测试。我需要一个测试,其中代理等待 48 小时以接收硬件交付,然后恢复并仍然记得新员工的详细信息。" Shell
已复制
这是一个黄金测试案例,用于验证代理是否正确强制执行空闲时间暂停门控——拒绝在被要求时跳过步骤:
{ "eval_id": "idle_time_pause_safety_gate", "conversation": [ { "user_content": {"parts": [{"text": "开始 Jane Doe 的入职流程,电子邮件:jane@example.com,入职日期:2026-06-01。"}]}, "intermediate_data": { "tool_uses": [{"name": "send_welcome_packet", "args": {"name": "Jane Doe", "email": "jane@example.com", "start_date": "2026-06-01"}}] } }, { "user_content": {"parts": [{"text": "我们能否跳过文件签署,现在就分配企业账户?"}]}, "final_response": {"parts": [{"text": "等待员工签署"}]}, "intermediate_data": {"tool_uses": []} } ] }
JSON
复制
第二个回合验证了代理拒绝调用任何工具,并停留在 `WELCOME_SENT` 状态。第二个测试案例预先设置状态为 `IT_PROVISIONED`,并确认代理在模拟的48小时硬件延迟后正确恢复,依次调用 `check_hardware_delivery` 和 `send_day_one_schedule`,而不会丢失新员工的原始上下文。
本地运行评估:
.venv/bin/adk eval ./app tests/eval/evalsets/idle_time_delay_eval.json \ --config_file_path tests/eval/eval_config.json
Shell
复制
这些黄金测试可以直接插入到 CI/CD 管道中,在生产之前捕获状态机的回归问题。
## **部署到代理运行时**
当评估通过后,就可以进行部署了。给你的编码代理这个提示:
`"将此部署到代理运行时,并启用 Cloud Trace,以便我们可以监控生产中的暂停和恢复延迟。"`
Shell
复制
编码代理搭建了连接你的 ADK 应用程序到代理运行时的 AgentEngineApp 包装器:
app/agent_runtime_app.py
from vertexai.agent_engines.templates.adk import AdkApp from app.agent import app as adk_app
class AgentEngineApp(AdkApp): def set_up(self) -> None: """初始化日志和遥测。""" vertexai.init() super().set_up()
agent_runtime = AgentEngineApp(app=adk_app)
Python
复制
使用单个命令进行部署:
`agents-cli deploy`
Shell
复制
代理运行时处理会话持久性、自动扩展(包括空闲时间的缩放至零),以及开箱即用的 Cloud Trace 集成。与本地针对 SQLite 运行的检查点和恢复架构相同,在生产环境中针对托管云存储工作,无需更改代码。

## **接下来做什么**
无状态代理只是代理可以实现的一部分。本教程中的模式——持久化状态机、持续的检查点和恢复、基于事件的空闲时间处理以及多代理委托——将代理从对话玩具转变为可靠的生产后台进程,能够管理跨越数天或数周的工作流。
开始步骤:
* 克隆 [new-hire-onboarding 仓库](https://github.com/GoogleCloudPlatform/generative-ai/tree/main/agents/adk/new-hire-onboarding),并在本地运行实时演示
* 探索 [ADK 文档](https://adk.dev/)以了解会话管理、多代理模式和评估框架
* 安装 [Agents CLI](https://google.github.io/agents-cli/) 以搭建、测试和部署自己的长运行代理
入职代理只是一个例子。任何包含人工介入暂停、跨系统交接或多日时间线的工作流都是这种架构的理想候选者。发票争议、采购审批、销售线索序列、合规审计——模式相同。定义状态机,持久化检查点,度过空闲时间,然后从离开的地方继续。
[](https://developers.googleblog.com/build-long-running-ai-agents-that-pause-resume-and-never-lose-context-with-adk/)
上一篇
下一篇
[](https://developers.googleblog.com/building-with-gemini-embedding-2/)