Hindsight 为 Pydantic AI Agent 提供类型安全、异步原生的长期记忆:retain / recall / reflect 接入实战
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
本篇技术指南围绕 Hindsight 的 Pydantic AI 官方集成展开:如何在保持 Pydantic AI 异步原生与强类型契约的前提下,通过create_hindsight_tools(...)与memory_instructions(...)为 Agent 接入 retain(存储)、recall(检索)、reflect(综合)三把长期记忆工具,并深入剖析其源码实现、配置优先级与常见陷阱,帮助读者在生产环境中构建可预测、可复现的记忆流程。
为什么异步原生的记忆对 Pydantic AI 至关重要
Pydantic AI 的 Agent 运行在asyncio事件循环之上:agent.run(...)是可等待的、工具调用是被await的、模型调用是非阻塞的。其核心价值在于——单个 Agent 可以并发扇出多个任务,同时不阻塞事件循环。
记忆能力应当参与这套并发模型,而不是与之对抗。Hindsight 的 Pydantic AI 集成直接使用 Pydantic AI 的异步工具接口,因此hindsight_retain、hindsight_recall、hindsight_reflect与 Agent 中的其他任何工具一样被await。一次运行中的 recall 不会阻塞事件循环:当记忆调用在途时,其他被 await 的工作可以继续推进。
相反的选择——在异步 Agent 内部调用同步记忆客户端——会在每次记忆操作的持续时间内阻塞整个事件循环。在一个并发处理多个请求的服务中,一次 recall 就可能拖住无关的工作。异步原生的记忆从根本上绕开了这一问题。
拒绝线程池包装(thread-pool hacks)
当某个记忆库只提供同步客户端时,异步 Agent 中常见的变通方案是把每次调用丢到线程池执行器(executor)上。这种方式能跑,但代价明显:
- 延迟与开销:每一次 retain 或 recall 都要跳到工作线程上,产生线程切换与上下文开销;
- 错误不透明:异常跨越 executor 边界后,会丢失自然的异步堆栈上下文,排查困难;
- 推理困难:调用流程不再是一条被 await 的直线,心智模型被打破。
Hindsight 的 Pydantic AI 工具端到端可等待,以上问题都不存在。从实现上看,tools.py 中三个工具闭包(hindsight_retain、hindsight_recall、hindsight_reflect)全部定义为async def,内部依次调用客户端 hindsight_client.py 中的aretain(第 1053 行)、arecall(第 1115 行)、areflect(第 1249 行)三个异步方法。工具直接 await 在 Agent 所在的事件循环上,心智模型保持简单:Agent 等待它的工具,而记忆只是又一个被等待的工具。
记忆工具挂载到 Pydantic AI Agent 的两个位置
有两条清晰的挂载路径,分别对应两种不同的设计意图。
工具(Tools)——将create_hindsight_tools(...)传入 Agent 的tools=[...]。这赋予 Agent 三个明确的、可等待的动作,由模型自主决定何时调用:存储事实(hindsight_retain)、搜索记忆(hindsight_recall)、基于记忆综合答案(hindsight_reflect)。是否触及记忆由模型判断。
指令(Instructions)——将memory_instructions(...)传入 Agent 的instructions=[...]。它在运行开始之前就完成记忆召回并将其注入系统提示词,因此上下文天然在场,Agent 无需先决定去取。
两者的完整组合示例如下(摘自 集成 README 与 集成文档):
from hindsight_client import Hindsight from hindsight_pydantic_ai import create_hindsight_tools, memory_instructions from pydantic_ai import Agent client = Hindsight(base_url="https://api.hindsight.vectorize.io", api_key="hsk_...") agent = Agent( "openai:gpt-4o", tools=create_hindsight_tools(client=client, bank_id="user-123"), instructions=[memory_instructions(client=client, bank_id="user-123")], ) result = await agent.run("What do you remember about my preferences?") print(result.output)三种组合策略:
- 工具 + 指令都用:既想要自动上下文注入,又想要显式记忆动作;
- 只用工具:希望 Agent 自行决定何时使用记忆;
- 只用指令:只想自动注入召回上下文,不把记忆工具暴露给模型。
如果只需要其中部分工具,include_retain、include_recall、include_reflect三个参数可以精确控制挂载哪些工具。这一点有测试直接验证:tests/test_tools.py 中的test_include_retain_only、test_include_recall_only、test_include_reflect_only分别断言只挂载对应单个工具,而test_no_tools_when_all_excluded验证三个开关全关时返回空列表。
连接 Pydantic AI 与 Hindsight
安装与连接细节由标准的 Pydantic AI 记忆接入指南 完整覆盖,核心三步:
- 安装依赖:
pip install hindsight-pydantic-ai(依赖仅包含pydantic-ai-slim>=1.0.0与hindsight-client>=0.4.0,见 pyproject.toml,不拖入全部模型提供方,保持轻量); - 将客户端指向 Hindsight Cloud 或本地自托管服务器;
- 将工具接入你的 Agent。
本指南聚焦设计层面:为什么异步原生记忆契合 Pydantic AI、记忆工具挂载在哪里、如何让整个流程在生产环境中保持类型安全与可预测。安装一次之后,再回到这里看设计细节。
保持记忆流程类型安全且可预测
可预测性来自几处关于配置与作用域的刻意设计选择。
选定一种 bank 策略并保持稳定。recall 与 retain 都以bank_id为键。个人助手场景对每个用户使用稳定 ID;一个用户驱动多个无关系统时,按项目划分。若每次请求都轮换bank_id,即使集成本身正确,Agent 也会表现得像无状态一样——这是最常见的隐性错误来源。
配置一次,按需覆盖。你可以把 client 传给每次调用,也可以只调用一次configure(...),此后创建工具时不再传 client。从源码看,config.py 维护一个模块级全局配置_global_config,configure()会解析HINDSIGHT_API_KEY环境变量并落库;而 tools.py 中的_resolve_client()遵循严格的优先级:显式传入的client优先,其次是hindsight_api_url/api_key参数,最后回落到全局配置。每调用级的构造参数(如budget、tags)会覆盖全局配置——所以当某个值看起来没生效时,先检查是不是有 per-call 参数在"赢"。
用 tags 收敛 recall 范围。recall_tags与recall_tags_match(取值any/all/any_strict/all_strict)能限定一次运行可以"看见"哪些记忆,从而让注入的上下文保持相关、跨运行结果可复现。测试test_recall_passes_tags验证了tags与tags_match会原样透传到arecall的调用参数中。
参数参考
create_hindsight_tools()关键参数(默认值以当前仓库源码为准):
| 参数 | 默认值 | 说明 |
|---|---|---|
bank_id | 必填 | Hindsight 记忆库 ID |
client | None | 预配置的 Hindsight 客户端(优先) |
hindsight_api_url | None | API 地址(未传 client 时使用) |
api_key | None | API 密钥(未传 client 时使用) |
budget | "mid" | recall/reflect 预算级别(low/mid/high) |
max_tokens | 4096 | recall 结果的最大 token 数 |
tags | None | retain 存储记忆时附加的标签 |
recall_tags | None | 检索记忆时过滤的标签 |
recall_tags_match | "any" | 标签匹配模式 |
include_retain/include_recall/include_reflect | True | 是否挂载对应工具 |
memory_instructions()关键参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
query | "relevant context about the user" | 注入前的召回查询词 |
budget | "low" | 召回预算级别(默认比工具更省,保持注入轻快) |
max_results | 5 | 最多注入的记忆条数 |
max_tokens | 4096 | recall 结果最大 token 数 |
prefix | "Relevant memories:\n" | 记忆列表前的前缀文本 |
tags/tags_match | None/"any" | 召回结果的标签过滤 |
一个值得注意的实现细节:memory_instructions(...)返回的是一个async可调用对象,Pydantic AI 会在每次运行时重新求值 instructions,因此即使复用message_history,注入的记忆也能保持新鲜。当召回异常时它会静默返回空字符串(见 tools.py 第 243-245 行),确保记忆失败不会阻断 Agent 主流程——这一行为由测试test_error_returns_empty_string与test_empty_results_returns_empty_string双重锁定。
验证记忆是否真的在工作
- 使用稳定的
bank_id运行一次 Agent,让它存储一条偏好或运行规则; - 用同一个
bank_id开启一次全新运行,询问刚才那条信息; - 检查在任何显式工具调用发生之前,答案是否就反映了先前的记忆——如果是,说明
memory_instructions(...)已成功注入召回上下文; - 若没有,检查指令输出,确认 recall 使用了预期的 bank。
如果第二次运行能基于第一次运行的记忆作答,说明整套链路已经打通。需要更低层行为时,可进一步阅读客户端源码 hindsight_client.py 中aretain/arecall/areflect的完整实现。
常见错误
仍然把客户端包装进线程池
工具本身已经可等待。直接 await 才是正确做法——推进 executor 只增加开销、隐藏错误,毫无收益。
工具和指令用了不同的 bank ID
这样 recall 读取的 bank 与 retain 写入的 bank 不一致,注入的上下文看起来总是空的。请确保两处的bank_id完全一致。
指望只挂工具就能自动注入
工具只给 Agent选择调用记忆的选项。若希望在运行开始前上下文就已就位、无需工具调用,请额外添加memory_instructions(...)。
忘记 per-call 覆盖优先级更高
per-call 的budget、tags或max_tokens会覆盖全局configure(...)中的值。某个设置看起来被忽略时,优先检查是否有构造参数在覆盖它。
FAQ
为什么异步原生对 Pydantic AI 尤其重要?因为 Agent 运行在事件循环上。可等待的记忆工具让记忆停留在这个循环里,一次 recall 不会阻塞无关的并发工作,流程始终是一串干净的 awaited 调用。
工具和 memory instructions 必须二选一吗?不需要。两者可以共存——既要自动注入、又要显式记忆动作时一起用;否则按 Agent 设计选择其一即可。
必须使用 Hindsight Cloud 吗?不是。自托管 Hindsight 服务器同样可行——把客户端指向本地服务即可,例如Hindsight(base_url="http://localhost:8888")(本地开发可参考 scripts/dev 下的启动脚本)。
能限制 Agent 获得的记忆工具吗?可以。在create_hindsight_tools(...)上通过include_retain、include_recall、include_reflect只挂载需要的工具。
进一步阅读
- 完整的安装与接线步骤:Pydantic AI 持久记忆接入指南
- 集成能力总览与完整参数表:Pydantic AI 集成文档
- 集成源码与示例:hindsight_pydantic_ai 包
- 底层异步客户端实现:
aretain/arecall/areflect位于 hindsight_client.py - 集成测试用例:tests/test_tools.py 与 tests/test_config.py
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考