news 2026/9/14 23:33:18

Hindsight × Strands Agents SDK 集成指南:为 Strands Agent 赋予跨会话的持久记忆

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hindsight × Strands Agents SDK 集成指南:为 Strands Agent 赋予跨会话的持久记忆

Hindsight × Strands Agents SDK 集成指南:为 Strands Agent 赋予跨会话的持久记忆

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

本技术指南围绕 Hindsight 开源仓库中的hindsight-strands集成包展开,讲解如何通过 Strands Agents SDK 的原生@tool模式,为 Agent 接入 Hindsight 的长期记忆能力(retain 存储、recall 检索、reflect 综合)。读完本文,你将掌握该集成包的安装接入、三大记忆工具的调用机制、全局配置方式,以及客户端生命周期管理的最佳实践,并能在本地或云端 Hindsight 服务上直接落地一套具备长期记忆的 Strands Agent。

集成概述:为什么需要 hindsight-strands

Strands Agents SDK 提供了一套简洁的 Python Agent 开发范式,Agent 的能力通过原生@tool装饰的普通 Python 函数声明。但 Strands 本身不提供长期记忆:每个会话结束后,Agent 的上下文便随会话丢失。hindsight-strands的作用正是把 Hindsight 的持久化记忆能力封装成 Strands 兼容的工具函数,让 Agent 在会话之间"记住"事实、偏好与决策。

从仓库结构看,该集成是 Hindsight 众多 Agent 框架集成中的一个独立可安装包,位于 hindsight-integrations/strands,核心代码仅由三个模块构成:

  • hindsight_strands/tools.py —— 工具工厂create_hindsight_tools()与记忆注入函数memory_instructions()
  • hindsight_strands/config.py —— 全局配置configure()与配置数据类HindsightStrandsConfig
  • hindsight_strands/errors.py —— 统一异常类型HindsightError

根据 pyproject.toml 中的声明,包版本为 0.1.3,要求 Python >= 3.10,依赖strands-agentshindsight-client>=0.4.0,并需要一台运行中的 Hindsight API 服务。

安装与快速开始

安装只需一条命令:

pip install hindsight-strands

快速开始的最小示例(完整代码见 README.md):

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 会获得三个可调用工具:

工具名职责底层 API
hindsight_retain把信息写入长期记忆client.retain()
hindsight_recall检索长期记忆中相关事实client.recall()
hindsight_reflect基于记忆综合出有依据的回答client.reflect()

本地自托管接入

如果在本机通过./scripts/dev/start-api.sh启动 Hindsight 本地服务,只需把地址指向本地端口即可:

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

三大记忆工具的源码级实现

三个工具都在 tools.py 中由工厂函数按开关参数动态生成,每个工具都是被 Strands@tool装饰的普通 Python 函数,bank_id与 Hindsight 客户端通过闭包在构造时捕获,无需修改 Agent 上下文。

hindsight_retain:存储记忆

hindsight_retain(content: str)接收一段文本,调用client.retain(bank_id=..., content=...)写入记忆库;若通过tags配置了标签,则一并传入tags参数。调用前会执行_ensure_bank(),确保记忆银行(bank)存在:

def _ensure_bank(bid: str) -> None: if bid in created_banks: return try: resolved_client.create_bank(bank_id=bid, name=bid) created_banks.add(bid) except Exception: created_banks.add(bid)

注意created_banks集合保证了同一进程内只创建一次银行,而异常被吞掉则是为了兼容"银行已存在"的场景——这正是仓库测试 test_tools.py 中test_retain_bank_already_exists所验证的行为。成功时工具返回字符串"Memory stored successfully."

hindsight_recall:检索记忆

hindsight_recall(query: str)调用client.recall(),并透传budget(预算档位)与max_tokens(结果 token 上限)两个参数;配置了recall_tags时还会附带tagstags_match。返回结果会被格式化为编号列表:

lines = [] for i, result in enumerate(response.results, 1): lines.append(f"{i}. {result.text}") return "\n".join(lines)

无结果时返回固定文案"No relevant memories found."(测试test_recall_no_results/test_recall_none_results覆盖了空列表与 None 两种边界)。

