TypeScript Strands 代理
生成一个 TypeScript Strands Agent 用于构建带有工具的 AI 代理,并可选择将其部署到 Amazon Bedrock AgentCore Runtime。该生成器使用基于 WebSocket 的 tRPC 来利用 AgentCore 的双向流支持,实现实时、类型安全的通信。
什么是 Strands?
Section titled “什么是 Strands?”Strands 是一个用于构建 AI 代理的轻量级框架。主要特性包括:
- 轻量且可定制: 简单的代理循环,不会妨碍您的工作
- 生产就绪: 完整的可观测性、追踪和大规模部署选项
- 模型和提供商无关: 支持来自各种提供商的多种不同模型
- 社区驱动的工具: 强大的社区贡献工具集
- 多代理支持: 高级技术,如代理团队和自主代理
- 灵活的交互模式: 对话式、流式和非流式支持
生成 Strands Agent
Section titled “生成 Strands Agent”您可以通过两种方式生成 TypeScript Strands Agent:
- 安装 Nx Console VSCode Plugin 如果您尚未安装
- 在VSCode中打开Nx控制台
- 点击
Generate (UI)在"Common Nx Commands"部分 - 搜索
@aws/nx-plugin - ts#strands-agent - 填写必需参数
- 点击
Generate
pnpm nx g @aws/nx-plugin:ts#strands-agentyarn nx g @aws/nx-plugin:ts#strands-agentnpx nx g @aws/nx-plugin:ts#strands-agentbunx nx g @aws/nx-plugin:ts#strands-agent您还可以执行试运行以查看哪些文件会被更改
pnpm nx g @aws/nx-plugin:ts#strands-agent --dry-runyarn nx g @aws/nx-plugin:ts#strands-agent --dry-runnpx nx g @aws/nx-plugin:ts#strands-agent --dry-runbunx nx g @aws/nx-plugin:ts#strands-agent --dry-run| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| project 必需 | string | - | The project to add the Strands Agent to |
| computeType | string | BedrockAgentCoreRuntime | The type of compute to host your Strands Agent. |
| name | string | - | The name of your Strands Agent (default: agent) |
| iacProvider | string | Inherit | The preferred IaC provider. By default this is inherited from your initial selection. |
生成器将向您现有的 TypeScript 项目添加以下文件:
文件夹your-project/
文件夹src/
文件夹agent/ (如果指定,则为自定义名称)
- index.ts Bedrock AgentCore Runtime 的入口点
- init.ts tRPC 初始化
- router.ts 带有代理过程的 tRPC 路由器
- agent.ts 带有示例工具的主代理定义
- client.ts 用于调用代理的客户端
- agent-core-trpc-client.ts 用于连接到 AgentCore Runtime 上的代理的客户端工厂
- agent-core-mcp-client.ts 用于连接到 AgentCore Runtime 上的 MCP 服务器的客户端工厂
- Dockerfile 托管代理的入口点(当
computeType设置为None时排除)
- package.json 已更新 Strands 依赖项
- project.json 已更新代理服务目标
由于该生成器会根据您选择的 iacProvider 以基础设施即代码的形式输出,它将在 packages/common 目录下创建一个包含相关 CDK 构造体或 Terraform 模块的项目。
通用的基础设施即代码项目结构如下:
文件夹packages/common/constructs
文件夹src
文件夹app/ 针对特定项目/生成器的基础设施构造体
- …
文件夹core/ 被
app目录构造体重用的通用构造体- …
- index.ts 导出
app目录构造体的入口文件
- project.json 项目构建目标与配置
文件夹packages/common/terraform
文件夹src
文件夹app/ 针对特定项目/生成器的 Terraform 模块
- …
文件夹core/ 被
app目录模块重用的通用模块- …
- project.json 项目构建目标与配置
为了部署您的 Strands Agent,将生成以下文件:
文件夹packages/common/constructs/src
文件夹app
文件夹agents
文件夹<project-name>
- <project-name>.ts 用于部署代理的 CDK 构造
- Dockerfile CDK 构造使用的传递 docker 文件
文件夹packages/common/terraform/src
文件夹app
文件夹agents
文件夹<project-name>
- <project-name>.tf 用于部署代理的模块
文件夹core
文件夹agent-core
- runtime.tf 用于部署到 Bedrock AgentCore Runtime 的通用模块
使用您的 Strands Agent
Section titled “使用您的 Strands Agent”基于 WebSocket 的 tRPC
Section titled “基于 WebSocket 的 tRPC”TypeScript Strands Agent 使用基于 WebSocket 的 tRPC,利用 AgentCore 的双向流支持 在客户端和您的代理之间实现实时、类型安全的通信。
由于 tRPC 支持通过 WebSocket 的 Query、Mutation 和 Subscription 过程,您可以定义任意数量的过程。默认情况下,在 router.ts 中为您定义了一个名为 invoke 的单个订阅过程。
工具是 AI 代理可以调用以执行操作的函数。您可以在 agent.ts 文件中添加新工具:
import { Agent, tool } from '@strands-agents/sdk';import z from 'zod';
const letterCounter = tool({ name: 'letter_counter', description: 'Count occurrences of a specific letter in a word', inputSchema: z.object({ word: z.string().describe('The input word to search in'), letter: z.string().length(1).describe('The specific letter to count'), }), callback: (input) => { const { word, letter } = input; const count = word.toLowerCase().split(letter.toLowerCase()).length - 1; return `The letter '${letter}' appears ${count} time(s) in '${word}'`; },});
// Add tools to your agentexport const agent = new Agent({ systemPrompt: 'You are a helpful assistant with access to various tools.', tools: [letterCounter],});Strands 框架自动处理:
- 使用 Zod 模式进行输入验证
- 为工具调用生成 JSON 模式
- 错误处理和响应格式化
默认情况下,Strands 代理使用 Claude 4 Sonnet,但您可以轻松切换模型提供商:
import { Agent } from '@strands-agents/sdk';import { BedrockModel } from '@strands-agents/sdk/models/bedrock';import { OpenAIModel } from '@strands-agents/sdk/models/openai';
// Use Bedrockconst bedrockModel = new BedrockModel({ modelId: 'anthropic.claude-sonnet-4-20250514-v1:0',});let agent = new Agent({ model: bedrockModel });let response = await agent.invoke('What can you help me with?');
// Alternatively, use OpenAI by just switching model providerconst openaiModel = new OpenAIModel({ apiKey: process.env.OPENAI_API_KEY, modelId: 'gpt-4o',});agent = new Agent({ model: openaiModel });response = await agent.invoke('What can you help me with?');有关更多配置选项,请参阅 Strands 关于模型提供商的文档。
使用 MCP 服务器
Section titled “使用 MCP 服务器”您可以从 MCP 服务器添加工具到您的 Strands 代理。
对于使用 py#mcp-server 或 ts#mcp-server 生成器创建的 MCP 服务器(或托管在 Bedrock AgentCore Runtime 上的其他服务器),会在 agent-core-mcp-client.ts 中为您生成一个客户端工厂。
您可以在 agent.ts 中更新代理初始化以创建 MCP 客户端并添加工具。以下示例显示如何使用 IAM (SigV4) 身份验证执行此操作:
import { Agent } from '@strands-agents/sdk';import { AgentCoreMcpClient } from './agent-core-mcp-client.js';
const mcpClient = AgentCoreMcpClient.withIamAuth({ agentRuntimeArn: process.env.MCP_AGENTCORE_RUNTIME_ARN!, region: process.env.AWS_REGION || 'us-west-2', sessionId: 'my-session-id',});
export const agent = new Agent({ systemPrompt: '...', tools: [mcpClient],});使用上面的 IAM 身份验证示例,我们需要在基础设施中配置两件事。首先,我们需要为代理添加环境变量,用于 MCP 服务器的 AgentCore Runtime ARN,其次我们需要授予代理调用 MCP 服务器的权限。这可以按如下方式实现:
import { MyProjectAgent, MyProjectMcpServer } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const mcpServer = new MyProjectMcpServer(this, 'MyProjectMcpServer');
const agent = new MyProjectAgent(this, 'MyProjectAgent', { environmentVariables: { MCP_AGENTCORE_RUNTIME_ARN: mcpServer.agentCoreRuntime.agentRuntimeArn, }, });
mcpServer.agentCoreRuntime.grantInvoke(agent.agentCoreRuntime); }}# MCP Servermodule "my_project_mcp_server" { source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"}
# Agentmodule "my_project_agent" { source = "../../common/terraform/src/app/agents/my-project-agent"
env = { MCP_AGENTCORE_RUNTIME_ARN = module.my_project_mcp_server.agent_core_runtime_arn }
additional_iam_policy_statements = [ { Effect = "Allow" Action = [ "bedrock-agentcore:InvokeAgentRuntime" ] Resource = [ module.my_project_mcp_server.agent_core_runtime_arn, "${module.my_project_mcp_server.agent_core_runtime_arn}/*" ] } ]}有关编写 Strands 代理的更深入指南,请参阅 Strands 文档。
运行您的 Strands Agent
Section titled “运行您的 Strands Agent”生成器配置了一个名为 <your-agent-name>-serve 的目标,用于在本地启动您的 Strands Agent 进行开发和测试。
pnpm nx run your-project:agent-serveyarn nx run your-project:agent-servenpx nx run your-project:agent-servebunx nx run your-project:agent-serve此命令使用 tsx --watch 在文件更改时自动重启服务器。代理将在 http://localhost:8081 上可用(如果您有多个代理,则为分配的端口)。
将您的 Strands Agent 部署到 Bedrock AgentCore Runtime
Section titled “将您的 Strands Agent 部署到 Bedrock AgentCore Runtime”基础设施即代码
Section titled “基础设施即代码”如果您为 computeType 选择了 BedrockAgentCoreRuntime,将生成相关的 CDK 或 Terraform 基础设施,您可以使用它将 Strands Agent 部署到 Amazon Bedrock AgentCore Runtime。
系统会为您的代理生成一个 CDK 构造,其名称基于您运行生成器时选择的 name,或默认为 <ProjectName>Agent。
您可以在 CDK 应用程序中使用此 CDK 构造:
import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // Add the agent to your stack const agent = new MyProjectAgent(this, 'MyProjectAgent');
// Grant permissions to invoke the relevant models in bedrock agent.agentCoreRuntime.addToRolePolicy( new PolicyStatement({ actions: [ 'bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream', ], // You can scope the below down to the specific models you use resources: [ 'arn:aws:bedrock:*:*:foundation-model/*', 'arn:aws:bedrock:*:*:inference-profile/*', ], }), ); }}系统会为您生成一个 Terraform 模块,其名称基于您运行生成器时选择的 name,或默认为 <ProjectName>-agent。
您可以在 Terraform 项目中使用此 terraform 模块:
# Agentmodule "my_project_agent" { # Relative path to the generated module in the common/terraform project source = "../../common/terraform/src/app/agents/my-project-agent"
# Grant permissions to invoke the relevant models in bedrock additional_iam_policy_statements = [ { Effect = "Allow" Action = [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream" ] # You can scope the below down to the specific models you use Resource = [ "arn:aws:bedrock:*:*:foundation-model/*", "arn:aws:bedrock:*:*:inference-profile/*" ] } ]}默认情况下,您的 Strands Agent 将使用 IAM 身份验证进行保护,只需在不带任何参数的情况下部署它:
import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { new MyProjectAgent(this, 'MyProjectAgent'); }}您可以使用 grantInvoke 方法授予调用 Bedrock AgentCore Runtime 上代理的访问权限,例如:
import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const agent = new MyProjectAgent(this, 'MyProjectAgent'); const lambdaFunction = new Function(this, ...);
agent.agentCoreRuntime.grantInvoke(lambdaFunction); }}# Agentmodule "my_project_agent" { # Relative path to the generated module in the common/terraform project source = "../../common/terraform/src/app/agents/my-project-agent"}要授予调用代理的访问权限,您需要添加如下策略,引用 module.my_project_agent.agent_core_runtime_arn 输出:
{ Effect = "Allow" Action = [ "bedrock-agentcore:InvokeAgentRuntime" ] Resource = [ module.my_project_agent.agent_core_runtime_arn, "${module.my_project_agent.agent_core_runtime_arn}/*" ]}Cognito JWT 身份验证
Section titled “Cognito JWT 身份验证”以下演示了如何为您的代理配置 Cognito 身份验证。
要使用 Cognito 配置 JWT 身份验证,请使用 RuntimeAuthorizerConfiguration.usingCognito() 工厂方法:
import { MyProjectAgent } from ':my-scope/common-constructs';import { RuntimeAuthorizerConfiguration } from '@aws-cdk/aws-bedrock-agentcore-alpha';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const userPool = new UserPool(this, 'UserPool'); const client = userPool.addClient('Client', { authFlows: { userPassword: true, }, });
new MyProjectAgent(this, 'MyProjectAgent', { authorizerConfiguration: RuntimeAuthorizerConfiguration.usingCognito( userPool, [client], ), }); }}或者,对于使用您自己的 OIDC 提供商的自定义 JWT 身份验证,请使用 RuntimeAuthorizerConfiguration.usingJWT():
import { MyProjectAgent } from ':my-scope/common-constructs';import { RuntimeAuthorizerConfiguration } from '@aws-cdk/aws-bedrock-agentcore-alpha';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { new MyProjectAgent(this, 'MyProjectAgent', { authorizerConfiguration: RuntimeAuthorizerConfiguration.usingJWT( 'https://example.com/.well-known/openid-configuration', ['client1', 'client2'], // Allowed Client IDs (optional) ['audience1'], // Allowed Audiences (optional) ), }); }}要配置 JWT 身份验证,您可以编辑代理模块以配置 authorizer_configuration 变量,如下所示:
data "aws_region" "current" {}
locals { aws_region = data.aws_region.current.id
# Replace with your user pool and client ids or expose as variables user_pool_id = "xxx" user_pool_client_ids = ["yyy"]}
module "agent_core_runtime" { source = "../../../core/agent-core" agent_runtime_name = "MyProjectAgent" docker_image_tag = "my-scope-my-project-agent:latest" server_protocol = "HTTP" authorizer_configuration = { custom_jwt_authorizer = { discovery_url = "https://cognito-idp.${local.aws_region}.amazonaws.com/${local.user_pool_id}/.well-known/openid-configuration" allowed_clients = local.user_pool_client_ids } } env = var.env additional_iam_policy_statements = var.additional_iam_policy_statements tags = var.tags}Bundle 目标
Section titled “Bundle 目标”生成器会自动配置一个使用 Rolldown 创建部署包的 bundle 目标:
pnpm nx run <project-name>:bundleyarn nx run <project-name>:bundlenpx nx run <project-name>:bundlebunx nx run <project-name>:bundleRolldown 配置位于 rolldown.config.ts 文件中,每个要生成的包都有对应的入口配置。如果定义了多个包,Rolldown 会并行管理这些包的创建过程。
bundle 目标使用 index.ts 作为在 Bedrock AgentCore Runtime 上托管的 WebSocket 服务器的入口点。
Docker 目标
Section titled “Docker 目标”生成器配置了一个 <your-agent-name>-docker 目标,根据 AgentCore 运行时契约 在端口 8080 上运行打包的 WebSocket 服务器。
如果您定义了多个代理,还会生成一个 docker 目标,用于为所有代理运行 docker 构建。
您的代理会自动配置可观测性,使用 AWS Distro for Open Telemetry (ADOT),通过在 Dockerfile 中配置自动检测。
您可以在 CloudWatch AWS 控制台中找到追踪,方法是在菜单中选择”GenAI Observability”。请注意,要填充追踪,您需要启用 Transaction Search。
有关更多详细信息,请参阅 AgentCore 关于可观测性的文档。
调用您的 Strands Agent
Section titled “调用您的 Strands Agent”代理通信通过基于 WebSocket 的 tRPC 传输。因此,建议使用 client.ts 中生成的类型安全客户端工厂。
调用本地服务器
Section titled “调用本地服务器”您可以使用客户端工厂的 .local 工厂方法调用本地运行的代理。
例如,您可以在工作区中创建一个名为 scripts/test.ts 的文件来导入客户端:
import { AgentClient } from '../packages/<project>/src/agent/client.js';
const client = AgentClient.local({ url: 'http://localhost:8081/ws' });
client.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: console.log });调用已部署的代理
Section titled “调用已部署的代理”要调用部署到 Bedrock AgentCore Runtime 的 Agent,您可以使用 URL 编码的运行时 ARN 向 Bedrock AgentCore Runtime 数据平面端点发送 POST 请求。
您可以按如下方式从基础设施中获取运行时 ARN:
import { CfnOutput } from 'aws-cdk-lib';import { MyProjectAgent } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const agent = new MyProjectAgent(this, 'MyProjectAgent');
new CfnOutput(this, 'AgentArn', { value: agent.agentCoreRuntime.agentRuntimeArn, }); }}# Agentmodule "my_project_agent" { # Relative path to the generated module in the common/terraform project source = "../../common/terraform/src/app/agents/my-project-agent"}
output "agent_arn" { value = module.my_project_agent.agent_core_runtime_arn}ARN 将具有以下格式:arn:aws:bedrock-agentcore:<region>:<account>:runtime/<agent-runtime-id>。
然后,您可以通过将 : 替换为 %3A 和将 / 替换为 %2F 来对 ARN 进行 URL 编码。
用于调用 agent 的 Bedrock AgentCore Runtime 数据平面 URL 如下:
https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-arn>/invocations调用此 URL 的具体方式取决于所使用的身份验证方法。
NodeJS
Section titled “NodeJS”生成的 client.ts 文件包含一个类型安全的客户端工厂,可用于调用已部署的代理。
IAM 身份验证
Section titled “IAM 身份验证”您可以通过将其 ARN 传递给 withIamAuth 工厂方法来调用已部署的代理:
import { AgentClient } from './agent/client.js';
const client = AgentClient.withIamAuth({ agentRuntimeArn: 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent',});
client.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: (message) => console.log(message), onError: (error) => console.error(error), onComplete: () => console.log('Done'),});JWT / Cognito 身份验证
Section titled “JWT / Cognito 身份验证”使用 withJwtAuth 工厂方法通过 JWT / Cognito 访问令牌进行身份验证。
const client = AgentClient.withJwtAuth({ agentRuntimeArn: 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent', accessTokenProvider: async () => `<access-token>`,});
client.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: console.log,});accessTokenProvider 必须返回用于验证请求的令牌。例如,您可以在此方法中获取令牌,以确保在 tRPC 重启 WebSocket 连接时重用新凭证。以下演示了使用 AWS SDK 从 Cognito 获取令牌:
import { CognitoIdentityProvider } from "@aws-sdk/client-cognito-identity-provider";
const cognito = new CognitoIdentityProvider();
const jwtClient = AgentClient.withJwtAuth({ agentRuntimeArn: 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent', accessTokenProvider: async () => { const response = await cognito.adminInitiateAuth({ UserPoolId: '<user-pool-id>', ClientId: '<user-pool-client-id>', AuthFlow: 'ADMIN_NO_SRP_AUTH', AuthParameters: { USERNAME: '<username>', PASSWORD: '<password>', }, }); return response.AuthenticationResult!.AccessToken!; },});浏览器中的 WebSocket 不支持指定头(除了 Sec-WebSocket-Protocol),因此 client.ts 中生成的客户端工厂无法在浏览器中使用(这实际上会导致编译错误,因为 WebSocket 构造函数不像在 NodeJS 中那样接受头)。
JWT / Cognito 身份验证
Section titled “JWT / Cognito 身份验证”IAM 身份验证
Section titled “IAM 身份验证”要从浏览器调用您的代理,您需要使用 AWS SigV4 创建预签名的 WebSocket URL。
以下示例显示了获取凭证、创建预签名 URL 和调用代理的端到端流程:
import { createTRPCClient, createWSClient, wsLink } from '@trpc/client';import { AwsClient } from 'aws4fetch';import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity';import { fromCognitoIdentityPool } from '@aws-sdk/credential-provider-cognito-identity';import type { AppRouter } from './your-agent/router';
// Build a presigned WebSocket URLasync function buildSignedUrl( agentRuntimeArn: string, idToken: string, region: string = 'us-west-2'): Promise<string> { // Get credentials from a Cognito Identity Pool (or other source) const credentials = fromCognitoIdentityPool({ client: new CognitoIdentityClient({ region }), identityPoolId: 'us-west-2:xxxxx', logins: { [`cognito-idp.${region}.amazonaws.com/us-west-2_xxxxx`]: idToken, }, });
const cognitoIdentity = new CognitoIdentityClient({ credentials }); const credential = await cognitoIdentity.config.credentials();
// Create AWS SigV4 client const awsClient = new AwsClient({ ...credential, service: 'bedrock-agentcore', });
// Build WebSocket URL from ARN const wsUrl = `wss://bedrock-agentcore.${region}.amazonaws.com/runtimes/${agentRuntimeArn.replace(/:/g, '%3A').replace(/\//g, '%2F')}/ws`;
// Create presigned URL const signedRequest = await awsClient.sign(wsUrl, { method: 'GET', aws: { signQuery: true }, });
return signedRequest.url;}
// Create tRPC client with presigned WebSocket URLconst agentRuntimeArn = 'arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent';const idToken = '<your-id-token>';
const wsClient = createWSClient({ url: async () => buildSignedUrl(agentRuntimeArn, idToken),});
const trpcClient = createTRPCClient<AppRouter>({ links: [wsLink({ client: wsClient })],});
// Invoke the agenttrpcClient.invoke.subscribe({ message: 'what is 1 plus 1?' }, { onData: (message) => console.log(message),});