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

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 です
FTS5SQLite の全文検索です。デフォルトは AND です
session_search過去会話を検索、読取、前後移動するエージェント tool です
discoveryqueryを渡してヒットしたセッションを返す形です
scrollsession_idaround_message_idで前後を切り出す形です
readsession_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_scrolldetailfullでした。ヒットは 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、modescrollでした。返った id は 5 から 9 です。messages_beforemessages_afterはどちらも 2 で、この window では端まで届いていません。前方へ進むときは最後の id、後方へ戻るときは先頭の id を次のアンカーにします。境界の 1 件は、次の window にも残ります。

セッション全体を見るときはsession_idだけを渡します。

session_search(session_id="lab_fts_query", db=db)

所要時間は 5.03ms でした。modereadmessage_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_roletool、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 MemoryMEMORY.md と USER.md の容量、session_search との分担、セッション境界
SQLite FTS5AND デフォルト、引用句、OR、NOT、prefix
一時directoryの実測list 3件、stats 13 messages、discovery 4.5ms、scroll 4.4ms、0件検索 0.75ms
この記事をシェア