iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
AI Engineering

30天拆Agent:從Repo看設計系列 第 19 篇

Day 19|Codex、Claude、OpenClaw 都有 Memory,為什麼還需要 Hindsight?

  • 分享至 

  • xImage
  •  

前幾篇已經分別看過 Codex、Claude 與 OpenClaw 的 Memory。

現在的 Agent Framework 早就不是「每次對話結束就失憶」:

  • Codex 有自己的專案 Memory 與歷史整理。
  • Claude Code 有 CLAUDE.md、Auto Memory 與 Memory Tool。
  • OpenClaw 也有 daily memory、搜尋與 consolidation。

所以看到 Hindsight 時,第一個問題反而是:

既然 Agent 自己就有 Memory,為什麼還需要另一套 Memory Service?

這篇就從這個問題開始。


Hindsight 怎麼處理 Memory?

Hindsight 不是 Agent Framework,而是一個獨立的 Memory Server。

對外最核心只有三個操作:

API 用途 簡單例子
retain() 有新資訊值得長期保存 「使用者偏好早班機」
recall() 找某件已知的歷史資訊 「他上次選哪個座位?」
reflect() 綜合多筆 Memory 後整理結論 「這位客戶選航班最重視什麼?」

例如:

client.retain(
    bank_id="flight",
    content="User prefers morning flights"
)

client.recall(
    bank_id="flight",
    query="What seat did the user choose last time?"
)

client.reflect(
    bank_id="flight",
    query="Summarize this user's booking preferences"
)

官方 README 也是直接用這三個操作定義 Hindsight 的核心介面。

所以 Hindsight 和 MEMORY.md 最大的差別是:

它把 Memory 從 Agent 裡抽出來,做成一套獨立的資料服務。

接下來看四個比較有辨識度的設計。


1. Memory 不再綁在某一個 Agent

如果只使用原生 Memory:

Codex
└── Codex Memory

Claude Code
└── Claude Memory

OpenClaw
└── OpenClaw Memory

三套 Agent 就自然形成三套 Memory。

Hindsight 改成:

Codex ──────┐
Claude Code ├────→ Hindsight
OpenClaw ───┘

Repo 也直接提供:

hindsight-integrations/
├── codex/
├── claude-code/
└── openclaw/

例如 Claude Code Integration 會在 lifecycle 上接:

UserPromptSubmit
↓
Hindsight recall
↓
Memory 注入 Claude Context

Claude 完成工作
↓
Stop Hook
↓
Hindsight retain

所以它真正適合的情境是:

我同時有不同 Agent,希望它們使用同一套 Memory。

例如今天從 Codex 換到 Claude Code,之後又接 LINE Agent,只要都接 Hindsight,Memory 不一定跟著 Runtime 重建。


2. 不只統一存放,還能統一「什麼值得記」

只把所有 conversation 丟進同一個 PostgreSQL 意義不大。

更實際的問題是:

Codex 記 architecture decision
Claude 記 user correction
OpenClaw 記 preference

最後資料格式還是不一致。

Hindsight 因此讓 每個 Bank 自己設定 Extraction Rule。

而且這不是要修改 Hindsight 原始碼,而是 Bank Configuration。

官方提供三種設定方式:

Python / SDK
Bank Config API
Control Plane UI

Bank Config 文件也明確說,每個 Bank 可以獨立設定。


例如自己定義要記什麼

假設我希望 Agent 專案只記:

architecture decision
recurring problem
successful solution
failed approach
user correction

最簡單不用改 Extraction Logic,只設定 retain_mission:

client.create_bank(bank_id="engineering")

client.update_bank_config(
    "engineering",
    retain_mission="""
    Always retain:
    - architecture decisions
    - recurring problems
    - successful solutions
    - failed approaches
    - user corrections

    Ignore:
    - greetings
    - meeting logistics
    - temporary task status
    """
)

官方自己的 example 也是透過:

client.update_bank_config(
    "my-bank",
    retain_mission="Always include technical decisions...",
)

直接修改 Bank Config。

retain_mission 的意思是:

保留 Hindsight 原本的 Extraction Rule,只告訴它這個 Bank 特別要注意什麼。

如果連內建規則都不要,才改:

client.update_bank_config(
    "engineering",
    retain_extraction_mode="custom",
    retain_custom_instructions="""
    Extract only reusable engineering knowledge.

    Allowed:
    - decision
    - problem
    - solution
    - lesson

    Ignore temporary execution status.
    """
)