hindsight_reflect:综合记忆

hindsight_reflect(query: str)调用client.reflect(),返回response.text;若综合结果为空或为 None,则回退到"No relevant memories found."。与 recall 不同,reflect 返回的是模型基于记忆"想清楚之后"的连贯回答,而非原始事实列表。

错误处理契约

三个工具共享一致的错误处理模式(见源码):

  • 捕获HindsightError时直接原样抛出(不透传包装);
  • 捕获其他Exception时记录 error 日志并包装为HindsightError(f"Retain failed: {e}")抛出。

对应的单元测试(如test_retain_hindsight_error_not_wrappedtest_retain_failure_logs_error)验证了这两种行为,保证 Agent 侧可以通过统一的HindsightError感知记忆操作失败。

线程桥接:Strands 事件循环与 Hindsight 客户端的兼容关键

hindsight-strands在实现上有一个值得注意的工程细节:Strands 会在自身的 asyncio 事件循环中执行工具,而 Hindsight 客户端内部同样使用 asyncio(包括asyncio.timeout),直接调用会与已运行的事件循环冲突。因此 tools.py 定义了模块级线程池与桥接函数:

_executor = concurrent.futures.ThreadPoolExecutor(max_workers=4) def _run_in_thread(fn: Any, *args: Any, **kwargs: Any) -> Any: return _executor.submit(fn, *args, **kwargs).result()

所有对客户端的同步调用(retain/recall/reflect/create_bank/close)都经由_run_in_thread在独立线程中执行,从而获得干净的事件循环。这解释了为什么工具函数对外表现为纯同步接口——你可以把它理解为 Strands 工具生态与 asyncio 客户端之间的"线程桥"。

配置参考:三个入口的参数全解析

集成提供三个配置入口,均以*强制关键字参数,具体参数与默认值如下(与 README.md 的 Configuration Reference 保持一致,并结合 tools.py 与 config.py 源码核实):

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"标签匹配模式:any / all / any_strict / all_strict
enable_retainTrue是否包含 retain 工具
enable_recallTrue是否包含 recall 工具
enable_reflectTrue是否包含 reflect 工具

只挂载需要的工具可以减小工具面,例如enable_reflect=False即可省略综合工具(README 中给出完整示例)。测试test_creates_three_tools_by_default与三个 enable 单开测试共同验证了工具按开关组合生成的行为。

memory_instructions()

参数默认值说明
bank_id必填Hindsight 记忆银行 ID
clientNone预配置的 Hindsight 客户端
hindsight_api_urlNoneAPI 地址(未传 client 时使用)
api_keyNoneAPI 密钥
query"relevant context about the user"用于记忆注入的 recall 查询词
budget"low"recall 预算档位(预注入场景默认更低,控制成本)
max_results5最多注入多少条记忆
max_tokens4096recall 结果的 token 上限
prefix"Relevant memories:\n"记忆列表前的前缀文本
tagsNone过滤 recall 结果的标签
tags_match"any"标签匹配模式

memory_instructions()返回格式化字符串,可直接拼入 system prompt,让 Agent 在对话前就"预知"相关记忆。其实现细节(tools.py)值得注意:

  • 无结果时返回空字符串""
  • 任何异常都会被静默吞掉并返回""——注释明确说明"instructions 失败不应阻塞 Agent";
  • 若客户端是内部创建的,无论成功失败都会在finally中关闭,避免泄漏(测试test_closes_internally_created_client_on_success/test_closes_internally_created_client_on_exception验证)。

configure()

参数默认值说明
hindsight_api_url生产 API(https://api.hindsight.vectorize.ioHindsight API 地址
api_keyHINDSIGHT_API_KEY环境变量API 密钥
budget"mid"默认 recall 预算
max_tokens4096默认 recall token 上限
tagsNone默认 retain 标签
recall_tagsNone默认 recall 过滤标签
recall_tags_match"any"默认标签匹配模式
verboseFalse是否启用详细日志

configure()的实现(config.py)有两条优先级规则,被 test_config.py 的多个用例锁定:

  1. 显式参数 > 环境变量api_key = api_key or os.environ.get("HINDSIGHT_API_KEY"),因此传入api_key="explicit-key"会覆盖环境变量;
  2. 配置可被替换:每次调用configure()都会生成新的HindsightStrandsConfig实例(test_configure_replaces_previous_config),可通过get_config()读取、reset_config()复位。

配置完成后,后续创建工具无需再传连接信息:

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", # recall 预算:low/mid/high max_tokens=4096, # recall 结果最大 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")

