news 2026/9/14 14:15:26

用 Hindsight 记忆构建个性化搜索 Agent:retain / recall / reflect 实战配方

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 Hindsight 记忆构建个性化搜索 Agent:retain / recall / reflect 实战配方

用 Hindsight 记忆构建个性化搜索 Agent:retain / recall / reflect 实战配方

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

本文是 Hindsight Cookbook 系列中personalized_search配方的完整实战指南。它将带你用 Hindsight 的记忆能力(retain/recall/reflect)构建一个"认识你"的搜索助手——它会记住你的位置、饮食限制、生活方式与过往搜索,在每次搜索前自动检索相关偏好、改写查询并生成个性化摘要。读完本文,你将掌握 Hindsight 三大核心 API 的实际用法、本地 Docker 部署方式,以及一个可直接复制运行、还能自由扩展的搜索记忆系统。

一、配方概览:搜索为什么需要"记忆"

通用搜索引擎对所有用户返回相同结果,而本配方要解决的核心问题是:同一个查询,如何为不同用户返回不同答案。答案在于给搜索过程加一层"记忆":

  • 记忆写入(retain):把用户偏好、历史搜索交互写入 Hindsight 的 memory bank;
  • 记忆读取(recall):在搜索前按查询语义召回相关偏好与历史,作为上下文注入 LLM;
  • 记忆综合(reflect):让 Hindsight 基于整库记忆生成用户画像摘要,用于审视系统学到了什么。

本配方实现的"个性化搜索"功能特性如下:

  • 学习位置(location)、饮食限制(dietary restrictions)与生活方式(lifestyle);
  • 基于上下文个性化搜索查询(enhanced query);
  • 记住过往搜索与偏好;
  • 可选集成 Tavily 进行真实网页搜索(未配置时自动降级为模拟结果)。

整个配方以 Jupyter Notebook 形式组织(本仓库 cookbook 目录下同系列配方还包括 per-user-memory.md、personal_assistant.md、quickstart.md 等,可互相参照),你也可以直接把它整理为一个 Python 脚本运行。

二、前置条件

在动手之前,你需要准备:

  • OpenAI API key:同时用于 Hindsight 服务端的 retain/recall/reflect 语义处理与演示代码的查询增强/摘要生成;
  • Hindsight 本地实例:通过 Docker 启动(见下一节);
  • Tavily API key(可选):仅当你希望执行真实网页搜索时使用;不配置则演示使用模拟搜索结果。

依赖库方面,配方只用到hindsight-client(Hindsight 官方 Python 客户端)、openai、可选的tavily-python以及nest-asyncio(让 Jupyter/脚本中能安全运行异步代码)。

三、本地启动 Hindsight(Docker)

在运行 Notebook 之前,先在终端启动 Hindsight 服务:

export OPENAI_API_KEY="your-openai-api-key" docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \ -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \ -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \ -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \ ghcr.io/vectorize-io/hindsight:latest

几个关键点需要说明:

  • 端口约定:容器暴露了两个端口,8888是 Hindsight HTTP API(客户端连接入口),9999是控制平面(Control Plane)Web UI。这一端口约定在整个仓库中保持一致——例如 docker/docker-compose/local-llm/docker-compose.yaml 与 docker/docker-compose/custom-models/docker-compose.yaml 都映射了8888:88889999:9999;docker/standalone/Dockerfile 中同样通过EXPOSE 8888ENV HINDSIGHT_API_PORT=8888固定 API 端口。启动完成后,客户端应连接http://localhost:8888
  • 环境变量HINDSIGHT_API_LLM_API_KEYHINDSIGHT_API_LLM_MODEL告诉服务端用哪个模型处理记忆的写入、检索与综合(此处示例为gpt-4o-mini)。
  • 数据持久化-v $HOME/.hindsight-docker:/home/hindsight/.pg0把 Hindsight 内置的 PostgreSQL 数据目录(pg0)挂载到宿主机,容器重启后记忆不丢失。
  • 如果不想用 Docker 单容器镜像,仓库还提供了完整的 docker/docker-compose 多服务编排与 docker/standalone 独立部署脚本,适用于接入外部 PostgreSQL、向量扩展、自托管 LLM 等不同场景;本配方直接用官方镜像即可。

四、安装依赖与配置 API Key

4.1 安装依赖