差異很簡單:

設定 什麼時候用
retain_mission 大致接受 Hindsight 原本邏輯,只想改「注意什麼」
retain_extraction_mode="custom" 想自己定義 Extraction Rule
retain_custom_instructions custom 模式真正使用的規則

官方文件也特別說明,retain_custom_instructions 只有在 custom mode 下才生效。


entity_labels 也是 Bank Config,不是修改程式碼

假設我希望所有 Agent 最後都使用相同分類:

preference
decision
problem
solution
lesson

可以直接:

client.update_bank_config(
    "engineering",
    entity_labels=[
        {
            "key": "memory_type",
            "description": "Type of reusable project knowledge",
            "type": "value",
            "values": [
                {"value": "preference"},
                {"value": "decision"},
                {"value": "problem"},
                {"value": "solution"},
                {"value": "lesson"}
            ],
            "tag": True
        }
    ]
)

Hindsight 會把 Bank 裡的 entity_labels 編成 Structured Output Schema,讓 Extraction LLM 從指定 vocabulary 裡選值,而不是讓 Agent 自己自由發明分類。

因此後面可以直接:

client.recall(
    bank_id="engineering",
    query="What decisions did we make?",
    tags=["memory_type:decision"],
    tags_match="any_strict"
)

所以這裡的流程其實是:

entity_labels
→ 定義分類規則

LLM
→ 自動分類

tag: true
→ 分類結果可以直接拿來過濾

這比每個 Agent 自己定義一套 Memory Type 更容易統一。


3. Recall 不只 Vector Search,還另外提供 Reflect

Hindsight 的 Recall 同時使用幾種 Retrieval。

簡單看:

Search 適合找什麼 例子
Semantic 用不同文字描述同一件事 問「改票為什麼失敗?」找到 fare_rule_check was skipped
BM25 ID、Error Code、Function Name 等精確字詞 BR198、ERR302、fare_rule_check
Graph 找 Entity 之間的關聯 Vivian → BR198 → fare_rule_check
Temporal 問時間順序、最近一次、某期間 「上一次改票發生什麼?」

四條 Retrieval 結果最後再經過:

Semantic ─┐
BM25 ─────┤
Graph ────┼→ RRF → Cross-encoder Rerank → Result
Temporal ─┘

程式位置:hindsight-api-slim/hindsight_api/ engine/search


Recall 和 Reflect 什麼時候用?

Recall 比較像搜尋:

「Vivian 上次選哪個座位?」

找到:

Vivian 上次選走道位。

但:

「Vivian 選航班時最在意什麼?」

答案可能散在:

喜歡早班
偏好走道
不喜歡轉機
價差 2,000 內偏好直飛

這時可以用 reflect()。

Reflect Agent 在:engine/reflect/agent.py

它會從:

Mental Model
↓
Observation
↓
Raw Fact

逐層找 Evidence,再由 LLM 整理答案。


hindsight-system-evals/ 到底是什麼?

它是一套:Hindsight 自己的黑箱品質測試。

它會真的啟動或連接 hindsight-api,使用真實 LLM,從公開 Client API 去測整套 Memory 行為,而不是 import Hindsight Engine 直接測 function。

目前會測:

retain 有沒有抽錯語言 / 事實
reflect 能不能正確回答
knowledge page 更新後會不會把舊事實弄丟
source priority 有沒有選錯
refresh 花多少 token / cost

可以執行:

cd hindsight-system-evals

uv run pytest evals --full

因此它比較適合:

修改 Hindsight Config、Model 或版本後,確認 Memory 品質有沒有退化。

它不是拿來直接比較 Codex、Claude、OpenClaw native memory。

真正負責 Memory System Benchmark 的是另一個專案:

Agent Memory Benchmark
vectorize-io/agent-memory-benchmark

AMB 包含 LoComo、LongMemEval、BEAM、PersonaMem 等 Dataset,Evaluation Harness 也不綁 Hindsight internals,其他 Memory Backend 可以實作 Adapter 後跑同一套測試。

所以:

工具 用途
hindsight-system-evals/ 測 Hindsight 自己改版後有沒有退步
AMB 用相同 Dataset / Judge 比不同 Memory Backend

4. Bank + Tag:Hindsight 怎麼區分 User、Project,之後又怎麼分析?

前面介紹 retain() 時一直都有一個參數:

client.retain(
    bank_id="user-a",
    content="..."
)

這個 bank_id 就是 Hindsight 用來決定:

這筆 Memory 屬於哪一個獨立記憶空間。

可以先把 Bank 想成一個 Memory Container:

Hindsight
├── Bank: user-a
│   ├── Memory 1
│   ├── Memory 2
│   └── Memory 3
│
└── Bank: user-b
    ├── Memory 1
    └── Memory 2

不同 Bank 彼此隔離,所以 user-a 的 Recall 不會直接搜尋 user-b。

Bank 不需要 Model 自己判斷,也不像 Codex Worktree 一樣由 Git 結構決定,而是 Application 或 Integration 決定要使用哪個 bank_id。

最簡單可以直接指定:

client.retain(
    bank_id="user-a",
    content="User prefers morning flights"
)

而且 Bank 不一定要事先建立。第一次對這個 bank_id 寫入資料時,Hindsight 可以自動建立它。

如果使用 Codex、Claude Code、OpenClaw Integration,也可以設定讓 Plugin 自動依照目前的 User / Project 產生 Bank ID。

例如 Claude Code 可以設定:

dynamicBankId = true
dynamicBankGranularity = ["agent", "project"]

讓不同 Project 自動落到不同 Bank。

OpenClaw 也可以設定:

{
  "dynamicBankId": true,
  "dynamicBankGranularity": ["user"]
}

形成:

User A → Bank A
User B → Bank B

Bank 負責隔離,Tag 負責分類

如果同一個 User 又有很多 Project,就不一定需要每個 Project 都建立 Bank。

例如:

User A
├── flight
├── Jira
└── Commerce

如果這三個專案之間沒有安全隔離需求,而且之後還希望一起分析,可以設計成:

Bank = user-a

然後每筆 Memory 再加 Tag:

client.retain_batch(
    bank_id="user-a",
    items=[
        {
            "content": "Runtime tool gate replaced prompt approval.",
            "tags": ["project:flight"]
        },
        {
            "content": "OAuth credential must be bound to user scope.",
            "tags": ["project:jira"]
        }
    ]
)

兩者的差異可以直接記成:

Bank Tag
作用 Memory 硬隔離 Bank 裡面的分類
誰設定 Application / Integration 指定 bank_id Retain 時附上 tags,或由 entity_labels 自動產生
能不能一起查 不同 Bank 不能直接一次 Recall 同一 Bank 可以跨 Tag 查
適合 User、Tenant、安全邊界 Project、Topic、Agent Source
例子 user-a project:flight、source:codex

所以比較實際的設計可能是:

Hindsight
│
├── Bank: user-a
│   ├── project:flight
│   ├── project:jira
│   └── project:commerce
│
└── Bank: user-b
    ├── project:flight
    └── project:jira

這樣 User A / User B 完全隔離,但 User A 自己的不同 Project 還可以一起分析。


那我要怎麼分析這些 Memory?

即時分析
→ Application / Agent 分別 Recall 多個 Bank,再合併結果

大量分析
→ 用 SDK / REST API Export Memory,再交給 Python、BI 或其他 Analytics Pipeline

Hindsight 也提供 MCP,所以 Codex、Claude 或其他 MCP Client 可以直接操作:

list_banks
recall
reflect
list_memories

因此如果只是希望:

「讓 Agent 自己讀幾個 Project 的 Memory 後整理結論」

MCP / API 就已經足夠。

如果是:

「我要把幾萬筆 Memory 匯出,分析各 Project 最常見的 Failure Pattern」

則直接使用 Export API 會比較合理。

例如:

archive = await client.aexport_bank("flight")

再把不同 Bank 的結果交給自己的分析程式。


為什麼底層用 PostgreSQL?

原因其實很單純:Hindsight 存的不只是文字。

還包含:

Memory
Entity
Relationship
Tag
Timestamp
Embedding
Document
Observation

PostgreSQL 可以同時處理一般結構化資料、全文搜尋,以及透過 pgvector 儲存 Vector,所以不需要另外再維護:

SQL Database
+
Vector Database
+
Graph Database

對 Hindsight 這種想把 Memory 做成獨立資料服務的架構來說,用 PostgreSQL 可以把大部分資料放在同一套 Storage 裡。

這也是它和 Markdown-based Memory 很不一樣的地方:

Codex / OpenClaw 類型
→ Memory 比較像檔案與 Agent 工作空間的一部分

Hindsight
→ Memory 比較像一套可以被多個 Agent 查詢的 Database

5. 風險:多一套 Memory Pipeline,也多一套 Failure Point