在 tools.py 中,显式参数与全局配置的合并遵循"显式优先"原则:例如effective_tags = tags if tags is not None else (config.tags if config else None)。测试test_retain_explicit_tags_override_configtest_retain_config_tags分别验证了显式覆盖与配置兜底两条路径。

客户端生命周期管理:v0.1.3 修复的核心问题

create_hindsight_tools()返回的不是普通 list,而是 tools.py 中定义的HindsightTools容器——它继承list,因此可以直接传给Agent(tools=tools),同时额外携带客户端清理能力。这与 Strands 集成变更日志(strands.md)中v0.1.3 的 Bug Fix 直接对应:该版本"修复了 Strands 集成正确关闭内部持有的 Hindsight 客户端,避免资源泄漏与相关稳定性问题"。

理解这个修复需要掌握_resolve_client()的所有权判定逻辑:

def _resolve_client(client, hindsight_api_url, api_key): if client is not None: return client, False # 外部客户端:owns_client = False ... return Hindsight(**kwargs), True # 内部创建:owns_client = True
  • 传入client=:所有权归调用方,close()/aclose()是空操作(测试test_close_does_not_close_externally_owned_client);
  • 仅传hindsight_api_url/api_key:集成内部创建客户端并持有所有权,close()/aclose()会真正关闭它(测试test_close_closes_internally_owned_client/test_aclose_closes_internally_owned_client)。

HindsightTools还实现了上下文管理器协议,支持withasync with用法;close()通过_run_in_thread在独立线程中同步关闭,aclose()则直接await self._client.aclose()

推荐做法:FastAPI 生命周期中共享客户端

README 推荐在应用 lifespan 中创建一个共享Hindsight 客户端并显式管理其生命周期:

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) ...

这种模式下,工具容器不再拥有客户端,关闭动作统一由 lifespan 的await client.aclose()完成;而如果直接向create_hindsight_tools()hindsight_api_url/api_key,则必须在关闭阶段调用await tools.aclose()(或tools.close())——这正是 v0.1.3 修复所保障的行为。

类型支持与可观测性:v0.1.2 的两个改进

Strands 集成变更日志记录了v0.1.2的两个 Improvements,同样在源码中有迹可循:

  1. PEP 561 类型标记:包内携带 py.typed 标记文件(并在 pyproject.toml 的 wheel 打包配置packages = ["hindsight_strands"]中被一并发布),静态类型检查器据此对create_hindsight_toolsmemory_instructionsconfigure等 API 提供完整类型推断。

  2. 一致的 User-Agent:模块顶部通过importlib.metadata读取包版本并构造标识头:

try: _VERSION = metadata.version("hindsight-strands") except metadata.PackageNotFoundError: _VERSION = "0.0.0" _USER_AGENT = f"hindsight-strands/{_VERSION}"

所有内部创建的 Hindsight 客户端都会携带user_agent=_USER_AGENT(测试test_creates_client_from_url等断言了该参数),并附带 30 秒默认超时,便于服务端识别流量来源与排查问题。

版本演进时间线

结合 strands.md 变更日志,hindsight-strands的演进脉络如下:

  • v0.1.1(Features):新增 Strands Agents SDK 集成,使 Hindsight 记忆工具可用于 Strands Agent——即本包的核心能力首次落地;
  • v0.1.2(Improvements):发布 PEP 561py.typed标记以改进 Python 类型支持;所有 HTTP 请求携带统一 User-Agent,提升兼容性与排障体验;
  • v0.1.3(Bug Fixes):修复内部持有的 Hindsight 客户端关闭逻辑,杜绝资源泄漏与相关稳定性问题。

