如今,编程智能体可以构建能正常运行的 Web 应用:仪表盘、产品页面,或产品团队想要试用的原型。审查这些成果最快的方式,就是打开应用、使用它、提出修改要求,然后检查结果。当应用在智能体编辑代码的环境中持续运行时,这个循环十分顺畅,每次修改都能在几秒内显示在浏览器中。如果必须先把应用复制出来并部署到其他地方,每次修改都要等待部署,审查速度也会随之变慢。

Amazon Bedrock AgentCore 是一个大规模构建、连接和优化智能体的平台,你可以自行选择智能体框架和基础模型(FM)。AgentCore runtime 是 Amazon Bedrock AgentCore 的一项功能,它在各自隔离的 microVM 中运行每个智能体会话。借助 Session endpoints,你可以在自己的浏览器中打开会话里运行的应用。Session Endpoints 为每个会话提供一个经过身份验证的 URL,将每个请求的 HTTP 方法、路径、查询字符串和请求体,以及 WebSocket 连接,转发到你允许的端口。智能体每保存一个文件,你就能看到页面随之更新。

在本文中,你将部署一个编程智能体,让它在自己的会话中持续运行一个 Vite 网站,然后打开一个本地页面:左侧是聊天界面,右侧是实时网站。你要求智能体将网站切换为深色主题,实时查看它逐步执行的操作,并观察网站如何在不刷新的情况下发生变化。最后,你将了解内部发生了什么:同一会话中的第二个端口,以及 Session endpoint 会拒绝哪些请求。

完整示例,包括本文所用的 notebook,位于 agentcore-samples GitHub repo 的 14-remote-access folder 中。

Session endpoints 的工作原理

每个会话都有自己的 Session endpoint,有六个细节决定了如何使用它。

你可以选择它能访问哪些端口。调用 CreateAgentRuntime 时,设置 allowedPorts:填写 1–5 个条目,每个条目可以是单个端口,也可以是 8000–12000 范围内的一段端口,但不包括平台保留端口。允许访问某个端口并不会启动服务器,因此你的应用必须绑定到 0.0.0.0 并监听该端口。

URL 随会话一起返回。InvokeAgentRuntime 响应在 X-Amzn-Bedrock-AgentCore-Runtime-Session-Endpoint-Url 标头中携带该 URL;支持该字段的 SDK 将其暴露为 sessionEndpointUrl。

每个请求都需要身份验证。对于使用 AWS Identity and Access Management(IAM) 的 runtime,每个 HTTP 请求、HTTP/2 流和 WebSocket 握手都需要携带 Signature Version 4(SigV4) 签名。调用方还需要对 runtime 及其 runtime endpoint 拥有 bedrock-agentcore:InvokeAgentRuntimeProxy 权限。对于使用入站 JSON Web Token(JWT)授权器 的 runtime,每个请求则需携带一个 bearer token。知道 URL 并不能获得访问权限。

主机名决定端口。无论目标端口是什么,客户端都通过端口 443 连接,主机名上的前缀则用于选择会话内部的端口:

https://8090--<session-endpoint-id>.runtime.bedrock-agentcore.<region>.on.aws/index.html
https://8081--<session-endpoint-id>.runtime.bedrock-agentcore.<region>.on.aws/status

它支持你的服务器所使用的协议。HTTP/1.1 和 HTTP/2 请求(包括服务器发送事件和 gRPC)以及 WebSocket 连接都可以通过。本教程使用 HTTP/1.1 和 WebSocket。

它的作用范围仅限于一个会话。该 URL 的每个请求都需要凭证,且只在会话期间有效,因此可以用它来构建、测试和审查智能体生成的内容。要向用户提供应用,请将其部署到 AWS Amplify Hosting 等托管服务。

浏览器无法自行为页面发出的请求添加 SigV4 签名或 bearer token,因此 bedrock-agentcore SDK 中的 Session 端点客户端可以在你的机器上打开一个本地端点。浏览器打开 http://localhost:8088,本地端点会为每个请求和 WebSocket 握手添加你的凭证,然后再进行转发。

Session 端点与 AgentCore runtime 已提供的 runtime 端点不同。使用 DEFAULT 等 runtime 端点可以访问你的智能体;该端点控制调用方访问智能体的哪个版本。InvokeAgentRuntime 调用通过该端点进行路由,因此你可以让开发环境和生产环境使用不同版本,并在不更改调用方的情况下升级或回滚。使用 Session 端点可以查看智能体在会话中运行的内容,例如网页预览。它是一个经过身份验证的 URL,只能访问你允许的端口,适用于会话运行的任何版本,并在会话结束时失效。完整文档请参阅通过 Session 端点连接到 runtime 会话。

