Hindsight × Strands Agents 集成指南:用 retain / recall / reflect 为 Strands 智能体赋予长期记忆
2026/9/14 8:47:52 网站建设 项目流程

Hindsight × Strands Agents 集成指南:用 retain / recall / reflect 为 Strands 智能体赋予长期记忆

【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight

本指南围绕 Hindsight 官方提供的hindsight-strands集成包展开,讲解如何通过 Strands Agents SDK 原生的@tool模式,为 Strands 智能体接入 Hindsight 的长期记忆能力——hindsight_retain(存储)、hindsight_recall(检索)、hindsight_reflect(综合作答),并覆盖快速接入、记忆注入、客户端生命周期管理、全局配置与底层实现原理。读完本文,你将能够在自己的 Strands Agent 项目中直接落地可持久化的记忆机制,并理解该集成包各版本演进背后的工程考量。

集成定位:让 Strands 智能体"学会记忆"

Strands Agents SDK 是社区中一个以 Python 优先的智能体开发框架,其核心模式是用@tool装饰器把普通 Python 函数包装成可供Agent(tools=[...])直接调用的工具。Hindsight 则是"会学习的 Agent 记忆"(Agent Memory That Learns)系统,提供内存银行(memory bank)机制与 retain / recall / reflect 三大核心 API。

hindsight-strands正是把两者缝合起来的桥梁:它以原生@tool函数的形式暴露 Hindsight 记忆能力,因此无需修改 Strands 的上下文机制,直接通过闭包捕获bank_id与客户端即可工作。在 hindsight-integrations/README.md 的 Agent 框架集成清单中,Strands 与 LangGraph、LlamaIndex、CrewAI 等并列,属于 Hindsight 官方一等公民集成之一。包的基本信息可以从 pyproject.toml 确认:Python >= 3.10,依赖strands-agentshindsight-client>=0.4.0,当前版本为 0.1.3,采用 MIT 许可证。

快速开始:三行代码接入长期记忆

安装

pip install hindsight-strands

最小可用示例

参照 strands 集成 README 的 Quick Start,接入过程只需两个步骤:创建记忆工具、交给 Agent。

from strands import Agent from hindsight_strands import create_hindsight_tools tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="https://api.hindsight.vectorize.io", api_key="hsk_...", # 或通过环境变量 HINDSIGHT_API_KEY 提供 ) agent = Agent(tools=tools) agent("Remember that I prefer dark mode") agent("What are my preferences?") tools.close() # 仅在 hindsight-strands 内部创建了客户端时需要关闭

创建后,Agent 就拥有三个可调用的记忆工具:

工具名作用参数
hindsight_retain将信息写入长期记忆(事实、偏好、决策、跨会话上下文)content
hindsight_recall在长期记忆中检索相关事实,返回编号列表query
hindsight_reflect基于记忆综合生成有推理依据的答案,而非罗列原始事实query

这三个工具的签名与行为可以直接在 tools.py 中看到:hindsight_retain成功时返回"Memory stored successfully."hindsight_recall无结果时返回"No relevant memories found.",有结果时按"1. fact1\n2. fact2\n..."编号输出;hindsight_reflect优先返回综合文本,空文本时回退到"No relevant memories found."

本地自托管开发

如果你通过./scripts/dev/start-api.sh在本地运行 Hindsight 服务,把地址指向本地即可(见 README 的 Self-hosting 小节):

tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="http://localhost:8888", )

本地模式下通常不需要显式传入 API key;生产接入则推荐 Hindsight Cloud 或自建 Hindsight API 服务。

记忆注入:memory_instructions()预召回

除了让 Agent 在对话中按需调用工具,hindsight-strands还提供memory_instructions(),在会话开始前同步执行一次 recall,把命中的记忆拼装成字符串注入系统提示词。这在"每次对话开始时自动携带用户历史上下文"的场景下非常实用:

from hindsight_strands import create_hindsight_tools, memory_instructions tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="https://api.hindsight.vectorize.io", api_key="hsk_...", ) memories = memory_instructions( bank_id="user-123", hindsight_api_url="https://api.hindsight.vectorize.io", api_key="hsk_...", ) agent = Agent( tools=tools, system_prompt=f"You are a helpful assistant.\n\n{memories}", )

从 tools.py 的实现看,memory_instructions()默认输出格式为:

Relevant memories: 1. pref1 2. pref2

值得注意的是它的容错设计:任何异常都会被静默吞掉并返回空字符串(return ""),注释明确说明"instructions failures shouldn't block the agent"——记忆注入失败不应阻塞智能体启动。对应的测试用例tests/test_tools.py中的test_returns_empty_on_exception验证了该行为。

客户端生命周期管理:谁创建,谁关闭

这是本集成最值得注意的工程细节,也是 changelog v0.1.3 修复的核心问题。

推荐的 FastAPI 生命周期模式

README 推荐的模式是在应用 lifespan 中创建一个共享的 Hindsight 客户端,通过client=...显式传入,所有权归应用,关闭时调用await client.aclose()

from contextlib import asynccontextmanager from fastapi import FastAPI from hindsight_client import Hindsight from hindsight_strands import create_hindsight_tools, memory_instructions @asynccontextmanager async def lifespan(app: FastAPI): client = Hindsight(base_url="http://localhost:8888", api_key="test-key") app.state.hindsight_client = client try: yield finally: await client.aclose() app = FastAPI(lifespan=lifespan) @app.post("/chat") async def chat(): client = app.state.hindsight_client tools = create_hindsight_tools(bank_id="user-123", client=client) memories = memory_instructions(bank_id="user-123", client=client) ...

内部创建客户端时的关闭义务

反之,如果直接把hindsight_api_url/api_key传给create_hindsight_tools(),则客户端由集成内部创建并持有,调用方需要在关闭阶段调用await tools.aclose()tools.close()

围绕这一点,源码中设计了一个专门的容器类HindsightTools(list)(见 tools.py):

  • 它是list的子类,兼容Agent(tools=[...])的列表传参方式;
  • 记录owns_client标记区分客户端归属;
  • 提供close()aclose()仅当owns_client=True时才真正关闭内部客户端,外部传入的客户端绝不会被误关;
  • 同时实现上下文管理器协议:with tools:/async with tools:会在退出时自动清理。

这与 changelog v0.1.3 的 Bug Fix 一一对应:该版本修复了"Strands 集成未能正确关闭内部持有的 Hindsight 客户端"的问题,防止了资源泄漏与相关稳定性问题。测试 tests/test_tools.py 中test_close_closes_internally_owned_clienttest_close_does_not_close_externally_owned_client分别验证了两种归属场景。

客户端解析优先级

_resolve_client()的解析逻辑(同样位于 tools.py)遵循明确优先级:

  1. 显式传入的client直接使用(忽略 URL / key),owns_client=False
  2. 否则取hindsight_api_url/api_key参数;
  3. 再回退到全局配置get_config()
  4. 都没有 URL 时抛出HindsightError("No Hindsight API URL configured. ...")

内部创建客户端时固定传入timeout=30.0,并附带统一的user_agent。值得注意的是,memory_instructions()内部创建的客户端在使用完毕后会自动关闭(见finally分支),测试test_closes_internally_created_client_on_success/on_exception验证了成功与异常两条路径都会关闭。

按需选择工具组合

不是每个场景都需要全部三个工具,create_hindsight_tools()提供三个开关(详见 README 的 Selecting Tools 小节):