# Tavily is optional - demo works with simulated results if not installed !pip install -q hindsight-client openai tavily-python nest-asyncio

其中hindsight-client即仓库 hindsight-clients/python 目录维护的官方 Python 客户端:上层是手写维护、易用的Hindsight包装类(hindsight_client.py),底层是基于 OpenAPI 自动生成的完整 API 客户端hindsight_client_api

4.2 配置 API Key

import getpass import os # Set OpenAI API key (used by both Hindsight and the demo) if not os.getenv("OPENAI_API_KEY"): os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ") # Tavily is optional - for real web search if not os.getenv("TAVILY_API_KEY"): tavily_key = getpass.getpass("Enter your Tavily API key (or press Enter to skip): ") if tavily_key: os.environ["TAVILY_API_KEY"] = tavily_key print("API keys configured!")

注意:OpenAI API key 是必需的,它同时服务于两端——Hindsight 服务端负责记忆的语义化处理,演示代码负责查询改写与摘要生成。Tavily 则完全是可选项,跳过即可用模拟结果跑通全流程。

五、初始化客户端:连接本地 Hindsight

import nest_asyncio nest_asyncio.apply() from openai import OpenAI from hindsight_client import Hindsight # Initialize Hindsight client (connects to local Docker instance) hindsight = Hindsight( base_url=os.getenv("HINDSIGHT_BASE_URL", "http://localhost:8888"), ) openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # Optional: Tavily for real web search try: from tavily import TavilyClient tavily = TavilyClient(api_key=os.getenv("TAVILY_API_KEY")) HAS_TAVILY = True print("Tavily configured - using real web search!") except (ImportError, Exception) as e: HAS_TAVILY = False print("Note: Using simulated search results (Tavily not configured)") USER_ID = "search-user-demo" print("Clients initialized!")

从源码看,Hindsight构造函数接受base_url、可选的api_key(作为 Bearer token 注入 Authorization 头)、请求超时timeout(默认 300 秒)与user_agent等参数(见 hindsight_client.py)。本配方中所有记忆都写入同一个bank_id="search-user-demo",这就是"按用户隔离记忆"的体现——每个用户/租户对应一个 memory bank,天然支持多用户场景。

六、三个核心 API:retain / recall / reflect

在进入业务函数之前,先理解本配方依赖的三大记忆操作。它们对应 HTTP API 的 memory 端点,并在 Python 客户端中都有同步(retain/recall/reflect)与异步(aretain/arecall/areflect)两种形式,同步版本适合脚本与 REPL。

6.1 retain —— 写入记忆

retain(bank_id, content, metadata, ...)把一条记忆写入指定 bank,服务端会自动完成分块、嵌入与事实抽取。常用参数(见 hindsight_client.py):

参数含义
bank_id目标 memory bank 的唯一标识
content记忆内容:纯字符串,或有序的 content block 列表(多模态附件,需要服务端 vision 能力)
timestamp可选事件时间戳
context可选的上下文描述
document_id可选,用于对记忆分组
metadata用户自定义元数据(dict[str, str]),本配方用它标注category
entities/resolve_entities显式实体标注,以及是否与 bank 已有实体解析对齐
tags可选标签,recall/reflect 时可按标签过滤
retain_async/operation_id异步处理与幂等重试支持

6.2 recall —— 语义召回记忆

recall(bank_id, query, budget, ...)用语义相似度检索相关记忆,返回RecallResponse,其resultsRecallResult列表,每项至少包含idtext字段(见 recall_response.py 与 recall_result.py),所以代码中可以直接取m.text拼上下文。常用参数(见 hindsight_client.py):