解决方案概览

该示例由四部分组成。一个编码智能体在会话的 8080 端口上运行,并使用四种工具编辑网站文件:列出、读取、写入和编辑。智能体在 8090 端口上启动一个 Vite 开发服务器,该服务器提供网站服务,并通过其热重载 WebSocket 将每次保存的更改推送到浏览器。在你的机器上,PreviewApp 提供预览页面,将你的提示词发送给 InvokeAgentRuntime,并为网站打开一个本地端点。如图 1 所示,预览页面通过本地端点和 Session 端点,从 8090 端口上的 Vite 开发服务器加载网站,并通过 InvokeAgentRuntime 将你的提示词发送给 8080 端口上的智能体。数字表示处理一次请求的步骤顺序。

图 1:解决方案架构

在步骤 1 之前,PreviewApp 已调用过一次 InvokeAgentRuntime 来启动会话,同时启动智能体和 Vite 开发服务器。PreviewApp 从该响应中读取了 Session 端点 URL,并为 8090 端口打开了一个本地端点。图 1 中带编号的步骤随后展示了一次更改请求的处理过程:

  • 预览页面的网站窗格从 localhost:8088 上的本地端点加载网站。本地端点为每个请求和 WebSocket 握手添加你的凭证,并将其转发到 Session 端点。Session 端点检查签名以及端口是否符合 allowedPorts 中的配置,然后将其转发到 8090 端口上的 Vite 开发服务器。页面保持 Vite 热重载 WebSocket 连接打开。
  • 你从聊天窗格发送提示词。PreviewApp 将其作为使用你的 AWS 凭证签名的标准 AWS API 调用发送给 InvokeAgentRuntime,runtime 再将其传递给 8080 端口上的智能体。智能体工作时,其执行步骤会以流式方式返回到聊天窗格。
  • 智能体调用 Amazon Bedrock 中的 Claude Haiku 5.5,决定要做哪些更改。
  • 智能体读取网站文件并保存修改过的文件。Vite 开发服务器提供这些文件,并监视文件变更。
  • 文件保存后,Vite 通过步骤 1 中打开的 WebSocket,经过 Session 端点和本地端点推送更改,网站窗格无需刷新即可更新。
  • 在 notebook 中,PreviewApp 通过同一个 Session 端点读取智能体在 8081 端口上的 /status 路由。只有端口发生变化。

8080 端口不在 allowedPorts 中,因为 InvokeAgentRuntime 无需经过 Session 端点即可访问智能体。

前提条件

要跟随本教程操作,你需要:

  • 一个 AWS 账户,所在 AWS 区域提供 AgentCore runtime Session 端点,并已配置凭证。
  • 创建 AWS Identity and Access Management (IAM) 角色、向 Amazon Simple Storage Service (Amazon S3) 上传内容以及创建 runtime 的权限,此外还需要该 runtime 及其 DEFAULT runtime 端点的 bedrock-agentcore:InvokeAgentRuntime 和 bedrock-agentcore:InvokeAgentRuntimeProxy 权限。详情请参阅 AgentCore runtime 的 IAM 权限。
  • Python 3.10 或更高版本,以及 bedrock-agentcore 1.25.2 或更高版本和 boto3 1.43.122 或更高版本。
  • 用于克隆示例仓库的 Git,以及用于运行 notebook 的 Jupyter 环境,例如 JupyterLab 或安装了 Jupyter 扩展的 Visual Studio Code。
  • 访问 Amazon Bedrock 中的 Claude Haiku 5.5 的权限。有关各区域的模型可用性,请参阅各模型的区域可用性。
  • Node.js 22 或更高版本以及 npm,部署脚本会使用它们来打包智能体和网站。

该示例使用 Node.js 直接代码部署,将智能体作为 zip 文件部署在 Amazon S3 中,因此你不需要 Docker 或 Amazon Elastic Container Registry (Amazon ECR) 仓库。Session 端点适用于 microVM runtime,不适用于 Instances 计算类型;Session 端点 URL 可通过公共 HTTPS 访问。入站连接不支持 AWS PrivateLink。

部署智能体及其网站

首先,部署 runtime,你在此选择的配置将决定 Session 端点可以访问哪些端口。部署脚本会将智能体打包成一个文件,为 runtime 的 Linux on Arm64 系统添加网站及其依赖项,将 zip 文件上传到 Amazon S3,然后创建 runtime。

