RAGSpine
意思決定(ADR)

ADR 0017 — 会話履歴は生成専用のコンテキストである

過去のターンをインテント解析、検索、セキュリティ判断、エビデンスに入り込ませることなく、第一級の履歴機能を追加する。

ステータス: accepted

日付: 2026-07-14

不変の記録。ドリフト追跡の対象外(covers なし)。編集ではなく、上書き(supersede)で置き換えること。

本決定は、エンジン ADR 0005(バイト同一のデフォルトパスと並置する新機能)およびエンジン ADR 0010(モデル向けコンテキストから独立した決定的なインテント/セキュリティ)によって制約される。spinestudio ADR 0006 で顕在化したマルチターンの製品面のギャップに応えるものである。

ADR 番号はリポジトリローカルである。この原本の記録における参照は RAGSpine エンジンの ADR を指しており、 ウェブサイトの過去の ADR 0012 は別個の、ファミリーレベルの記録である。

コンテキスト

answer_question(question, store, provider, ...) は単発かつステートレスであり、履歴パラメータを 持っていなかった。製品レイヤーがマルチターンチャットを追加したとき、モデルに過去のコンテキストを 与える唯一の方法は、履歴を question 文字列に継ぎ足すことだった。

これは決定的なインテント解析を汚染した。1320 のような過去の回答の数値が期間 FY1320 として 読み取られる可能性があり、単一メトリクスの照会が誤った比較へと静かに変わり、決定的ルーティングと 捏造防止の正しさを壊してしまう。そのため製品レイヤーは、解析入力と生成コンテキストを構造的に 分離する第一級のパラメータをエンジンが公開するのを待つ間、履歴の注入をデフォルトオフに しておいた。

決定

answer_question にオプションのキーワード引数 history: Sequence[tuple[str, str]] | None = None を追加する。各ターンは (role, text) であり、想定されるロールは user または assistant である。同じ形状とセマンティクスを、サービスの /v1/ask/v1/ask/streamAskRequest.history として貫通させる。

デフォルトの None はバイト同一

_history_messages(None) == []。履歴がない場合、ツールループとナラティブのメッセージシーケンスは 変わらない。tests/agent/test_history.py::test_default_none_is_byte_identical のパラメータ化された回帰テストが、構造化チャネルの発見あり・発見なし・複数サブタスク・ナラティブの各挙動をカバーする。

履歴は決定的なインテント解析に決して入らない

パーサーが見るのは現在の question のみである: intent = parser.parse(question, …)。セキュリティ ゲートも同様にその生の質問を再スクリーニングする。_history_messages は過去のターンを OpenAI 形式の メッセージに変換し、_run_tool_loop_run_narrative においてシステムプロンプトと現在のユーザーターンの間に挿入する。現在の質問は最後のユーザーメッセージのままであるため、MockProvider の 最終ユーザーターン解析は汚染されない。

公開される履歴の形状はフラットな (role, text) タプルである。system/tool ロールを持ち込むことも、 tool_calls を密輸することもできない。未知のロールは user に正規化される。この解析入力と 生成コンテキストの構造的な分離が、製品側のペインポイントを解決する。

捏造防止は履歴があっても維持される

構造化チャネルは引き続き、発見された回答をツールのファクトから決定的にレンダリングするか、 ミスを not-found へ書き換える。履歴はエビデンスを生成しない。出典追跡は依然として実際のツール/検索 ヒットのみを指す。ネガティブテストでは、捏造されたファクト——Shanghai FY2099 REVENUE = 999——を履歴に配置し、 ナレッジベースのミスが拒否回答のままであること、決して 999 を引用しないこと、sources が空であることをアサートする。

ナラティブの検索クエリは現在の質問のみのままである。履歴は検索に決して入らない。

RESTRICTED の二重出口分離は変わらない

履歴が追加するのは生成コンテキストのメッセージのみである。検索には一切触れないため、link/rerank/ の出口は引き続き RESTRICTED コンテンツを除去する。tests/conformance/test_history_isolation.py が両方の履歴形態を拘束し、空虚でないリーキーリトリーバーによる逆証明を含む。

シグネチャはエンジンのオンボーディング複雑性予算の範囲内に留まる。history はデフォルト付きの キーワード専用引数であるため、最初の回答を得るまでのオンボーディングコストは変わらない。対応するエンジン側の記録は docs/adr/0012-onboarding-complexity-budget.md であり、ウェブサイトの同番号のファミリー ADR ではない。

検討した代替案(却下)

  • 履歴を question に継ぎ足し続ける: これこそがまさに当該の欠陥であり、決定的解析と 捏造防止を汚染する。
  • 任意の OpenAI list[dict] メッセージを受け入れる: 呼び出し側が systemtool_calls、または 捏造されたツール結果を注入できてしまう。フラットな (role, text) タプルは、意図的にその形状を表現できないようにしてある。
  • 照応・省略解決のためにインテントパーサーへ履歴を渡す: これは汚染経路を再び開くことになる。 決定的解析は現在の質問のみに依存する。オプションの ConversationSession によるスロットの 引き継ぎは別物のままである。
  • 履歴をナラティブ検索に供給する: 履歴が引用されないエビデンスソースになってしまう。 検索クエリは現在の質問のみのままとする。

帰結

  • エンジンは第一級のマルチターンコンテキストパラメータを持つ。製品は決定的ルーティングや 捏造防止を壊すことなく履歴を有効化できる。
  • バイト同一のデフォルト挙動、解析/コンテキスト分離、捏造防止、および RESTRICTED 分離はテストによって拘束される。
  • 新たな成果物は answer_question(history=)_history_messages、両方の ask ルートにおける AskRequest.historytests/agent/test_history.pytests/conformance/test_history_isolation.py、 および tests/service/api/test_api_ask_history.py である。
  • ConversationSession には手を触れていない。これは直交する存在であり、将来的には記憶された ターンから履歴リストを構築することもできる。

このページ