fix: HIL の設計の欠落 4〜9 を修正する - #1120
Draft
owk-owk130 wants to merge 16 commits into
Draft
Conversation
review_decisions は 1095 日残るが、参照先の mastra_messages は 30 日、 retrieval_runs は 90 日で消えるため、判断履歴だけが根拠を失って残っていた。 判断時に質問・回答・根拠 source を evidence へ保存する。 生のユーザー質問を 3 年残すことになるため、Mastra の PIIDetector で氏名・ 住所・電話番号等を伏せてから保存する。redact に失敗したら本文を落として 根拠 source だけ残す。thread_id を持たせ、ユーザー削除依頼で retrieval_runs と review_decisions も消えるようにした。 retrieval_runs の 90 日は据え置き。人口規模から文脈だけで個人が特定できる ため、PII 検出を通したことは長期保管の根拠にしない。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
承認を取り消した情報源への訂正が公開中のまま残り、廃止した情報の訂正が 回答に注入され続けていた。承認が外れた時点で紐づく訂正を要再確認にし、 検索対象外の情報源に対する新規の訂正発行は 400 で拒否する。 要再確認の理由を needs_review_reason で区別し、元ページ更新と情報源失効を 村職員が画面で見分けられるようにした。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
訂正は admin で発行できるのに、その訂正の前提となる情報源の承認が super_admin 限定で、村職員が承認フローに入れなかった。 情報源の一覧と承認状態の変更を admin へ開放し、R2 の直接操作 (ファイル編集・変換・全同期・backfill)は super_admin のまま据え置く。 同じ prefix に mount した sub-app は blanket middleware を共有するため、 route ごとの middleware で指定する。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
情報源の承認 API に対応する画面が無く、村職員が管理画面だけで承認フローを 完結できなかった。未承認・公開中・停止中・却下済みで絞り込み、承認と停止を 確認ダイアログ付きで実行できる画面を追加する。 訂正一覧では要再確認の理由を区別して表示し、回答レビューでは会話と検索記録が 保管期限で消えている場合に判断時のスナップショットを表示する。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
Contributor
Coverage Report for server
File Coverage |
Contributor
Coverage Report for shared
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
redactPii は review の知識を持たない汎用処理で、ペルソナ抽出や Eval ケース 昇格でも保存前に通したい。PIIDetector の設定も定数化し、エージェントの inputProcessors に入れるときに検出種別が二重管理にならないようにする。 伏せた結果の対応付けを入力の添字からメッセージ id に変えた。strategy を filter / block にすると伏せられたメッセージが返り値から落ちて添字がずれ、 別のテキストへ他人の結果が入る。落ちた分は原文ではなく空文字にする。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
PIIDetector は PII を検出しても redacted_content を返せなかった場合、 handleDetectedPII が null を返して processInput が原文をそのまま通す。 warn ログだけ出て返り値は成功扱いなので、呼び出し側から区別できない。 電話番号・メール・郵便番号は形式が決まっており正規表現で決定的に伏せられる ため、LLM 判定の後段に置く。日本の電話番号は 090-1234-5678(3-4-4)から 01656-5-3311(5-1-4)まで桁の配分が一定しないため、0 始まりでハイフン区切り 3 ブロックとして扱う。 氏名と住所は決定的に書けないため LLM 判定のままとする。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
trace を訂正注入の前に書いていたため、retrieval_runs に curated/corrections が入らず、レビュー画面の根拠一覧に訂正が現れなかった。回答本文には反映されて いるので、村職員から見ると訂正が効いたのか判断できない状態だった。 注入後の結果からトレースを組み立て、検索でヒットしなかった source を訂正分と して記録する。レビュー画面では訂正に反映済みのバッジを出す。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
いずれも一度操作すると戻せず、押し間違いを村職員が自力で直せなかった。
- PATCH /admin/corrections/{id}: 本文を書き換えて R2 と検索へ再反映する
- DELETE /admin/review/{answerRunId}/decision: 直近の判断を削除して未判断に
戻す。その判断で解決済みにした bad 評価も、他の判断が参照していなければ
未解決へ戻す
- PATCH /admin/source-candidates/{id}/status に reset を追加。判断者と判断
日時を消す
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
- 訂正一覧に本文の修正フォームを追加 - 回答レビューに直近の判断を取り消すボタンを追加 - 情報源候補に未判断へ戻すボタンを追加 - 訂正・情報源・情報源候補の一覧に検索を追加 回答レビューの一覧はサーバー側でページングしているため検索を付けていない。 読み込み済みのページだけを絞り込むと全件を検索したように見えてしまう。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
# Conflicts: # web/src/app/dashboard/components/CorrectionsPanel.test.tsx # web/src/app/dashboard/components/SourceCandidatesPanel.test.tsx # web/src/app/dashboard/components/review/ReviewDetailModal.test.tsx
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014kkNoEdkn1kVK4La79XFpc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014kkNoEdkn1kVK4La79XFpc
削除系の DB 操作を deleteWithCount 経由に揃える Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014kkNoEdkn1kVK4La79XFpc
Contributor
Contributor
Coverage Report for widget
File CoverageNo changed files found. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
grilling で挙げた設計の欠落 4〜9 を修正する。
4. 情報源の承認画面が無かった
承認 API は PR #1114 で作ったが画面が無く、村職員が管理画面だけで承認フローを完結できなかった。「村職員が Git を触らない」という前提が崩れていた。
未承認・公開中・停止中・却下済みで絞り込める
SourcesPanelを追加し、承認と停止を確認ダイアログ付きで実行できるようにした。5. 権限の逆転
訂正の発行は admin なのに、その前提となる情報源の承認が super_admin 限定だった。訂正は検索結果へ強制注入されるため、より強い権限を要求するのが筋。
情報源の一覧と承認状態の変更を admin に開放した。R2 の直接操作(ファイル編集・変換・全同期・backfill)は super_admin のまま据え置く。
同じ prefix に mount した sub-app は blanket middleware を共有してしまうため、route ごとの middleware で指定している。
6. 情報源の停止が訂正に伝播しなかった
承認を取り消した情報源への訂正が公開中のまま残り、廃止した情報の訂正が回答に注入され続けていた。計画時に「元ソースの失効」を想定していなかった。
needs_review_reasonで「元ページ更新」と「情報源失効」を区別し、画面で見分けられるようにした7. 監査記録が根拠を失っていた
review_decisionsは 1095 日残るが、参照先のmastra_messagesは 30 日、retrieval_runsは 90 日で消える。3 年後には「誰がいつ判断したか」だけが残り、何についての判断か分からない行になっていた。判断時に質問・回答・根拠 source を
evidenceへスナップショットする方式にした。retrieval_runsの 90 日は動かしていない。人口規模から固有表現が無くても文脈だけで個人が特定できるため、生の質問文の保持期間を延ばす選択は取らなかった。生のユーザー質問を 3 年残すことになるので、Mastra の
PIIDetector(@mastra/core/processors、core 1.52.1 に同梱・新規依存なし)をstrategy: "redact"で通してから保存する。redact に失敗した場合は本文を落として根拠 source だけ残す。thread_idを持たせ、ユーザー削除依頼でretrieval_runsとreview_decisionsも消えるようにした。伏せ処理は
lib/pii.tsに切り出し、piiDetectorConfig()で検出種別を一箇所にまとめた。ペルソナ抽出や Eval ケース昇格でも保存前に通せる。伏せた結果の対応付けは入力の添字ではなくメッセージ id で引く(strategyをfilter/blockにすると伏せられたメッセージが返り値から落ちて添字がずれる)。PIIDetector には素通りのパスがある。PII を検出しても
redacted_contentを返せなかった場合、handleDetectedPIIが null を返してprocessInputが原文をそのまま push する。warn ログは出るが返り値は成功扱いで呼び出し側から区別できない。電話番号・メール・郵便番号は形式が決まっているので、LLM 判定の後段に正規表現を置いて決定的に伏せる。日本の電話番号は090-1234-5678(3-4-4)から01656-5-3311(5-1-4)まで桁の配分が一定しないため、0 始まりでハイフン区切り 3 ブロックとして扱う。画面側は、会話と検索記録が保管期限で消えている場合にスナップショットを表示する。
8. 訂正が効いたかレビュー画面で確認できなかった
trace を訂正注入の前に書いていたため、
retrieval_runsにcurated/corrections/が入らず、レビュー画面の根拠一覧に訂正が現れなかった。回答本文には反映されているので、村職員から見ると訂正が効いたのか効いていないのか判断できない状態だった。注入後の結果からトレースを組み立て、検索でヒットしなかった source を訂正分として記録する。レビュー画面では訂正の行に「訂正が反映されました」を出す。
9. 一度操作すると戻せなかった
押し間違いを村職員が自力で直せず、開発者に頼るしかない状態だった。
PATCH /admin/corrections/{id}。本文を書き換えて R2 と検索へ再反映し、確認日を更新して要再確認を解除するDELETE /admin/review/{answerRunId}/decision。直近の判断を削除して未判断に戻す。その判断で解決済みにした bad 評価も、他の判断が参照していなければ未解決へ戻すPATCH /admin/source-candidates/{id}/statusにresetを追加。判断者と判断日時を消す回答レビューの一覧はサーバー側でページングしているため検索を付けていない。読み込み済みのページだけを絞り込むと、全件を検索したように見えてしまう。
検証
残っている既知の課題
このPRの変更が残したものだけを書く。
review_decisionsの 1095 日が妥当かは村の監査要件次第。PII を伏せたとはいえ会話内容を持つようになったため、365 日で足りるならその方が安全redactは LLM が返すテキスト全体の書き直しを採用する。オフセット指定ではなく全文置換なので、モデルが無関係な部分を言い換える余地がある