以下 deploy.py 代码摘录展示了本文涉及的两项重要设置。protocolConfiguration 指定智能体监听 InvokeAgentRuntime 的位置,allowedPorts 则列出 Session 端点可以访问的端口:网站的 8090 端口和智能体状态路由的 8081 端口。8080 端口不在列表中,因为 InvokeAgentRuntime 无需通过它即可访问智能体。有关端口和路径设置,请参阅配置智能体的服务端口和调用路径。

agentRuntimeArtifact={"codeConfiguration": {
"code": {"s3": {"bucket": code_bucket(), "prefix": S3_PREFIX}},
"runtime": "NODE_22",
"entryPoint": ["app.js"],
}},
protocolConfiguration={"serverProtocol": "HTTP", "port": 8080, "path": "/invocations"},
allowedPorts=[{"port": 8090}, {"port": 8081}],

该网站的 Vite 配置使开发服务器可以通过 Session 端点访问。服务器在端口 8090 上监听 0.0.0.0,并且不限制 Host 请求头(server.allowedHosts 为 true),因为请求使用的是运行时分配的主机名,而不是 localhost。Session 端点会在每个请求到达服务器之前对其进行身份验证,因此服务器无需自行检查主机名。

部署运行时:

  1. 克隆示例仓库并切换到示例文件夹:
git clone https://github.com/awslabs/agentcore-samples.git
cd agentcore-samples/01-features/02-host-your-agent/01-runtime/03-advanced/16-session-endpoints
  1. 从示例文件夹中打开 session_endpoints_preview.ipynb。

  2. 运行安装单元格,安装 Python 依赖项。

  3. 运行 %run deploy.py 单元格。部署需要几分钟,大部分时间都在等待运行环境达到 READY 状态。

打开预览

运行环境准备就绪后,即可启动预览。一次调用会完成三件事:启动会话(从而启动网站)、为端口 8090 开启一个本地端点,并在 localhost:8087 上提供预览页面。

from preview_app import PreviewApp

app = PreviewApp().start()
print("Open", app.url)

在 start() 内,使用 Session 端点的调用很简短。第一次 InvokeAgentRuntime 调用在响应头中返回 URL,session_endpoint_url 读取该 URL,然后 Session 端点客户端为端口 8090 打开一个本地端点:

answer = client.invoke_agent_runtime(
agentRuntimeArn=runtime_arn, runtimeSessionId=session_id,
payload=json.dumps({"prompt": ""}).encode(), contentType="application/json",
)
endpoint = client.session_endpoint_client(session_endpoint_url(answer))
local = endpoint.open_local_endpoint(port=8090, local_port=8088).start()

打开预览:

  • 运行以 PreviewApp 开头的单元格。
  • 在浏览器中打开输出的链接 http://localhost:8087。

页面右侧显示网站,由会话中的 8090 端口提供服务;左侧显示一份简短指南,列出了建议使用的请求。

要求智能体进行修改

现在,你可以提出修改要求,并观察修改如何生效。每条提示词都通过 InvokeAgentRuntime 发送给智能体。智能体会读取所需文件,写入需要修改的文件,并实时报告每个步骤。保存文件时,Vite 开发服务器通过 WebSocket,经由 Session 端点和本地端点推送更改,网站随即在原页面更新。

提出修改要求:

  • 在预览页面上,选择“将此网站切换为深色咖啡馆配色主题”。
  • 在聊天面板中观察智能体读取和保存文件的步骤。
  • 观察网站面板。智能体保存样式表时,网站会随之变化,一条横幅会显示更改来自哪个文件。

下面的视频展示了智能体处理多个请求时的预览页面。选择一条建议请求会将其发送给智能体,聊天面板会列出智能体读取和保存的每个文件,右侧的网站面板无需刷新即可更新,展示从深色主题到绿色“提前点单”按钮的变化。

图 2:智能体工作时的预览页面

这些步骤之所以能够流式显示,是因为智能体会在你请求时通过服务器发送事件返回响应。你也可以通过 Python 使用 app.ask 发送相同的提示词,它会使用 bedrock-agentcore SDK 中的 iter_events 读取事件流。已打开的页面也会以相同方式更新:

for event in app.ask("Make the Order ahead button green"):
if "step" in event:
print("  ", event["step"])
elif "done" in event:
print(event["done"]["reply"])

这个 Python 脚本的输出应类似于以下内容:

Looking at the site's files
Reading src/main.js
Reading src/style.css
Reading index.html
Saved src/style.css
The Order ahead button is now green instead of brown, with white text kept for contrast. You can see it in the hero section, just below the "Small batch coffee, roasted every morning" heading at the top of the page.

