

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 针对亚马逊 Bedrock AgentCore Runtime V2 优化您的代理
<a name="runtime-v2-optimize"></a>

亚马逊 Bedrock AgentCore Runtime V2 通过恢复快照来启动您的代理。这改变了您构造代理代码的方式。您的代理在启动时所做的工作将捕获在快照中，并由每个还原的实例共享。改为在请求处理程序中生成任何在不同请求之间必须不同或可能过期的值。本主题介绍如何围绕启动工作和每个请求的工作来构建代理，以使其在每次还原后保持正确。有关平台版本 V2 的概述，请参阅[平台版本](runtime-how-it-works.md#runtime-platform-versions)。

您的代理有两个执行上下文：

启动  
在 Runtime 拍摄快照之前，在进程启动 AgentCore 时运行一次的代码。 AgentCore Runtime 在快照中捕获此代码，每个实例都会继承其结果。

请求处理  
您的`/invocations`处理程序中的代码。此代码在每个实例的每个请求上运行。

使用一条规则来决定代码属于哪里。如果该值在快照的生命周期内保持不变，则在启动时计算该值。如果它因请求而异或可能过期，请在您的处理程序中进行计算。 AgentCore Runtime 为每个代理版本拍摄一个快照，并在您创建或更新代理之前一直使用该快照，因此快照的生命周期就是该版本的生命周期。

## 启动时初始化一次
<a name="_initialize_once_at_startup"></a>

在创建快照之前，在流程启动时进行昂贵的、可重复使用的工作。例如，导入依赖关系、加载模型权重或从部署包中读取静态配置。使用 S AgentCore DK，在调用之前，在模块范围内完成这项工作`app.run()`。您的代理在`app.run()`运行之前不会监听端口 8080，因此在启动工作完成之前，`/ping`无法成功且无法拍摄快照。快照通过构造捕获完全初始化的代理，您无需关`/ping`闭。在启动后 120 秒内完成初始化。如果您的代理没有及时恢复正常，则运行时无法通过运行状况检查。有关`/ping`和`/invocations`终端节点的更多信息，请参阅[了解 AgentCore 运行时服务合同](runtime-service-contract.md)。

```
import json, pathlib

from bedrock_agentcore.runtime import BedrockAgentCoreApp

# Runs once at import, before app.run() starts the server and before the
# snapshot. Every restored instance inherits these objects.
MODEL = load_model_weights()
SETTINGS = json.loads((pathlib.Path(__file__).parent / "agent.json").read_text())

app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload):
    # Per-request work runs here on every instance.
    ...

app.run()  # starts listening on 8080; the snapshot is taken after this
```

如果您的代理运行自己的 HTTP 服务器而不是 AgentCore SDK，则`/ping`仅在初始化完成后才报告运行状况良好，以便快照捕获完全初始化的代理。

启动时的只读数据，每个实例都相同，不会过期。在处理程序中处理时间敏感值，例如短期证书。

**注意**  
如果不重新部署，请勿在启动时计算任何更改的内容。例如，从 AgentCore Gateway 获取的工具目录看起来像是理想的启动值，因为它速度慢且加载成本高，但启动时缓存它会冻结快照时代理的工具清单。

## 使每个请求的状态保持最新状态
<a name="_keep_per_request_state_fresh"></a>

快照仅捕获一次，并由每个还原的实例共享，因此您的代理在启动时生成的任何值在各个实例之间是相同的，并且在生成快照时是固定的。处理快照无法在处理`/invocations`程序中携带的值。下表描述了要在处理程序中计算的值，而不是在启动时计算的值。


| 要执行的操作 | 在处理器里做 | Reason | 
| --- | --- | --- | 
| 生成随机值、标识符或令牌 | 致电 `os.urandom()``secrets`，或`uuid.uuid4()`按每项要求拨打 |  `os.urandom()`只有在还原后每次调用它时才会返回新的熵。启动时读取的值会被复制到快照中，并且在每个实例上都是相同的，无论读取时熵源有多好。该`random`模块同样在启动时进行播种，因此还原的实例会重复相同的顺序。 | 
| 读取当前时间 | 为每个请求计算 | 启动时捕获的时间戳在快照时是固定的。 | 
| 测量经过的时间 | 在处理器中获取参考时间戳 |  `time.monotonic()`在还原过程中不会向前推进，因此从启动参考中测得的持续时间是错误的，而且看上去不正确。 | 
| 使用凭证或令牌 | 过期时刷新它们 | 启动时加载的凭证可能会在实例启动之前过期。 | 
| 识别实例或工作程序 | 为每个请求生成一个 ID；不要从主机获取 | 每个还原的实例报告相同的主机名 (`localhost`) 和 PID (`1`)，因此使用两者作为 ID 会折叠队列中的指标、日志流和锁定所有者。 | 

在启动时构建可重复使用的客户端，并在每次调用时计算每个请求的值。

```
import os, time

# Build reusable clients at startup, before the snapshot. Exercise them here too
# (for example, with a warm-up call) so the setup the client caches — endpoint and
# credential resolution, connection pool — is captured in the snapshot.
client = build_client()
warm_up(client)

@app.entrypoint
def invoke(payload):
    creds = get_credentials()          # refreshed when expired, not read at startup
    request_id = os.urandom(16).hex()  # unique per request
    now = time.time()                  # current time, not snapshot time
    # Handle the request.
```

## 使用快照安全的加密库
<a name="_use_snapshot_safe_cryptographic_libraries"></a>

当 AgentCore Runtime 从快照恢复实例时，在启动时缓存随机状态的加密库可以在实例间重复使用该状态。您的加密库必须使用快照安全 (snapsafe) 版本，该版本在还原后会重新生成。

直接代码部署  
服务管理的基础映像已经包含其加密库的快照安全版本，因此您无需对其采取任何操作。

Bring-your-own 加密库  
如果您自带加密库（例如在容器代理中），请使用快照安全构建，以便它们在还原后重新播种。在亚马逊 Linux 2023 上，使用`openssl-snapsafe-libs`。

## Networking
<a name="_networking"></a>

在启动时构建客户端，并期待透明的重新连接  
您在启动时打开的套接字无法在还原后存活，但是您的客户端库在其周围缓存的设置（服务模型解析、端点解析、凭据解析和连接池）可以保存。在启动时构建和运行您的客户端，并期望恢复后的第一次调用以透明地重新建立连接。

不要使用主机名或 PID 作为唯一的实例标识符  
每个还原的实例都从相同的快照开始，并报告相同的主机名和进程 ID。在每个请求中生成一个唯一标识符。

避免绑定到固定源端口  
还原后，在快照时保持的连接将重新建立，并且固定的源端口可能会与实例中的替代端口发生冲突。还原的实例是具有自己的网络命名空间的独立微型虚拟机，因此冲突发生在实例内部，而不是实例之间。