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-agents与hindsight-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_client与test_close_does_not_close_externally_owned_client分别验证了两种归属场景。
客户端解析优先级
_resolve_client()的解析逻辑(同样位于 tools.py)遵循明确优先级:
- 显式传入的
client直接使用(忽略 URL / key),owns_client=False; - 否则取
hindsight_api_url/api_key参数; - 再回退到全局配置
get_config(); - 都没有 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_tags、effective_budget、effective_max_tokens等),测试test_retain_explicit_tags_override_config验证了显式标签覆盖全局配置的行为。
配置参考:三个核心 API 的完整参数表
create_hindsight_tools()
| 参数 | 默认值 | 说明 |
|---|---|---|
bank_id | 必填 | Hindsight 记忆银行 ID |
client | None | 预配置的 Hindsight 客户端(由调用方管理生命周期) |
hindsight_api_url | None | API 地址(传入则集成内部创建并持有客户端) |
api_key | None | API 密钥(未传 client 时使用) |
budget | "mid" | recall/reflect 预算级别(low/mid/high) |
max_tokens | 4096 | recall 结果的最大 token 数 |
tags | None | 存储记忆时附加的标签 |
recall_tags | None | 检索时用于过滤的标签 |
recall_tags_match | "any" | 标签匹配模式 |
enable_retain | True | 是否包含 retain(存储)工具 |
enable_recall | True | 是否包含 recall(检索)工具 |
enable_reflect | True | 是否包含 reflect(综合)工具 |
memory_instructions()
| 参数 | 默认值 | 说明 |
|---|---|---|
bank_id | 必填 | Hindsight 记忆银行 ID |
client | None | 预配置的 Hindsight 客户端 |
hindsight_api_url | None | API 地址(未传 client 时使用) |
api_key | None | API 密钥(未传 client 时使用) |
query | "relevant context about the user" | 记忆注入的召回查询 |
budget | "low" | 召回预算级别 |
max_results | 5 | 最多注入的记忆条数 |
max_tokens | 4096 | 召回结果的最大 token 数 |
prefix | "Relevant memories:\n" | 记忆列表前追加的文本 |
tags | None | 过滤召回结果的标签 |
tags_match | "any" | 标签匹配模式 |
configure()
| 参数 | 默认值 | 说明 |
|---|---|---|
hindsight_api_url | 生产 API | Hindsight API 地址 |
api_key | HINDSIGHT_API_KEY环境变量 | API 认证密钥 |
budget | "mid" | 默认召回预算级别 |
max_tokens | 4096 | 默认召回最大 token 数 |
tags | None | retain 操作的默认标签 |
recall_tags | None | 过滤召回的默认标签 |
recall_tags_match | "any" | 默认标签匹配模式 |
verbose | False | 是否开启详细日志 |
底层实现原理(源码级解读)
闭包捕获与@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=4的ThreadPoolExecutor,把同步调用提交到独立线程执行,让每个调用拥有干净的事件循环(tools.py 中对此有明确的注释说明)。
自动创建记忆银行
_ensure_bank()在首次调用 retain 前会尝试client.create_bank(bank_id=bid, name=bid),并且用created_banks集合做去重,保证同一客户端只创建一次;若银行已存在(抛出异常),也会静默标记为已创建,不影响后续写入。对应测试test_retain_creates_bank与test_retain_creates_bank_only_once验证了"建库且仅建一次"的行为。
统一错误模型
所有工具的错误都被收敛到自定义的HindsightError(定义于 errors.py)。从源码逻辑看:若底层已抛出HindsightError则原样透传不包装;其余异常则记录logger.error后包装为HindsightError重新抛出。测试test_retain_hindsight_error_not_wrapped与test_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.1 | Features | 新增 Strands Agents SDK 集成,让 Hindsight 记忆工具可用于 Strands 智能体(即本文介绍的全部功能起点) |
| 0.1.2 | Improvements | 通过 PEP 561py.typed标记改进 Python 类型支持(commitd054b884) |
| 0.1.2 | Bug Fixes | 所有 HTTP 请求携带一致的 User-Agent,提升兼容性与可排查性(commit9372462e) |
| 0.1.3 | Bug 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-agents与hindsight-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,此时必须显式传入client或hindsight_api_url,否则会抛出HindsightError。理解这些边界,能帮助你在 Strands 项目中更准确地组合记忆能力。
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考