tools = create_hindsight_tools( bank_id="user-123", hindsight_api_url="http://localhost:8888", enable_retain=True, enable_recall=True, enable_reflect=False, # 省略 reflect )

测试覆盖了所有组合:默认创建 3 个工具、仅 retain、仅 recall、仅 reflect、全部禁用时返回空列表(见 tests/test_tools.py 的TestCreateHindsightTools类)。

全局配置:configure()一次设置,处处生效

如果不想在每次调用时重复传连接信息,可以用configure()设置全局默认值:

from hindsight_strands import configure, create_hindsight_tools configure( hindsight_api_url="http://localhost:8888", api_key="your-api-key", # 或设置 HINDSIGHT_API_KEY 环境变量 budget="mid", # 召回预算:low/mid/high max_tokens=4096, # 召回结果的最大 token 数 tags=["env:prod"], # 存储记忆时附加的标签 recall_tags=["scope:global"], # 召回时用于过滤的标签 recall_tags_match="any", # 标签匹配模式:any/all/any_strict/all_strict ) # 此后无需再传连接信息 tools = create_hindsight_tools(bank_id="user-123")

从 config.py 的实现可以确认几点细节:

  • API key 解析顺序:显式api_key参数 >HINDSIGHT_API_KEY环境变量 >None
  • API URL 缺省值为生产环境https://api.hindsight.vectorize.io
  • 配置以dataclass HindsightStrandsConfig形式保存在模块级全局变量中,可通过get_config()读取、reset_config()重置;
  • 每次调用configure()都会生成新的配置实例并替换旧值(测试test_configure_replaces_previous_config验证了这一点);
  • create_hindsight_tools()内部,参数与全局配置的优先级为:显式参数优先,未传则回退全局配置(如effective_tagseffective_budgeteffective_max_tokens等),测试test_retain_explicit_tags_override_config验证了显式标签覆盖全局配置的行为。

配置参考:三个核心 API 的完整参数表

create_hindsight_tools()

参数默认值说明
bank_id必填Hindsight 记忆银行 ID
clientNone预配置的 Hindsight 客户端(由调用方管理生命周期)
hindsight_api_urlNoneAPI 地址(传入则集成内部创建并持有客户端)
api_keyNoneAPI 密钥(未传 client 时使用)
budget"mid"recall/reflect 预算级别(low/mid/high)
max_tokens4096recall 结果的最大 token 数
tagsNone存储记忆时附加的标签
recall_tagsNone检索时用于过滤的标签
recall_tags_match"any"标签匹配模式
enable_retainTrue是否包含 retain(存储)工具
enable_recallTrue是否包含 recall(检索)工具
enable_reflectTrue是否包含 reflect(综合)工具

memory_instructions()

参数默认值说明
bank_id必填Hindsight 记忆银行 ID
clientNone预配置的 Hindsight 客户端
hindsight_api_urlNoneAPI 地址(未传 client 时使用)
api_keyNoneAPI 密钥(未传 client 时使用)
query"relevant context about the user"记忆注入的召回查询
budget"low"召回预算级别
max_results5最多注入的记忆条数
max_tokens4096召回结果的最大 token 数
prefix"Relevant memories:\n"记忆列表前追加的文本
tagsNone过滤召回结果的标签
tags_match"any"标签匹配模式

configure()

参数默认值说明
hindsight_api_url生产 APIHindsight API 地址
api_keyHINDSIGHT_API_KEY环境变量API 认证密钥
budget"mid"默认召回预算级别
max_tokens4096默认召回最大 token 数
tagsNoneretain 操作的默认标签
recall_tagsNone过滤召回的默认标签
recall_tags_match"any"默认标签匹配模式
verboseFalse是否开启详细日志

底层实现原理(源码级解读)

闭包捕获与@tool原生集成

create_hindsight_tools()返回的每个工具都是闭包函数:bank_id和解析后的客户端在构造时被捕获进闭包,函数体直接用resolved_client.retain(...)resolved_client.recall(...)resolved_client.reflect(...)调用 hindsight-client 中的对应方法。这意味着工具与 Strands 的@tool装饰器完全兼容,无需修改 Agent 的上下文传递机制。

线程池隔离事件循环

一个值得展开的实现细节是_run_in_thread():Strands 在自己的 asyncio 事件循环中执行工具,而 Hindsight 客户端内部也是 asyncio 实现(包括asyncio.timeout),在同一运行中的循环里嵌套会冲突。因此集成维护了一个max_workers=4ThreadPoolExecutor,把同步调用提交到独立线程执行,让每个调用拥有干净的事件循环(tools.py 中对此有明确的注释说明)。

自动创建记忆银行

_ensure_bank()在首次调用 retain 前会尝试client.create_bank(bank_id=bid, name=bid),并且用created_banks集合做去重,保证同一客户端只创建一次;若银行已存在(抛出异常),也会静默标记为已创建,不影响后续写入。对应测试test_retain_creates_banktest_retain_creates_bank_only_once验证了"建库且仅建一次"的行为。

统一错误模型

所有工具的错误都被收敛到自定义的HindsightError(定义于 errors.py)。从源码逻辑看:若底层已抛出HindsightError则原样透传不包装;其余异常则记录logger.error后包装为HindsightError重新抛出。测试test_retain_hindsight_error_not_wrappedtest_retain_failure_raises_hindsight_error分别覆盖了这两种路径。

一致的 User-Agent

从 tools.py 可以看到,模块启动时通过importlib.metadata读取自身版本号(取不到时回退0.0.0),组装成hindsight-strands/{version}形式的_USER_AGENT,并随内部创建的每个客户端请求发送。这正是 changelog v0.1.2 中"所有 HTTP 请求携带一致 User-Agent"改进的实现载体,用于服务端兼容性与问题排查。测试test_creates_client_from_url等均断言了user_agent == _USER_AGENT

PEP 561 类型标注支持

v0.1.2 的另一项改进是为集成包补齐py.typed标记(文件位于 hindsight_strands/py.typed),使类型检查器(如 mypy、pyright)能够读取包内完整的类型标注——这也是hindsight_strands模块内所有函数签名均为显式类型注解(如tools: list[Any]bank_id: str)的原因。

版本演进:从 0.1.1 到 0.1.3

版本演进记录来自 strands 集成 Changelog,其脉络与本集成包的成熟过程一致:

版本类型内容
0.1.1Features新增 Strands Agents SDK 集成,让 Hindsight 记忆工具可用于 Strands 智能体(即本文介绍的全部功能起点)
0.1.2Improvements通过 PEP 561py.typed标记改进 Python 类型支持(commitd054b884
0.1.2Bug Fixes所有 HTTP 请求携带一致的 User-Agent,提升兼容性与可排查性(commit9372462e
0.1.3Bug Fixes修复内部持有的 Hindsight 客户端未能正确关闭的问题,防止资源泄漏与稳定性隐患(commit2bfd7747

可以看到,从 0.1.1 的功能落地到 0.1.3 的生命周期修复,版本演进的每一步都能在源码与测试中找到对应实现,这也为使用方选择版本提供了参考:若你长期运行长生命周期服务,应优先使用包含客户端关闭修复的 0.1.3+。

运行前提与适用限制

依据 pyproject.toml 与 README 的 Requirements 小节,接入前需满足:

  • Python >= 3.10(pyproject 声明支持 3.10 / 3.11 / 3.12);
  • 安装strands-agentshindsight-client>=0.4.0
  • 有一个可访问的 Hindsight API 服务:可以是 Hindsight Cloud(获取 API key),也可以是本地自托管服务(如./scripts/dev/start-api.sh启动的http://localhost:8888)。

另外需要注意:memory_instructions()的默认召回预算为low(区别于工具内 recall 的mid),且结果数量受max_results(默认 5)限制,适合作为系统提示词的轻量上下文;而configure()仅在调用方显式调用后才生效,未调用configure()get_config()返回None,此时必须显式传入clienthindsight_api_url,否则会抛出HindsightError。理解这些边界,能帮助你在 Strands 项目中更准确地组合记忆能力。

【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询