Serverless Framework 实战:在 AWS Bedrock AgentCore 上部署带代码解释器的 LangGraph JavaScript Agent
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
本文基于 Serverless Framework 仓库中bedrock-agentcore插件的官方示例,完整讲解如何使用LangGraph JS + AWS 托管代码解释器(Code Interpreter)构建一个可执行 Python/JavaScript/TypeScript 代码、读写沙箱文件、运行 Shell 命令的 AI Agent,并把它部署到 AWS Bedrock AgentCore。读完本文,你将掌握示例工程的结构、serverless.yml中最简配置的写法、沙箱代码执行工具的实现方式、本地端到端测试(test-invoke.js)以及后续移除资源的标准流程。
示例工程完整源码位于 langgraph-code-interpreter 示例目录,配套插件说明见 Bedrock AgentCore Plugin 文档。
示例概览:让 Agent 真正"动手算"
大模型本身的弱点是"只说不做"。本示例解决的核心问题就是:当模型需要精确计算、生成/分析数据或运行脚本时,如何安全地让它执行真实代码。
它由两部分协作构成:
- LangGraph JS Agent:采用 LangChain/LangGraph 生态的
createAgent(ReAct 模式的 Agent),由 Claude(Amazon Bedrock 上的ChatBedrockConverse)驱动,负责理解用户问题并自主决定何时调用工具; - AWS 托管代码解释器:示例直接使用AWS 管理的默认解释器(SANDBOX 模式),无需自建容器,模型通过一组文件/命令工具驱动解释器在隔离沙箱中执行代码。
示例声明的核心能力(对应 README):
- 沙箱化执行:在 SANDBOX 环境中运行 Python、JavaScript 或 TypeScript;
- 文件操作:在沙箱中读取、写入、列出文件;
- Shell 命令:执行 Shell 命令;
- LangGraph JS:ReAct 模式的 Agent + 代码执行工具链。
提供给 Agent 的 5 个工具
示例中所有能力都以工具(tool)形式暴露给模型,由模型按需自主调用:
| 工具 | 作用 |
|---|---|
execute_code | 在沙箱中执行 Python / JavaScript / TypeScript 代码 |
execute_command | 运行 Shell 命令 |
read_files | 读取沙箱中的文件内容 |
write_files | 向沙箱写入文件 |
list_files | 列出目录内容 |
这 5 个工具并不是凭空注册的——它们分别是对CodeInterpreter客户端方法的薄封装(详见下文 index.js 实现解析)。仓库中还有一组同主题的 Python 版本示例可供对照:langgraph-code-interpreter(Python)。
工程结构与零配置 serverless.yml
示例目录文件如下:
langgraph-code-interpreter/ ├── serverless.yml # Serverless Framework 配置(Agent 定义) ├── index.js # LangGraph JS Agent 主逻辑(入口) ├── test-invoke.js # 本地端到端测试脚本 ├── package.json # npm 依赖与 Node 版本声明 ├── package-lock.json └── README.md其中最值得一提的是serverless.yml只有寥寥几行:
# Deploy: serverless deploy # Test: RUNTIME_ARN=<arn> node test-invoke.js service: langgraph-code-interpreter provider: name: aws ai: agents: # Runtime agent with code execution capabilities # Uses AWS-managed default code interpreter (SANDBOX mode) codeAgent: {}对应 serverless.yml 的完整内容。它之所以能这么简洁,是因为:
ai顶级配置属性由 Serverless Framework 的 Bedrock AgentCore 插件解析(见插件 README),负责把ai.agents.codeAgent翻译成 AgentCore 的 CloudFormation 资源;- 不需要显式声明代码解释器:默认的 AWS 托管代码解释器(SANDBOX 模式)由 AgentCore 自动检测并注入,这正是本示例与"自定义解释器"示例的最大区别;
- 部署形态:目录中没有
Dockerfile,插件会自动根据package.json检测 Node.js 运行环境、执行依赖安装并构建 ARM64 容器镜像——这与 langgraph-basic 示例 描述的"免 Dockerfile 构建"机制一致; package.json通过engines.node声明24.x(见 package.json),作为容器基础镜像选型的依据。
ai顶层属性下实际上支持 6 类资源:agents(运行时 Agent)、memory(对话记忆)、tools(Lambda/OpenAPI/Smithy/MCP 工具)、gateways(工具路由网关)、browsers(浏览器能力)与codeInterpreters(自定义代码解释器)。每种资源使用独立的节区,不存在type判别字段。本示例只用到agents,且整个 Agent 采用默认配置。
核心实现 index.js 解析
index.js采用BedrockAgentCoreApp的运行时编程模型,整体流程是:初始化 LLM → 注册 Agent 应用 → 在每次请求中创建独立的CodeInterpreter→ 用 5 个工具包裹其方法 →createAgent组装 → 调用并返回结果。
1. 模型与运行时初始化
const AWS_REGION = process.env.AWS_REGION || 'us-east-1' const MODEL_ID = process.env.MODEL_ID || 'us.anthropic.claude-sonnet-4-5-20250929-v1:0' const model = new ChatBedrockConverse({ model: MODEL_ID, region: AWS_REGION, }) const app = new BedrockAgentCoreApp({ invocationHandler: { requestSchema: z.object({ prompt: z.string().describe('The user message to process'), }), async process(request, context) { /* ... */ }, }, }) app.run()关键点:
- 模型默认使用 Claude Sonnet 4.5(
us.anthropic.claude-sonnet-4-5-20250929-v1:0),均可用MODEL_ID、AWS_REGION环境变量覆盖;其中AWS_REGION会被 AgentCore 自动注入到运行时; - 运行时 SDK 来自
bedrock-agentcore/runtime(npm 依赖bedrock-agentcore,见 package.json 第 13 行); requestSchema用 zod 声明请求载荷为{ prompt },与客户端test-invoke.js发送的 JSON 一一对应。
2. 每次请求创建独立的代码解释器会话
const sessionId = context?.sessionId || 'default' // Create code interpreter per request (for session isolation) const codeInterpreter = new CodeInterpreter({ region: AWS_REGION })示例刻意在process内部创建解释器实例,以便实现会话级隔离:每个请求拥有独立的执行沙箱,互不污染。
3. 工具封装:5 个工具对应 5 个方法
下面完整展示 5 个工具的定义(均使用 LangChain 的tool()与 zod schema),这是理解"代码解释器如何变成 Agent 工具"的核心代码。
执行代码:
const executeCode = tool( async ({ code, language }) => { const result = await codeInterpreter.executeCode({ code, language: language || 'python', }) return result || 'Code executed successfully (no output)' }, { name: 'execute_code', description: 'Execute code in a secure sandbox. Supports Python, JavaScript, and TypeScript.', schema: z.object({ code: z.string().describe('Code to execute'), language: z .enum(['python', 'javascript', 'typescript']) .optional() .describe('Programming language (default: python)'), }), }, )执行 Shell 命令:
const executeCommand = tool( async ({ command }) => { const result = await codeInterpreter.executeCommand({ command }) return result || 'Command executed successfully (no output)' }, { name: 'execute_command', description: 'Execute a shell command in the sandbox.', schema: z.object({ command: z.string().describe('Shell command to execute'), }), }, )读取文件:
const readFiles = tool( async ({ paths }) => { const result = await codeInterpreter.readFiles({ paths }) return result || 'No content' }, { name: 'read_files', description: 'Read contents of files in the sandbox.', schema: z.object({ paths: z.array(z.string()).describe('List of file paths to read'), }), }, )写入文件:
const writeFiles = tool( async ({ files }) => { const result = await codeInterpreter.writeFiles({ files }) return result || 'Files written successfully' }, { name: 'write_files', description: 'Write files in the sandbox.', schema: z.object({ files: z .array( z.object({ path: z.string().describe('File path'), content: z.string().describe('File content'), }), ) .describe('Files to write'), }), }, )列出目录:
const listFiles = tool( async ({ path }) => { const result = await codeInterpreter.listFiles({ path: path || '.' }) return result || 'No files found' }, { name: 'list_files', description: 'List files in the sandbox directory.', schema: z.object({ path: z .string() .optional() .describe('Directory path (default: current directory)'), }), }, )4. 组装 Agent 并完成一轮推理
const codeTools = [executeCode, executeCommand, readFiles, writeFiles, listFiles] const agent = createAgent({ model, tools: codeTools, }) const result = await agent.invoke({ messages: [{ role: 'user', content: request.prompt }], }) const finalMessage = result.messages[result.messages.length - 1] const response = finalMessage.content return JSON.stringify({ result: response, tools_used: codeTools.map((t) => t.name), interpreter_type: 'default', })这段代码体现了 ReAct 模式的精髓:Agent 根据用户问题规划步骤,若需要计算/文件操作便自主挑选合适工具调用,最后把推理结果作为result返回给客户端。
5. 会话清理
无论成功还是失败,方法都在finally中显式停止解释器会话,避免沙箱资源泄漏:
} finally { try { await codeInterpreter.stopSession() console.log('Code interpreter session stopped') } catch { // ignore cleanup errors } }沙箱模式说明(SANDBOX / PUBLIC / VPC)
本示例使用的是AWS 托管默认代码解释器,网络模式为SANDBOX(无网络访问),适合纯隔离计算场景。从底层编译器源码可以确认这种模式划分:
compilers/codeInterpreter.js中的buildCodeInterpreterNetworkConfiguration会将配置归一化映射为AWS::BedrockAgentCore::CodeInterpreterCustom资源的NetworkConfiguration:
// mode 缺省时大写归一为 SANDBOX,支持 PUBLIC / SANDBOX / VPC const networkMode = (network.mode || 'SANDBOX').toUpperCase()对应 CloudFormation 模式枚举:PUBLIC(可访问外网)、SANDBOX(默认,隔离无网)、VPC(私有网络,需提供 Subnets 与 SecurityGroups)。
插件 README 中关于ai.codeInterpreters的配置表也印证了这一点:
| 属性 | 是否必填 | 说明 |
|---|---|---|
network.mode | 否 | sandbox(默认)、public或vpc |
network.subnets | 否 | VPC 子网 ID(vpc模式必填) |
network.securityGroups | 否 | VPC 安全组 ID(vpc模式必填) |
description | 否 | CodeInterpreter 描述(≤1200 字符) |
role | 否 | IAM 角色 ARN 或定制化对象 |
tags | 否 | 资源标签键值对 |
因此:只要你的场景是"隔离的数值/数据处理",默认解释器(如本示例)就是最省事的方案;当 Agent 需要调用外部 API、抓取网页等联网能力时,才需要显式声明一个network.mode: public的自定义解释器。
补充:需要公网时的自定义解释器
仓库提供了配套对照示例 langgraph-code-interpreter-custom,两示例的差异可概括为:
| 维度 | 默认解释器(本示例) | 自定义解释器(custom 示例) |
|---|---|---|
| 网络模式 | SANDBOX(无网络) | PUBLIC(可访问公网) |
| 标识 | AWS 托管(aws.codeinterpreter.v1) | 自定义解释器 ID |
| 典型场景 | 隔离计算 | 外部 API 调用、网页抓取 |
自定义示例的serverless.yml会在ai.codeInterpreters下定义解释器、通过!GetAtt拿到CodeInterpreterId写入环境变量,并手动授予StartCodeInterpreterSession/InvokeCodeInterpreter/StopCodeInterpreterSession权限;而本示例由于使用托管默认解释器,上述 IAM 授权全部由插件自动生成,无需任何手工配置。
从部署到端到端验证
1. 部署
在示例目录下执行:
npm install sls deploy前置条件(与仓库其他 LangGraph JS 示例一致,参见 langgraph-basic README):
- Node.js 20+
- Docker(用于自动构建容器镜像)
- 已配置 AWS 凭证
- Serverless Framework CLI(
npm install -g serverless)
sls deploy完成后,从 CloudFormation 输出中取出 Agent 的Runtime ARN。按插件约定,每个 Agent 资源都会自动生成形如{Name}RuntimeArn的输出(AgentCore 插件的输出命名规则见 CloudFormation Outputs 一节),因此本例可在部署输出中找到codeAgentRuntimeArn。
2. 本地端到端测试
测试脚本通过InvokeAgentRuntimeCommand直接调用已部署的 Agent,读取环境变量RUNTIME_ARN与可选AWS_REGION,并为每次运行生成随机SESSION_ID:
RUNTIME_ARN=<your-runtime-arn> node test-invoke.js脚本内置了两组真实场景的测试用例(见 test-invoke.js):
- Test 1:让 Agent"编写并执行 Python 代码,计算并展示前 20 个斐波那契数";
- Test 2:让 Agent"生成 1~100 之间的 10 个随机数,并计算它们的均值、中位数与标准差"。
这两组用例都要求模型真正调用execute_code工具让沙箱执行代码,而不是凭语言模型"猜答案",能非常直观地验证代码解释器是否正常工作。
调用协议方面值得注意的实现细节(同样来自test-invoke.js):
const command = new InvokeAgentRuntimeCommand({ agentRuntimeArn: RUNTIME_ARN, runtimeSessionId: SESSION_ID, payload: Buffer.from(JSON.stringify({ prompt: inputText })), contentType: 'application/json', accept: 'application/json, text/event-stream', })payload的 JSON 结构{ prompt }必须与index.js中requestSchema的 zod 声明严格一致;accept同时声明 JSON 与text/event-stream,脚本对异步迭代的响应体逐块聚合后尝试JSON.parse,优先输出result字段。
此外,index.js通过tools_used字段会把本次实际调用的工具列表随结果返回,方便你在测试输出中确认 Agent 是否真的按预期执行了代码——例如{"result":"...", "tools_used":["execute_code"], "interpreter_type":"default"}。
3. 更多验证与清理手段
插件为 AgentCore 资源提供了一组配套 CLI 命令(见插件 README 的 Commands 一节),部署完成后也可以直接调用:
sls invoke --agent codeAgent -d "Hello" # 直接调用已部署的 Agent sls logs --agent codeAgent # 拉取 Agent 运行日志 sls package # 仅生成 CloudFormation,不部署不再需要时执行资源清理:
sls remove延伸阅读
如果你希望进一步探索 AgentCore 代码执行能力的边界,建议继续阅读仓库内以下相关内容:
- 自定义代码解释器示例(JavaScript):同主题的 PUBLIC 网络版对照实现;
- 自定义代码解释器示例(Python):Python 语言版本的自定义解释器写法;
- 代码解释器编译器源码:底层 CloudFormation 资源生成逻辑;
- Bedrock AgentCore Plugin 文档:
ai配置全部资源类型的完整参考与输出变量说明; - langgraph-basic 示例:理解"免 Dockerfile 自动构建"的 Agent 基础部署方式。
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考