在同一会话中访问第二个端口

你能访问的服务器不只有网站。智能体在端口 8081 上提供 /status 服务,列出每条提示词及其修改的文件。同一个 Session 端点也可以访问该服务,唯一的区别是端口参数。

status = app.endpoint.request("GET", "/status", port=8081)
print("GET /status on port 8081: HTTP", status.status)
for edit in status.json()["edits"]:
print("  ", edit["prompt"], "->", ", ".join(edit["files"]))

这个 Python 脚本的输出应类似于以下内容:

GET /status on port 8081: HTTP 200
Make the Order ahead button green -> src/style.css

Session 端点会拒绝哪些请求

当运行时拒绝请求时,它会返回一个 HTTP 状态码、一个空响应体,并在响应头中提供错误代码,让你能够区分运行时的拒绝响应和应用程序自身返回的错误。笔记本会向 allowedPorts 之外的端口发送一个请求,再发送一个没有凭证的请求,运行时会以 403 AccessDenied 拒绝这两个请求。

下表列出了使用 Session 端点进行开发时可能遇到的拒绝响应。

请求 运行时的响应 不在 allowedPorts 中的端口 403 AccessDenied 没有有效凭证的请求 403 AccessDenied 或 401 AuthenticationRequired 允许访问但没有进程监听的端口 502 PortNotReachable 会话已不存在的 URL 404 SessionNotFound

如何保护对会话的访问

运行时在转发请求之前会移除自己的身份验证标头,因此网站不会收到你的签名。请求会作为 InvokeAgentRuntimeProxy 事件记录在 AWS CloudTrail 中,事件源为 bedrock-agentcore.amazonaws.com。本地端点使用你的凭证执行操作,因此会限制可使用该端点的对象。它只监听 127.0.0.1,只响应发送到其自身本地地址的请求,并拒绝由其他源的页面发起的 WebSocket 连接。只在使用预览时运行本地端点。更多信息,请参阅在 CloudTrail 中记录会话端点请求。

这些访问控制不会将网站与代理隔离。两者在同一个会话中运行,因此应只向执行角色授予代理所需的权限。如果运行时替换了会话的计算资源,已打开的连接会断开;除非会话使用持久化存储,否则代理所做的编辑可能无法保留。如果需要在计算资源终止后保留文件,请参阅 AgentCore 运行时的文件系统配置。

代理的文件工具会将每个路径解析到网站文件夹内,并拒绝该文件夹以外的路径,因此提示词无法让代理修改会话中的其他文件。如果其他人能够向你的编码代理发送提示词,可以考虑使用 Amazon Bedrock Guardrails 过滤有害的提示词和回复,并在发布代理所做的更改之前进行审查。

清理

为了避免持续产生费用,请停止预览并删除示例创建的资源。代理编辑的网站存放在会话中,因此会随会话一同被删除。

清理步骤:

  1. 运行停止单元格,并在笔记本提示时按 Enter。这会停止预览页面、本地端点和会话。

  2. 运行 %run cleanup.py 单元格,它会删除运行时、上传的代码和 IAM 角色。

结论

Session 端点让你能够通过自己的工具,使用 HTTP/1.1、HTTP/2 和 WebSocket,经过身份验证访问 AgentCore 运行时会话中的应用程序,访问范围限于你允许的端口。在本文中,你部署了一个编码代理,其会话中包含一个 Vite 网站;通过本地端点在浏览器中打开了实时网站;让代理添加深色主题;并在代理保存文件时看到了更改。你还访问了同一会话中的第二个端口,并确认了 Session 端点会拒绝哪些请求。

如需进一步探索,可以将自己的网站放入 site 文件夹并运行笔记本,或者修改代理的工具,使其符合你的编码代理的工作方式。有关全部选项,包括端口范围、OAuth、HTTP/2 和 gRPC,请参阅通过会话端点连接到运行时会话。

要更改你自己的代理中 InvokeAgentRuntime 使用的端口或路径,请参阅配置代理的服务端口和调用路径。如果你的代理还需要在同一会话中运行构建或测试命令,请参阅在 AgentCore 运行时会话中执行 shell 命令。要像本示例一样通过 zip 文件部署你自己的 Node.js 代理,请参阅 Node.js 代码直接部署。有关该服务提供的其他功能,请从使用 Amazon Bedrock AgentCore 运行时托管代理或工具入手,并在 agentcore-samples GitHub 仓库中查找更多示例。