Hindsight 的問題其實和它的特色來自同一件事:

Conversation
↓
Integration
↓
Chunk
↓
Extraction
↓
Entity
↓
Observation
↓
Index
↓
Retrieval
↓
Rerank

中間步驟比直接讀 MEMORY.md 多很多。

實際 Issue 已經出現幾類問題:

問題 結果
Extraction / Chunk 缺 Context User 的資訊被記成 Assistant,或抽出錯誤 Fact
Vector Index / Retrieval 異常 Memory 明明存在,但 Recall under-return
Bank 切太細或 Bank ID 不穩 Memory 還在,但 Agent 查不到,看起來像失憶
Consolidation / Reflect 太重 大 Bank 可能增加大量 DB、LLM 與記憶體成本

例如已有使用者回報 extraction 經常出現 attribution 錯誤,而且已影響實際使用。

也有 Production deployment 發現 Vector Index 問題會讓 Recall 結果 under-return。

Hindsight 增加了 Memory 的結構化與查詢能力,也增加了需要測試、監控與排查的環節。


6. 模型與原生 Memory 已經很強,還需要 Hindsight 嗎?

Hindsight 最大的風險不是「沒有用」,而是:

Codex、Claude、OpenClaw 本來就有自己的 Memory,再加一套 Hindsight,可能只是重複處理同一批資訊。

例如 Codex 已經記住:

這個專案的 API 採 REST,不使用 GraphQL。

Hindsight 又把同一段對話做:

Conversation
↓
Fact Extraction
↓
Observation
↓
Recall
↓
重新放回 Codex Context

這時就可能出現幾個問題:

  • 重複資訊:Native Memory 和 Hindsight 都保存同一件事。
  • 舊資訊干擾:程式已經修改,但 Hindsight Recall 到以前的決定。
  • 資訊被篩掉:Extraction 或 Rerank 沒選中原本重要的內容。
  • 額外成本:多了 Extraction、Retrieval、Reflect 的模型呼叫與延遲。

因此真正值得比較的是:

方案 要確認什麼
Agent 原生 Memory Codex / Claude / OpenClaw 自己的 Memory 是否已經足夠
原生 Memory + Hindsight 加上 Hindsight 後,Recall 是否真的更準、跨 Agent 是否更方便
調整後的 Hindsight 設定 Bank、Tag、Extraction Rule 後,改善是否值得額外複雜度

例如準備一組真的需要長期記憶的情境:

Session 1
決定所有改票前都必須執行 fare_rule_check

...經過多次 Session...

Session 20
請 Agent 修改改票流程

再看:

只用 Native Memory
→ 有沒有記得 fare_rule_check?

Native Memory + Hindsight
→ 有沒有找得更準?
→ 有沒有反而叫回過期資訊?
→ 多花多少時間與 Token?

這樣測的才是 Hindsight 真正增加的價值,而不是拿它跟「完全沒有 Memory」比較。

Hindsight Repo 的 Issue #2347 也有使用者提出類似疑問:Coding Agent 本來就已經有 Native Memory、Git History、文件等 Context,因此加入額外 Memory Layer 前,應該先確認它是否真的改善結果,而不是預設 Memory Pipeline 越多越好。

模型能力越強後,這個問題也會更明顯。如果 Agent 原本就能直接理解足夠的歷史內容:

Raw History
↓
Strong Model

那再經過:

Fact Extraction
↓
Observation
↓
Retrieval
↓
Rerank
↓
Strong Model

未必比較好,反而增加中途誤判的機會。

不過 Hindsight 有些功能不太會因模型變強而消失:

  • 跨 Agent 共用:Codex、Claude、OpenClaw 使用同一份 Memory。
  • User 隔離:不同 User 的 Memory 分開管理。
  • 來源追蹤:知道 Memory 從哪個 Agent、Project、Document 而來。
  • 匯出分析:Memory 可以另外交給程式或 Analytics Pipeline 分析。

所以 Hindsight 真正比較難被取代的價值,不是:

幫更強的模型多做幾層摘要。

而是:

當 Agent、User、Project 變多時,提供一個共同管理 Memory 的資料層。


References


上一篇
Day 18|OpenClaw 怎麼記住人?從 Memory Promotion 到多人記憶隔離
下一篇
Day 20|外部 Memory Service 之後:ai-memory 為什麼把共享記憶做成 Git-backed Project Wiki?
系列文
30天拆Agent:從Repo看設計 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言