参数含义
query检索查询,语义匹配
budget召回预算:"low"/"mid"/"high"(默认"mid"),控制召回多少内容(对应枚举定义见 budget.py)
types事实类型过滤(world/experience/observation
max_tokens结果最大 token 数(默认 4096)
tags/tags_match标签过滤与匹配模式(any/all/any_strict/all_strict/exact
min_scores各阶段分数下限,如{"semantic": 0.2, "final": 0.5}
include_entities/include_chunks/include_source_facts是否附带实体观察、原始分块、来源事实
temporal_window时间窗口,用于时间感知检索

6.3 reflect —— 基于记忆生成回答

reflect(bank_id, query, budget, ...)基于 bank 的身份与全部记忆生成上下文相关的回答,返回ReflectResponse,其text字段是格式良好的 Markdown 文本(见 reflect_response.py)。除budget(默认"low")外,还可传response_schema做结构化输出、include_facts返回based_on依据、tags过滤等(见 hindsight_client.py)。

一句话总结三者分工retain让系统"记住",recall让系统"想起",reflect让系统"总结与作答"。本配方正是用这三个原语拼出完整记忆闭环。

七、定义辅助函数:记忆驱动的搜索流水线

本配方把整条流水线封装为 5 个辅助函数,逐一看它们的职责与实现。

7.1 存储偏好与交互记录

def store_preference(preference: str) -> str: """Store a user preference.""" hindsight.retain( bank_id=USER_ID, content=f"User preference: {preference}", metadata={"category": "preference"}, ) return f"Learned: {preference}" def store_interaction(query: str, response: str) -> None: """Store a search interaction.""" hindsight.retain( bank_id=USER_ID, content=f"Search query: {query}\nResult highlights: {response[:200]}", metadata={"category": "search_history"}, )

两个写入函数都依赖retain,并通过metadata["category"]区分记忆类型——preference(用户偏好)与search_history(搜索历史)。由于metadata支持任意键值对,后续可以按类别筛选、审计或做标签化管理。store_interaction只截取回答前 200 字符写入,避免把冗长回答整段塞进记忆,这是控制记忆体积的实用技巧。

7.2 召回用户上下文

def get_user_context(query: str) -> str: """Retrieve relevant user context.""" memories = hindsight.recall( bank_id=USER_ID, query=f"preferences location dietary lifestyle {query}", budget="mid", ) if memories and memories.results: return "\n".join(f"- {m.text}" for m in memories.results[:6]) return ""

这里有两个值得学习的细节:

  • 查询增强:在用户原始查询前拼接preferences location dietary lifestyle等提示词,引导语义检索偏向"偏好类"记忆——这是基于向量检索特性的工程技巧;
  • 预算与截断budget="mid"控制召回量级,再取results[:6]收紧到最多 6 条作为上下文,防止 prompt 过长。

7.3 个性化搜索主流程

def personalized_search(query: str) -> str: """Perform a personalized search.""" user_context = get_user_context(query) enhancement_prompt = f"""Given this user's preferences and the search query, suggest how to enhance the search. User preferences: {user_context if user_context else "No preferences recorded yet."} Search query: {query} Return a JSON object with: - "enhanced_query": The improved search query incorporating relevant preferences - "filters": Any specific filters to apply (e.g., "vegetarian", "within 5 miles") - "reasoning": Brief explanation of personalizations applied""" enhancement = openai_client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": enhancement_prompt}], temperature=0.3, max_tokens=300, ) enhanced_info = enhancement.choices[0].message.content # Perform the search if HAS_TAVILY: search_results = tavily.search( query=query, search_depth="advanced", max_results=5, ) results_text = "\n".join( f"- {r['title']}: {r['content'][:150]}..." for r in search_results.get('results', []) ) else: results_text = f"[Simulated search results for: {query}]" response_prompt = f"""Based on the search results and user preferences, provide a personalized summary. User preferences: {user_context if user_context else "No preferences recorded yet."} Query: {query} Search enhancement applied: {enhanced_info} Search results: {results_text} Provide a helpful, personalized response that takes into account their preferences.""" response = openai_client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": response_prompt}], temperature=0.7, max_tokens=500, ) answer = response.choices[0].message.content store_interaction(query, answer) return answer

该函数完整呈现了"记忆 → 查询改写 → 检索 → 个性化摘要 → 记忆回写"的闭环:

  1. 取上下文get_user_context(query)召回相关偏好;
  2. 查询增强:让 LLM 基于偏好输出结构化的enhanced_query/filters/reasoning(温度 0.3 保证稳定输出);
  3. 执行搜索:配置了 Tavily 时调用tavily.search(search_depth="advanced", max_results=5)取真实结果,否则用模拟文本占位——这保证了配方在无 Tavily key 时也能端到端跑通;
  4. 个性化摘要:把偏好、增强信息、检索结果一起交给 LLM 生成贴合用户需求的回答(温度 0.7 让表达更自然);
  5. 记忆回写store_interaction(query, answer)把本次交互写回记忆,形成"越用越懂你"的正反馈。

注意这里的enhanced_inforesults_text都是"文本摘要",未强制 JSON 解析——enhanced_query等内容主要通过 prompt 约束生成,演示更侧重流程完整性。生产环境可要求response_format={"type": "json_object"}并做严格解析。

7.4 生成用户偏好画像

def get_preference_profile() -> str: """Get a summary of the user's preference profile.""" profile = hindsight.reflect( bank_id=USER_ID, query="""Summarize what we know about this user: - Location and neighborhood - Dietary preferences and restrictions - Work style and schedule - Hobbies and interests - Family situation - Shopping preferences""", budget="high", ) return profile.text if hasattr(profile, 'text') else str(profile)

这一步用reflect让 Hindsight 基于整库记忆综合出结构化的用户画像。budget="high"意味着给综合过程更大的召回与生成预算,适合"汇总全貌"这类需要全面视角的任务。hasattr(profile, 'text')是防御性写法——ReflectResponsetext字段是 Markdown 格式的回答(见 reflect_response.py),直接打印即可得到可读画像。

八、构建用户画像:写入首批偏好

一切就绪后,先向 Hindsight 写入一批模拟用户偏好,作为系统"学习"的第一手素材:

print("Learning user preferences...") preferences = [ "Lives in San Francisco, Mission District", "Works remotely as a software engineer", "Vegetarian, prefers organic food when possible", "Has a 5-year-old daughter named Emma", "Enjoys hiking and outdoor activities on weekends", "Prefers quiet coffee shops for remote work", "Lactose intolerant, uses oat milk", "Interested in sustainable and eco-friendly products", "Usually free on Tuesday and Thursday afternoons", "Husband is allergic to nuts", ] for pref in preferences: result = store_preference(pref) print(f" {result}")

这 10 条偏好覆盖了位置、职业、饮食、家庭、爱好、购物倾向等多维信息,每条都会以metadata={"category": "preference"}写入search-user-demo这个 bank。它们是后续个性化搜索的"知识底座"。

九、验证效果:个性化搜索结果

记忆就绪后,运行三个典型的个性化搜索:

import time print("=" * 60) print(" Personalized Search Results") print("=" * 60) searches = [ "Find a good coffee shop for working remotely", "Restaurant recommendations for a family dinner", "Birthday gift ideas for a 5-year-old", ] for query in searches: print(f"\nSearch: {query}") print("-" * 40) result = personalized_search(query) print(result) time.sleep(1)

观察三个查询,理解"个性化"如何发生作用:

  • "Find a good coffee shop for working remotely":应命中"Prefers quiet coffee shops for remote work"与"Works remotely",从而倾向于推荐安静的、适合办公的咖啡店;
  • "Restaurant recommendations for a family dinner":应命中素食、乳糖不耐、丈夫坚果过敏、5 岁女儿等信息,从而过滤掉含坚果、乳制品的餐厅并考虑家庭友好;
  • "Birthday gift ideas for a 5-year-old":应命中女儿 Emma 的年龄与爱好(如户外活动),给出适龄且贴合兴趣的礼物建议。

time.sleep(1)是为了避免连续请求过密。若配置了 Tavily,这里会返回真实网页搜索结果的个性化摘要;否则返回基于模拟结果的演示文本——两种模式都能展示"偏好注入"的效果。

十、查看偏好画像:检验系统学到了什么

print("=" * 60) print(" User Preference Profile") print("=" * 60) print(get_preference_profile())

这一步通过reflect(budget="high")让 Hindsight 把散落的记忆综合成一份连贯的用户画像。它既是"效果检验"——确认系统确实学到了正确信息;也是"能力演示"——体现 reflect 与简单拼接式检索的本质区别:reflect 不是列出一堆记忆原文,而是基于记忆生成有结构的归纳性回答。配合reflectinclude_facts=True参数,还能进一步拿到based_on字段,追溯画像中每个结论依据了哪些具体记忆。

十一、输入你自己的搜索词

配方最后留了一个"自由发挥"入口,把任意查询换成你自己的问题:

your_search = "Best hiking trails near me" # Change this! print(f"Search: {your_search}") print("-" * 40) print(personalized_search(your_search))

例如把查询改为 "Best hiking trails near me",系统应结合"Enjoys hiking and outdoor activities on weekends""Lives in San Francisco, Mission District"等偏好,给出旧金山 Mission District 附近、周末可去的徒步路线建议——这正是个性化搜索与普通搜索的体验差异所在。此外,由于每次搜索都会通过store_interaction回写记忆,多跑几次后系统对用户的了解会持续加深。

十二、清理与收尾

hindsight.close() print("Client connection closed.")

close()关闭底层 HTTP 连接。在异步环境(如 FastAPI、LangGraph)中,客户端还提供aclose()与全套a*异步方法(aretain/arecall/areflect),应优先使用异步版本以避免阻塞事件循环(详见 hindsight_client.py 的文档说明)。由于记忆持久化在~/.hindsight-docker挂载卷中,即使关闭客户端甚至重启容器,search-user-demo这个 bank 的数据依然保留——再次运行 Notebook 时系统仍"记得"上次学到的偏好。

十三、进阶扩展方向

本配方是一个最小可用闭环,基于它你可以自然延伸:

  • 多用户隔离:每个用户使用独立bank_id,即可零改动扩展为多用户搜索助手;参考同系列 per-user-memory.md 配方中的多用户模式;
  • 标签化记忆治理retain支持tagsrecall/reflect支持tags_matchany/all/any_strict/all_strict/exact),可以为不同类别记忆打标签并精准过滤;
  • 结构化输出reflectresponse_schema参数支持 JSON Schema 结构化输出,把画像生成从"文本摘要"升级为"字段规整的用户档案";
  • 异步化改造:把脚本改造为异步版本(aretain/arecall/areflect),可嵌入 FastAPI 服务、LangGraph 或 CrewAI 等 Agent 框架;
  • 真实搜索增强:接入 Tavily 之外,还可以把enhanced_query真正用于改写后的搜索词、把filters转化为搜索 API 的结构化过滤参数;
  • 记忆质量优化:通过create_bankenable_observationsenable_rerankingenable_temporal_retrieval等 bank 级开关(见 hindsight_client.py),按业务需求调节记忆的观察综合、重排与时间感知能力。

总结

本配方完整演示了 Hindsight "Agent Memory That Learns" 的核心循环:retain沉淀偏好与交互、用recall在搜索前唤起相关记忆、用reflect综合生成画像与个性化回答,并以 Tavily(可选)作为真实搜索源。对照本仓库源码可以确认,budgetlow/mid/high取值、RecallResponse.results[].textReflectResponse.text等接口细节均与 Python 客户端实现一致。将这套模式扩展到垂直搜索、推荐、客服等场景,即可构建出"越用越懂用户"的下一代搜索体验。

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

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

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

多 Agent 跑 MCP 全域资源共享:Key 走 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 14:13:49

Vue3青光眼诊断前端系统:多模态数据融合与临床决策支持

简介:本资源是一套基于Vue框架开发的青光眼诊断系统前端源码,面向前端开发者、医疗信息化学习者及Web全栈初学者,聚焦数字医疗场景下的界面构建与交互实现。压缩包共32个文件,总大小2.45MB,涵盖9个功能完备的Vue组件&a…

作者头像 李华
网站建设 2026/9/14 14:13:39

ODrive深度解析:协议级文件系统挂载与数据主权实践

1. 这不是“又一个云盘同步工具”,而是一把能拧开本地数据主权的螺丝刀你搜“ODrive”时,大概率会看到一堆“网盘挂载”“多平台同步”的泛泛介绍——但真正用过的人知道,ODrive根本不是给小白点几下就完事的傻瓜软件。它本质是一个协议级数据…

作者头像 李华
网站建设 2026/9/14 14:13:21

PHP配资系统源码部署实战:策略买点计算与A股行情接入

简介:这是一套PHP编写的配资交易系统源码,面向具备一定PHP基础的开发者以及希望了解A股策略交易逻辑的技术人员,可用于快速搭建带有策略买点选择、账户资金管理和后台配置功能的线上模拟交易平台。压缩包共2001个文件,整体约47.11…

作者头像 李华
网站建设 2026/9/14 14:12:32

ScyllaDB 全文检索实战:fulltext_index 与 BM25 查询的完整解析

ScyllaDB 全文检索实战:fulltext_index 与 BM25 查询的完整解析 【免费下载链接】scylladb NoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB 项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb …

作者头像 李华