Hermes Agentの過去会話をFTS5で探す|session_searchとMemoryの分担

一時 directory へセッションを 3 件入れ、hibanas night draft で検索しました。discovery は 4.5ms で 3 件返りました。同じ DB の tool 出力に置いた合成トークンは、デフォルトの検索では 0 件です。
MEMORY.md に全部残す必要はありません。毎回使う事実だけを Memory へ置き、過去の言い回しは必要な時だけ引きます。session_search は LLM を呼ばず、SQLite の FTS5 が実メッセージを返します。
前回の再開手順から、中身の検索へ進む
前回は「Hermes Agentの過去セッションを名前で引き継ぐ」で、workspace、title、ID を照合してから再開しました。今回は会話を開かず、保存済みの本文から語句を探します。
| 時点 | 確認できる範囲 |
|---|---|
| 前回完了時 | workspace、title、IDを照合して対象だけを再開できます |
| 今回開始時 | 再開せずに、過去の発言そのものを探す手順が未確認です |
| 今回完了後 | discovery、scroll、read、browseと、デフォルトの role_filter を実測で区別できます |
再開したい ID が分かっている場合は前回へ戻ります。Memory の容量だけ知りたい場合は「なぜ全部を Memory に入れないのか」まで読めば足ります。tool 出力まで探す場合は「デフォルト検索は tool を見ない」から読めます。
なぜ全部を Memory に入れないのか
MEMORY.md は 2,200 文字、USER.md は 1,375 文字です。どちらもセッション開始時の system prompt へ入り、その後のターンでも固定費になります。作業の途中経過、一度きりの判断、長い引用をここに置くと、すぐ上限へ届きます。
session_search は別物です。公式 docs では、検索は数十ミリ秒、scroll は 1 から 2ms で、要約を生成しません。今回の一時 DB でも、discovery は 4.5ms、scroll は 4.4ms、存在しない語は 0.75ms でした。会話を再開するhermes -cとも違います。search は対象を開かず、ヒットしたメッセージを返します。
私は次の分岐で扱います。
毎回使う事実 → Memory(開始時に読む)
同じ会話を続きから開く → resume(list / rename / -c)
過去の発言を探す → session_search(FTS5、LLMなし)
gateway の長い 1 本セッションでは、この分岐が崩れます。本文がまだ context に残っていると、エージェントは search をほとんど使いません。公式も、仕事の区切りで/newを勧めています。
用語を揃える
| 用語 | この回での意味 |
|---|---|
| セッション | 1つの会話と、その message history をまとめた記録です |
| state.db | セッションのメタデータと本文を持つ SQLite です |
| FTS5 | SQLite の全文検索です。デフォルトは AND です |
| session_search | 過去会話を検索、読取、前後移動するエージェント tool です |
| discovery | queryを渡してヒットしたセッションを返す形です |
| scroll | session_idとaround_message_idで前後を切り出す形です |
| read | session_idだけを渡し、そのセッションを読む形です |
| browse | 引数なしで直近セッションを並べる形です |
| Memory | 毎回必要な少数の事実を、開始時の context へ入れる仕組みです |
| role_filter | 検索対象の role です。デフォルトは user と assistant です |
検証用の一時directoryを用意する
本番の Hermes home は使いません。同名の directory がある場合は上書きせず止めます。
LAB=/tmp/hibanas-session-search-lab-20260919
[ ! -e "$LAB" ] || {
printf 'already exists: %s\n' "$LAB"
exit 1
}
mkdir -p "$LAB"
export HERMES_HOME="$LAB"
今回の実機はHermes Agent v0.21.0 (2026.8.31)でした。version と sessions の subcommand を先に読みます。
hermes --version
hermes sessions --help
hermes --versionの先頭は次のとおりでした。
Hermes Agent v0.21.0 (2026.8.31) · upstream baf7ceea · local 1068df60 (+1 carried commit)
空の home のままでは検索対象がありません。検査では Install directory を PYTHONPATH へ足し、合成セッションを 3 件書きました。Install directory はhermes --versionの該当行を使います。ユーザー名付きの絶対パスはコマンドへ埋めません。
install_dir=$(hermes --version | awk -F': ' '/^Install directory:/{print $2}')
PYTHONPATH="$install_dir" python3 - <<'PY'
import os, time
from pathlib import Path
from hermes_state import SessionDB
lab = Path(os.environ['HERMES_HOME'])
db = SessionDB(lab / 'state.db')
now = int(time.time())
db.create_session('lab_memory_notes', source='cli')
db._conn.execute(
'UPDATE sessions SET started_at = ?, title = ? WHERE id = ?',
(now - 40000, 'Memory notes vs recall', 'lab_memory_notes'),
)
db.append_message('lab_memory_notes', role='user', content='MEMORY.md には毎回必要な事実だけを残したい')
db.append_message('lab_memory_notes', role='assistant', content='容量は 2,200 文字です。作業の詳細は session_search へ回します。')
db.append_message('lab_memory_notes', role='user', content='hibanas night draft の題材は session_search にする')
db.append_message('lab_memory_notes', role='assistant', content='FTS5 は LLM を呼ばず、実メッセージを返します。')
db.create_session('lab_fts_query', source='cli')
db._conn.execute(
'UPDATE sessions SET started_at = ?, title = ? WHERE id = ?',
(now - 20000, 'FTS5 query shapes', 'lab_fts_query'),
)
db.append_message('lab_fts_query', role='user', content='session_search の discovery で hibanas night draft を探す')
db.append_message('lab_fts_query', role='assistant', content='query を渡すと FTS5 でヒットし、上位は adaptive で hydrate されます。')
db.append_message('lab_fts_query', role='user', content='phrase 検索は "hibanas night draft" です')
db.append_message('lab_fts_query', role='assistant', content='AND が既定です。OR と NOT と prefix も使えます。')
db.append_message('lab_fts_query', role='tool', content='tool output noise: OPENROUTER_API_KEY=should-not-be-default-search-target')
db.create_session('lab_scroll', source='cli')
db._conn.execute(
'UPDATE sessions SET started_at = ?, title = ? WHERE id = ?',
(now - 1000, 'Scroll after discovery', 'lab_scroll'),
)
db.append_message('lab_scroll', role='user', content='ヒットした session の前後を読みたい')
db.append_message('lab_scroll', role='assistant', content='around_message_id と window で前後を切り出します。')
db.append_message('lab_scroll', role='user', content='hibanas night draft の続きを確認する')
db.append_message('lab_scroll', role='assistant', content='境界メッセージは次の window にも残ります。')
db._conn.commit()
print('seeded', lab / 'state.db')
PY
作成直後のstate.dbは 253,952 bytes でした。OS 付属の Python 3 と SQLite 3.46.1 で直接開くと、WAL-reset の警告が出ます。検査は Hermes CLI、またはhermes --versionが示す runtime を使います。古い system SQLite で本番のstate.dbを開かない方が安全です。
version 確認と一時 home の用意が済んでいる場合は、次の章へ進めます。
CLIで件数だけ先に読む
検索の前に、対象 home の件数を読みます。
hermes sessions list --limit 10
hermes sessions stats
今回の list は 3 行でした。
Title Preview Last Active ID
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
Scroll after discovery ヒットした session の前後を読みたい just now lab_scroll
FTS5 query shapes session_search の discovery で hibanas n just now lab_fts_query
Memory notes vs recall MEMORY.md には毎回必要な事実だけを残したい just now lab_memory_notes
stats は次のとおりです。
Total sessions: 3
Total messages: 13
cli: 3 sessions
Database size: 0.2 MB
source はすべてcliです。delete と prune は実行していません。ここまでが読み取りです。
discoveryで語句を探す
エージェントはsession_searchを tool として呼びます。検査では同じ関数を一時 DB へ向けました。queryだけを渡すと discovery です。mode引数はありません。
session_search(query="hibanas night draft", limit=3, db=db)
所要時間は 4.5ms でした。JSON の要点は次です。
success: true
mode: discover
detail: adaptive
count: 3
sessions_searched: 3
1 位はlab_scrollでdetailがfullでした。ヒットは message id 12、前後の本文も付きます。2 位と 3 位はcompactで、アンカー 1 件だけです。上位だけを詳しく読み、残りは ID を控えて scroll します。
存在しない語では、0.75ms で 0 件でした。
session_search(query="zzzz-no-such-token-xyz", limit=3, db=db)
success: true
mode: discover
count: 0
message: No matching sessions found. FTS5 ANDs all terms by default — broaden with OR (`alpha OR beta`), exact-match with quoted phrases, exclude with NOT, or prefix-match with `deploy*`.
複数語は AND です。ヒットが 0 件のときは、語を減らすか、ORか、引用句を試します。引用句'"hibanas night draft"'では、snippet のハイライトが単語単位ではなくフレーズ単位になりました。
compactの結果をscrollで広げる
2 位のlab_fts_queryは compact でした。match_message_id は 7 です。前後 2 件を見る呼び出しは次です。
session_search(session_id="lab_fts_query", around_message_id=7, window=2, db=db)
所要時間は 4.4ms、modeはscrollでした。返った id は 5 から 9 です。messages_beforeとmessages_afterはどちらも 2 で、この window では端まで届いていません。前方へ進むときは最後の id、後方へ戻るときは先頭の id を次のアンカーにします。境界の 1 件は、次の window にも残ります。
セッション全体を見るときはsession_idだけを渡します。
session_search(session_id="lab_fts_query", db=db)
所要時間は 5.03ms でした。modeはread、message_countは 5、truncatedは false です。小さなセッションなら、scroll より read の方が早いです。
引数なしは browse です。今回は 33.4ms で 3 件が新しい順に並びました。題名が思い当たらないときに使います。語句が分かっているなら discovery を先にします。
デフォルト検索は tool を見ない
lab_fts_queryの最後は role=toolです。本文には合成トークンを置きました。デフォルトの discovery では 0 件です。
session_search(query="OPENROUTER_API_KEY", limit=3, db=db)
success: true
mode: discover
count: 0
同じ語にrole_filter="user,assistant,tool"を付けると 1 件になります。matched_roleはtool、message id は 9 です。所要時間は短く、件数の差だけが重要です。
デフォルトが user と assistant なのは、tool 出力がノイズになりやすいからです。秘密が tool 結果へ混ざっていても、通常の検索では表に出ません。私はこれを安全装置だとは思いません。鍵を会話へ出さないことが先です。ただし「昨日のコマンド結果」を探すときは、role_filter を明示しないと見つかりません。
一時環境を片付ける
marker 代わりに、今回作ったパスだけを消します。別のパスなら止めます。
case "$LAB" in
/tmp/hibanas-session-search-lab-20260919)
test -f "$LAB/state.db"
;;
*)
printf 'unexpected LAB path\n' >&2
exit 1
;;
esac
rm -rf -- "$LAB"
unset HERMES_HOME
test ! -e "$LAB" && printf 'cleanup: OK\n'
この cleanup は記事用の一時 DB 向けです。本番 home では実行しません。
よくあるエラー
| 症状 | 原因 | 対応 |
|---|---|---|
| ヒットが 0 件です | FTS5 は複数語を AND します | 語を減らすか、OR、引用句、prefix を試します |
| コマンド結果だけが見つかりません | デフォルトの role_filter は tool を除外します | user,assistant,toolを明示します |
| 直近の会話が search に出ません | まだ同じセッションの context に残っています | 区切りで/newし、次のセッションから探します |
| 別件の会話が開きます | resume と search を取り違えています | 中身を見るだけなら session_search を使います |
| system Python で警告が出ます | OS の SQLite が古い場合があります | Hermes CLI か、version 表示の runtime を使います |
| 本番の会話が検索対象になります | HERMES_HOME を向けていません | 検査は一時 directory で行い、本番 home を検索しません |
出典
| 資料 | 確認した内容 |
|---|---|
| Hermes Agent Sessions | セッションの保存先、4つの呼び出し形、FTS5 の構文、role_filter |
| Hermes Agent Persistent Memory | MEMORY.md と USER.md の容量、session_search との分担、セッション境界 |
| SQLite FTS5 | AND デフォルト、引用句、OR、NOT、prefix |
| 一時directoryの実測 | list 3件、stats 13 messages、discovery 4.5ms、scroll 4.4ms、0件検索 0.75ms |
