市售的 AI 助手能够很好地回答个别问题,但在另一方面却存在不足:连贯性。如果今天向一个无状态助手询问你的花园情况,它根本不会知道你三周前提到过你的快速流失的高架苗床、你只使用有机肥料,或者你的矮牵牛在高温天气中遇到了困难。每次对话都从零开始,重新解释上下文的责任就落在用户身上。
问题不在于答案的质量,而是助手没有记住你。这篇帖子展示了如何构建一个个人助手,该助手利用 OpenClaw 来积累上下文信息,这是一套开源的代理系统,运行在 Amazon Bedrock AgentCore 的 AgentCore 运行时上。Amazon Bedrock AgentCore 的 AgentCore 记忆功能可以将一次性聊天记录转化为持久的知识。你还将看到如何通过结构化元数据对这些记忆进行标记,以便检索与当前问题相关的信息。
我们的运行示例是 Sprout,一个园艺助手,但其架构是与领域无关的。只需更换角色和技能清单,相同的流程即可用于支持机器人、健身教练或内部帮助台。整个系统都存在于一个 AWS CloudFormation 模板中,通过一条命令即可部署,并且采用按使用量计费的模式,轻度个人使用每月仅需花费几美元。 沿途,我们会分享一些设计指南,你可以将这些指南应用于基于该框架构建的助手系统中。
解决方案概述
AgentCore 是一个用于大规模构建、连接和优化智能体的平台,支持多种框架和模型。下图展示了端到端的请求流程:从入站 Telegram webhook 开始,经过 AgentCore 运行时系统,以及相关的 AWS 服务。
图 1:Telegram webhook 和 Amazon EventBridge 计划均调用相同的 AgentCore 运行时代理,该代理负责协调 OpenClaw 网关、AgentCore 内存以及 Amazon Bedrock。
两个入口点都指向同一个代理。Telegram 消息通过 Amazon API Gateway 和 AWS Lambda 函数中的 webhook 传入,而诸如早晨浇水提醒之类的定时任务则通过 Amazon EventBridge Scheduler 和 cronjob Lambda 函数传入。两者都会调用 AgentCore runtime 上的 InvokeAgentRuntime API,其中一个简单的 server.py 进程负责协调 OpenClaw 网关、AgentCore 内存以及 Amazon Bedrock Converse API。Amazon Simple Storage Service (Amazon S3) 提供工作区存储,AWS Key Management Service (AWS KMS) 处理加密操作,AWS Secrets Manager 保存机器人令牌,而 Amazon CloudWatch 则用于捕获日志和指标。
先决条件
若要使用 Launch Stack 按钮或 scripts/deploy.sh(详见“发展您的版本”部分)部署您自己的版本,您需要以下内容:
- Amazon Bedrock AgentCore 访问权限,包括 AgentCore 运行时间和 AgentCore 内存。
- 为您计划路由到的模型授予模型访问权限:用于文本处理的 Claude Haiku 4.5 和用于视觉处理的 Claude Sonnet 4.5(或您账户中可用的等效产品)。
- Docker 支持
linux/arm64构建方式,且已配置 AWS 命令行界面(AWS CLI)。此设置仅适用于您计划构建并推送自定义镜像的情况。 - Telegram 机器人令牌(来自 BotFather)作为助手的入口。
- 对代理编排概念和 CloudFormation 有基本了解。
架构:一个位于 AgentCore 运行环境中的无服务器代理
每个组件都存在于一个单独的 CloudFormation 模板中,无需使用任何构建工具即可启动。以下章节将介绍承载负担的相关决策。
AgentCore 运行时间:仅对正在使用的计算资源付费
该代理运行在 AgentCore 运行时环境中的容器中,采用消费型定价方式。您需支付其实际使用的计算资源费用,而非按系统运行时间计费;等待 I/O 操作(如模型响应)所花费的时间也不需付费。对于偶尔使用的个人助手而言,这相当于每月约 1–2 美元的基准费用,与每月约 35 美元的 Amazon Elastic Compute Cloud (Amazon EC2) 常驻实例费用相比。这些数字为 2026 年 7 月时轻度个人使用场景的估算值。有关当前定价信息,请参考 AgentCore 定价方案。
运行时强制遵循最低限度的容器约定:在 8080 端口上监听,通过 GET /ping 接口用于健康检查,通过 POST /invocations 接口作为代理入口。我们的容器为 linux/arm64,由官方 OpenClaw 镜像加上 Python 层构建的多阶段架构构成。
OpenClaw 作为代理基材
OpenClaw 提供了代理循环、工具使用以及技能系统。它运行一个包装器(server.py),该包装器使其符合 AgentCore HTTP 协议契约:
- 在容器启动时,
server.py会作为子进程运行openclaw gateway run,并对其进行健康检查。 GET /ping能快速返回健康状态,因此 AgentCore 准备性探测通过。POST /invocations负责实际工作:解析负载数据,获取内存资源,构建上下文环境,将任务转发给网关,并保存结果。有一个注意事项:AgentCore 可以解冻已停止的冻结容器子进程。因此,调用路径不会假设网关处于正常运行状态,而是调用ensure_openclaw_ready()辅助函数,在转发任务前重新检查网关的状态(必要时还会重启网关)。
这种包装模式可以应用于其他场景。任何作为本地进程运行的代理框架,都可以以相同的方式适配到 AgentCore 运行时中,而无需修改框架本身。
两种模型,按任务分类
文本聊天和图像理解的成本与质量之间存在差异,因此助手会将它们分别传输到不同的 Claude 模型上,地址为 Bedrock:
- Claude Haiku 4.5 for text: 适用于日常使用中常见的大量对话场景,速度快且成本低。
- Claude Sonnet 4.5 for vision: 用于诊断照片中的植物这一较少见但更困难的任务,能提供更强大的多模态推理能力。
文本通过 OpenClaw 网关传输,该网关负责传递技能信息与会话状态。图像通过 server.py 直接调用 Bedrock 的大型语言模型(LLM),并将图像字节作为多模态内容块传递。我们特意对图像在网关中的传输进行安排:容器内的 OpenClaw 构建过程会在图像到达 Bedrock 之前丢弃 image_url 内容部分,因此从 server.py 直接调用 Converse API 可以确保模型能够看到实际像素。两条路径都使用相同的系统提示词(人物角色加上记忆信息),因此体验保持一致。
模型 ID 是环境变量 (MODEL_ID、VISION_MODEL_ID),因此可以在每次部署时更换模型,而无需重新构建镜像。
技能作为可复用能力单元
在 community-skills.json 清单中,能力被定义为技能。部署时脚本会将这些技能转化为容器,并在镜像构建之前将其注册到 OpenClaw 配置中。发布本文时,Sprout 已包含天气、提醒和植物备注技能。更换清单后,相同的流程就可以用于其他领域。正是如此,整个系统才成为一种可复用模式,而不仅仅是一个机器人。
Telegram 作为无服务器前端入口
Telegram 是一个实用的个人助手渠道,因为它基于 Webhook 机制,且所有功能均无需服务器支持。它不需要客户端开发,可以在用户拥有的任何设备上运行,并通过简单的机器人 API 支持文本、图片和复杂的格式设置。BotFather 会生成一个机器人令牌,该令牌存储在 Secrets Manager 中。部署后,会注册一个 Webhook,将 Telegram 连接到 API 网关端点。当用户发送消息时,Telegram 会将消息传递给 Webhook Lambda 函数以验证内容,然后调用 InvokeAgentRuntime。回复则通过 Telegram 机器人 API 返回。
注意一个格式设置要点:Telegram 的旧版 markdown 模型对未转义字符非常严格,模型中出现一个孤立的下划线就可能导致整个消息无法发送。将回复渲染为 HTML 较为可靠,因此助手在发送前会将模型输出转换为适合 Telegram 使用的 HTML。
记忆:将一次性聊天记录转化为持久的知识
到目前为止所描述的架构是一种功能强大且成本低廉的无服务器代理,但单独使用时它仍然会在对话之间忘记用户。只有内存才能改变这种状况。想象一下,你几周前提到自己采用有机方式种植植物,而今天助手却主动推荐一种处理方案,并说明选择有机方案是因为你没有使用合成肥料。无状态模型无法做到这一点。
心理模型:短期事件,长期收益
AgentCore 内存包含两层。短期记忆将每一段对话内容作为事件通过 CreateEvent 存储,以 actorId(Telegram 聊天 ID)和 sessionId 为标识。这就是原始记录。长期记忆则通过管理的提取策略异步生成,并转化为持久化的结构化记录。我们配置了三种策略:
USER_PREFERENCE: 园丁明确说明的偏好(“我只使用有机肥料”)。SEMANTIC: 推断出的事实(“在 Corten 钢育秧床中种植墨西哥矮牵牛”)SUMMARIZATION: 分期会议总结(“讨论了热浪期间下部叶片变黄的问题”)
命名空间:每个园丁一个花园
Sprout 文件被记录到每个用户的命名空间中,因此没有两个聊天记录会混合在一起:
sprout/{chat_id}/long_term: 偏好设置与语义事实。sprout/{chat_id}/episodic/{session_id}: 会话摘要。
聊天 ID 是唯一的变量段,这使得隔离易于理解和测试:每个独特的园丁对应一个唯一的命名空间,且没有两个园丁产生冲突。
检索、组装和注入流程
在每一轮中,代理都会获取相关的长期记录,对它们进行排序,然后将其插入到系统提示中。以下是 server.py 文件中每一条消息发生的具体情况:
- 检索。 调用
RetrieveMemoryRecords方法,针对sprout/{chat_id}/long_term,以用户消息作为搜索查询,最多返回 50 条结果,耗时不超过 3 秒。如果检索超时或出现错误,系统将优雅降级,使用内存而非失败来响应。
try:
records = memory_client.retrieve_memory_records(
memoryId=MEMORY_ID,
namespace=f'sprout/{chat_id}/long_term',
searchCriteria={
'searchQuery': user_message,
'topK': 50,
'metadataFilters': []
},
) # 3s timeout
except Exception:
records = [] # fall back to answering without memory
片段 1:获取当前回合的长期记录(示例。完整源代码请查看仓库。
Assemble 函数添加了额外的自定义逻辑。我们希望显式的偏好优先于推断出的事实,每个类别内的顺序都是稳定的,并且结果在注入之前会被限制在一定范围内:
def assemble(records, cap=50):
explicit = [r for r in records if r.type == 'USER_PREFERENCE']
inferred = [r for r in records if r.type != 'USER_PREFERENCE']
# explicit beats inferred; stable order within each class
ordered = explicit + inferred
return ordered[:cap]
片段 2:组装步骤优先处理明确的偏好,而非推断出的事实。
元数据:在命名空间内对记忆进行分组
命名空间指明了记录所涉及的领域,而元数据则说明了其内容。在 sprout/{chat_id}/long_term 中,针对“我的菊苣花正在枯萎”这一查询,语义搜索会返回所有含义相近的内容。对于园丁来说,这意味着从三月开始的肥料选择建议以及无花果树修剪说明,都会与真正重要的记录一同被排序。而结构化的元数据则有助于在信息到达提示之前缩小记忆的范围。
这里有一条规则主导着所有决策。只有将元数据键声明为索引键时,才能在服务器端进行过滤。您可以在Amazon Bedrock AgentCore Memory中的结构化内存过滤技术中了解更多内容。在这种情况下,sprout使用了三个索引键:
IndexedKeys: # on the AWS::BedrockAgentCore::Memory resource
- Key: type # seperate the kinds of records
Type: STRING
- Key: section # which bed or area it describes
Type: STRING
- Key: plants # what is growing there
Type: STRINGLIST
每个条目都指定了一个键,该键必须与索引中的键一致才能被过滤,并将 extractionType 设置为从事件中传递来的 STRICTLY_CONSISTENT,或从对话中提取的 LLM_INFERRED。对于推断出的键,提取配置可以限制值为固定的列表。Sprout 正是如此做的,因此两种写入路径都会生成相同的词汇表,而过滤功能则意味着同样的意思,无论记录是由哪部分生成的。
保留转动并关闭循环
模型响应后,server.py 会调用 CreateEvent 方法,并传入用户发言和助手发言内容。这个新事件会被用于提取策略,从而丰富长期存储数据,为下次使用做准备。
memory.create_event(
memoryId=MEMORY_ID,
actorId=chat_id,
sessionId=session_id,
payload=[
{'role': 'user', 'content': user_message},
{'role': 'assistant', 'content': reply},
],
) # feeds USER_PREFERENCE / SEMANTIC / SUMMARIZATION extraction; errors are logged, never fatal
片段 3:保留轮次信息,以便提取策略能够异步地丰富长期记忆。
提取是异步进行的,因此本次会议中提到的信息通常在后续会议中才能被检索到。设计时就考虑到了这种延迟:短期会议事件涵盖当前对话内容,而长期记录则涵盖之前的所有内容。
整合起来:个性化的浇水计划
这是整个流程完整运行的环节。通过几次对话,你可以用简单的语言记录下花园里的每一株植物。每次提及都被视为一个事件。提取策略会将关于植物的信息、其位置以及阳光照射情况提取到 sprout/{chat_id}/long_term 中。今天早上,用户提问:“您还记得我花园里的其他植物吗?”系统会调取相关记录,对它们进行排序,然后将其输入到系统提示中。助手则根据用户的地理位置、阳光照射情况、花盆结构、土壤特性以及植物数量等信息来回答问题,而这些信息在消息本身中并未出现。
图 2:Sprout 通过回忆存储的植物库存和生长条件来回答关于花园的问题
使用 Amazon EventBridge → Cron 路径上的调度器功能,Sprout 还可以将计划转化为主动提醒(“跳过这些植物,因为土壤昨天还很潮湿”),并在下雨或热浪来临时根据天气情况对提醒进行调整。
记忆和视觉也相互结合。当用户发送一株枯萎植物的照片时,该图片会被发送到 Claude Sonnet 4.5,同时系统提示信息仍保留着记忆层所掌握的所有信息。助手会将照片与用户已保存的墨西哥菊花进行匹配,并在具体语境中诊断出枯萎的压力原因,而非对那张无名的植物照片进行冷冰冰的分析。
图 3:视觉与记忆协同工作。照片用于视觉模型,而系统提示则包含用户保存的花园相关上下文信息
视觉模型并非绝对准确。在之前没有库存背景的对话中,同样的植物被自信地识别为晚春花,这是一种具有类似喇叭形紫色花朵的物种。将视觉模型与用户自身存储的库存信息结合,才使得看似合理的猜测变成正确的个性化诊断结果。这很好地说明了为什么记忆能够提高准确性,而不仅仅是提升语气。
通过提示词缓存降低推理成本
在每一轮对话中注入记忆内容会使提示词变得很长,而简单的实现方式会在每次请求时都消耗这些令牌。Amazon Bedrock 的提示词缓存机制可以解决这一问题。该助手对提示词进行结构化处理,使得稳定的前缀、角色信息以及组装好的记忆块位于前面,而易变的用户消息则放在最后。Bedrock 会在多次请求中缓存处理后的前缀,因此对话中的重复轮次可以跳过对不变部分的重新计算。 对于支持的模型,提示缓存可以将成本降低至 90% 以下,延迟降低至 85% 以下。
排序规则比任何单一设置都更重要:将稳定的内容放在前面,不稳定的内容放在最后,并确保内存块的内部排序是确定的(前面的汇编函数有助于实现这一点),这样前缀就能在请求之间正确匹配。
在 AgentCore 和 OpenClaw 上构建的设计指南
Sprout 是一个助手,但其背后的决策具有通用性。如果你正在基于这个框架构建自己的助手,以下指南适用于任何领域。
- 包裹它,不要拆分。 通过薄的 HTTP 包装器将你的代理框架适配到 AgentCore 容器契约中,而非直接修改框架本身。该契约规模较小,端口为 8080,包含
/ping和/invocations接口,而包装器则能让你继续享受框架的升级功能。 - 在存储任何内容之前先设计命名空间。 内存命名空间就是你的隔离边界。让用户 ID 成为唯一的变量部分,并从你信任的渠道原生 ID 中选择,比如聊天 ID。多租户设计最终会面临审计和删除请求。清晰的命名空间方案就能让这一切变得简单。
- 将记忆视为一种提升,而非依赖。 每个记忆操作都应能够优雅地失败。检索失败时应给出无记忆反馈,而不阻塞回复过程。用户更愿意原谅一次疏忽的失误,而不是一次失败的失误。
- 按任务分类路由模型。 对于高流量文本,使用高效且成本合理的模型;对于需要多模态功能的场景,则保留更强大的模型。将模型 ID 存储在环境变量中,这样路由调整只需进行配置,无需修改代码。
- 缓存的排序提示。 首先确保用户身份和记忆的稳定性,最后处理易变的用户输入,整个过程中保持确定的排序方式。这种结构习惯正是节省推理时间的关键所在。
- 提取延迟计划。 长期记忆是异步提取的,因此不要承诺在同一会话中能回忆起新信息。让短期会话事件涵盖当前对话,而长期记录则涵盖之前的对话内容。
- 从第一天起就制定预算。 基于消费模式的代理成本较低,直到出现重试循环或聊天式用户行为导致情况改变为止。当 AWS Budgets 在每月上限的 80% 和 100% 时发出警报,无需花费任何费用,并能提前发现问题。
- 将技能保持为简单且单一用途。 一项技能应能够完成用户一句话就能描述的任务,例如查看天气或设置提醒。简单的技能可以独立测试,可以独立替换,也便于模型正确选择。而全能型技能则会让模型不得不猜测用户实际想要的行为是什么。
自己生长
两种种植方式,同一块花园:
- 单步部署堆栈: CloudFormation 模板指向一个公共的 Amazon Elastic Container Registry(Amazon ECR)镜像,因此仅部署了 Telegram 机器人令牌。
- 构建您自己的版本:脚本
deploy.sh会验证模板,构建并推送您的自定义 ARM64 镜像到私有的 Amazon ECR 仓库中,部署整个栈,并注册 Telegram 网页hook,从而实现完全可自定义的构建过程。
截至 2026 年 7 月,日常个人使用费用约为每月 5–9 美元(其中约 2 美元用于基础设施,1–3 美元用于 Haiku 文本,2 美元用于 Sonnet 创作)。系统内置 AWS 预算功能,会在你设定的上限达到 80% 和 100% 时发出提醒。
完整的源代码可在 sample-agentcore-memory-openclaw GitHub 仓库中找到。
清理
实验完成后,请拆除所有内容以避免产生持续费用。由于整个系统是一个 CloudFormation 堆栈,清理操作只需一次删除即可:
- 删除 CloudFormation 堆栈。这将移除 AgentCore 运行时代理、API Gateway、Lambda 函数、Amazon EventBridge 计划,以及相关的 AWS Identity and Access Management (IAM) 角色。
- 删除 AgentCore 内存存储(及其命名空间),这样就不会保留任何用户记录。
- 删除您提交到私有 ECR 仓库中的所有图片,如果仓库不再需要,也请删除该仓库本身。
- 如果您在栈之外创建了 AWS 预算警报,则移除该警报。
- 如果您不再需要,请取消 Telegram 的 webhook 功能(或通过 BotFather 删除机器人),并取消对 Bedrock 模型的访问权限。
结论
该解决方案的可复用核心在于 Amazon Bedrock AgentCore 上的服务器无托管代理,该代理具备技能系统和管理式内存功能。AgentCore 内存无需构建自定义向量存储和提取管道,同时让你完全控制代理记住与遗忘的内容。基于消耗计算的运算以及提示词缓存功能,使得每月只需几美元即可获得真正个性化的助手服务,而 OpenClaw 技能清单则使整个模式能够在不同领域间灵活应用。 个性化效果也会增强:用户互动越多,助手就越有用。
若要进一步发展,可从单一领域如浇水提醒开始,逐步扩大记忆范围;探索情节性记忆,使智能体能够引用过去的特定对话(“上次我们讨论无花果树时,你决定暂时不施肥”);或克隆代码库,加入自己的角色和技能,从而发展成所需的助手。
如需了解更多信息,请参考 AgentCore 文档。以下相关文章更深入地介绍了各个核心组成部分。
- Amazon Bedrock AgentCore 内存:构建具备上下文感知的智能代理
- 构建更智能的 AI 智能体:AgentCore 长期记忆深度解析
- 在 Amazon Bedrock 上有效使用提示缓存
- 在 Amazon Bedrock AgentCore 运行时上安全启动并扩展您的代理和工具