该集成与 Hindsight 核心采用独立版本节奏——正如总变更日志 index.md 所述,"每个集成按自己的节奏发布,并有自己的变更日志"。因此升级时应以hindsight-strands自身版本为准,而不是 Hindsight API 的版本号。

测试与验证

仓库为集成配备了完整的单元测试,可作为接入时的行为规范参考:

  • tests/test_config.py —— 覆盖默认值、环境变量读取、显式覆盖优先级、配置替换与复位等 20 余个用例;
  • tests/test_tools.py —— 覆盖客户端解析(_resolve_client)、工具数量与名称、三个工具的输入输出契约、银行自动创建、标签透传、错误包装、内部客户端关闭等行为。

例如,_resolve_client的测试断言了三条关键规则:显式client优先于 url/key;无 client 时回退到全局配置;配置完全缺失时抛出HindsightError(消息为"No Hindsight API URL configured. Pass client= or hindsight_api_url=, or call configure() first.")。这些测试事实均来自 test_tools.py 的TestResolveClient类。

总结

hindsight-strands以极小的 API 面(三个工具 + 一个注入函数 + 一个全局配置函数)为 Strands Agent 补齐了长期记忆能力。其设计要点可概括为:以原生@tool函数贴合 Strands 生态、以线程桥化解 asyncio 冲突、以显式所有权规则杜绝客户端资源泄漏(v0.1.3 的核心修复)、以py.typed与统一 User-Agent 提升工程体验(v0.1.2 的两项改进)。按 README 推荐在应用生命周期中共享客户端,并将memory_instructions()的结果拼入 system prompt,即可在几行代码内构建出真正"记得住"的 Strands Agent。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 23:31:05

红盟自动发卡网H5源码对接亿乐社区:部署与回调验证指南

简介:一套面向站长和开发者的自动发卡网H5源码,基于ThinkPHP框架构建,可直接对接亿乐社区,用于快速搭建支持数字商品售卖、自动发货、订单管理的小型交易平台。安装教程覆盖域名解析、宝塔主机环境配置、运行目录修改、伪静态规则…

作者头像 李华
网站建设 2026/9/14 23:29:26

基于SpringBoot + Vue的集采拼单与订单跟踪系统 毕业设计 -附源码

🍅全部选题源码免费分享、无偿获取,支持软件定制开发;由于篇幅限制,获取完整文章或源码、代做项目的,本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片。🍅 🍅全部选题源码…

作者头像 李华
网站建设 2026/9/14 23:28:44

鸿蒙应用开发中的高效日期时间处理:teno_datetime适配指南

1. 项目概述:为什么需要 teno_datetime 的鸿蒙适配?在鸿蒙应用开发中,日期时间处理是个高频但容易被忽视的痛点。传统方式需要手动处理格式化字符串、时区转换和多语言适配,代码往往冗长且易错。teno_datetime 这个 Flutter 三方库…

作者头像 李华
网站建设 2026/9/14 23:22:18

SpringBoot+Hadoop构建超市智能进货推荐系统实战

1. 项目概述与核心价值超市进货推荐系统是零售行业数字化转型中的关键一环。我去年为本地连锁超市部署的类似系统,帮助客户将库存周转率提升了37%,滞销商品比例下降52%。这个基于SpringBootHadoop的解决方案,本质上是通过大数据分析技术&…

作者头像 李华
网站建设 2026/9/14 23:18:43

区域综合能源系统的主从博弈优化与Matlab实现

1. 项目概述区域综合能源系统(RIES)作为能源互联网的重要载体,正在推动传统能源系统向低碳化、智能化方向转型。这个基于多主体主从博弈的分层优化调度模型,本质上是在解决一个复杂的"能源-经济-环境"三角平衡问题。我在…

作者头像 李华
网站建设 2026/9/14 23:18:34

北极星魔方

目录 北极星魔方 1,魔方三要素 2,复原方法 (1)复原6个中心块和8个角块的位置 (2)调整24个棱块的位置 (3)调整8个大角块的朝向 北极星魔方 1,魔方三要素 &#xf…

作者头像 李华