Skip to content

fix: HIL の設計の欠落 4〜9 を修正する - #1120

Draft
owk-owk130 wants to merge 16 commits into
fix/hil-review-findingsfrom
fix/hil-design-gaps
Draft

fix: HIL の設計の欠落 4〜9 を修正する#1120
owk-owk130 wants to merge 16 commits into
fix/hil-review-findingsfrom
fix/hil-design-gaps

Conversation

@owk-owk130

@owk-owk130 owk-owk130 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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. 情報源の停止が訂正に伝播しなかった

承認を取り消した情報源への訂正が公開中のまま残り、廃止した情報の訂正が回答に注入され続けていた。計画時に「元ソースの失効」を想定していなかった。

  • 承認が外れた時点で紐づく訂正を要再確認にする
  • 検索対象外の情報源に対する新規の訂正発行を 400 で拒否する
  • 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_runsreview_decisions も消えるようにした。

伏せ処理は lib/pii.ts に切り出し、piiDetectorConfig() で検出種別を一箇所にまとめた。ペルソナ抽出や Eval ケース昇格でも保存前に通せる。伏せた結果の対応付けは入力の添字ではなくメッセージ id で引く(strategyfilter / 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_runscurated/corrections/ が入らず、レビュー画面の根拠一覧に訂正が現れなかった。回答本文には反映されているので、村職員から見ると訂正が効いたのか効いていないのか判断できない状態だった。

注入後の結果からトレースを組み立て、検索でヒットしなかった source を訂正分として記録する。レビュー画面では訂正の行に「訂正が反映されました」を出す。

9. 一度操作すると戻せなかった

押し間違いを村職員が自力で直せず、開発者に頼るしかない状態だった。

追加したもの
訂正の修正 PATCH /admin/corrections/{id}。本文を書き換えて R2 と検索へ再反映し、確認日を更新して要再確認を解除する
判断の取り消し DELETE /admin/review/{answerRunId}/decision。直近の判断を削除して未判断に戻す。その判断で解決済みにした bad 評価も、他の判断が参照していなければ未解決へ戻す
候補の未判断化 PATCH /admin/source-candidates/{id}/statusreset を追加。判断者と判断日時を消す
一覧の検索 訂正・情報源・情報源候補の一覧に検索を追加

回答レビューの一覧はサーバー側でページングしているため検索を付けていない。読み込み済みのページだけを絞り込むと、全件を検索したように見えてしまう。

検証

  • server 1588 tests / web 736 tests / lint 0
  • migration 0001〜0034 をローカル D1 に空から適用して確認

残っている既知の課題

このPRの変更が残したものだけを書く。

  • review_decisions の 1095 日が妥当かは村の監査要件次第。PII を伏せたとはいえ会話内容を持つようになったため、365 日で足りるならその方が安全
  • 氏名と住所は決定的に伏せられないため LLM 判定に依存する。日本語での検出精度は未検証
  • redact は LLM が返すテキスト全体の書き直しを採用する。オフセット指定ではなく全文置換なので、モデルが無関係な部分を言い換える余地がある

owk-owk130 and others added 5 commits September 2, 2026 10:38
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
情報源の承認 API に対応する画面が無く、村職員が管理画面だけで承認フローを
完結できなかった。未承認・公開中・停止中・却下済みで絞り込み、承認と停止を
確認ダイアログ付きで実行できる画面を追加する。

訂正一覧では要再確認の理由を区別して表示し、回答レビューでは会話と検索記録が
保管期限で消えている場合に判断時のスナップショットを表示する。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018BjNFvQjDzMwF8HaZHnJjP
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for server

Status Category Percentage Covered / Total
🟢 Lines 98.08% (🎯 97%) 3954 / 4031
🟢 Statements 97.55% (🎯 96%) 4150 / 4254
🟢 Functions 97.82% (🎯 97%) 764 / 781
🟢 Branches 89.7% (🎯 87%) 1629 / 1816
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
server/src/db/schema.ts 100% 100% 100% 100%
server/src/lib/pii.ts 96.55% 75% 92.85% 96%
server/src/repository/knowledge-correction-repository.ts 90% 75% 85.71% 89.47%
server/src/repository/review-repository.ts 80% 85.71% 75% 79.41%
server/src/repository/source-candidate-repository.ts 100% 75% 100% 100%
server/src/routes/admin/corrections.ts 100% 92.3% 100% 100%
server/src/routes/admin/review.ts 100% 80.55% 100% 100%
server/src/routes/admin/source-candidates.ts 100% 100% 100% 100%
server/src/routes/admin/knowledge/convert.ts 97.14% 85.71% 100% 97.05%
server/src/routes/admin/knowledge/files.ts 100% 83.33% 100% 100%
server/src/routes/admin/knowledge/index.ts 100% 100% 100% 100%
server/src/routes/admin/knowledge/sources.ts 96.29% 87.5% 100% 98%
server/src/routes/admin/knowledge/sync.ts 100% 50% 100% 100%
server/src/services/review-evidence.ts 100% 100% 100% 100%
server/src/services/user-deletion.ts 100% 100% 100% 100%
server/src/services/knowledge/indexing.ts 100% 100% 100% 100%
server/src/services/knowledge/search.ts 95.12% 100% 90% 94.73%
Generated in workflow #2113 for commit bc3a449 by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for shared

Status Category Percentage Covered / Total
🟢 Lines 99.29% (🎯 98%) 283 / 285
🟢 Statements 80.31% (🎯 80%) 302 / 376
🟢 Functions 100% (🎯 98%) 97 / 97
🟢 Branches 62.25% (🎯 62%) 127 / 204
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
shared/src/api/types.ts 100% 100% 100% 100%
shared/src/api/repository/correction-repository.ts 76.92% 50% 100% 100%
shared/src/api/repository/knowledge-repository.ts 79.03% 50% 100% 100%
shared/src/api/repository/review-repository.ts 77.77% 73.33% 100% 100%
shared/src/api/repository/source-candidate-repository.ts 80% 50% 100% 100%
Generated in workflow #2113 for commit bc3a449 by the Vitest Coverage Report Action

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
owk-owk130 and others added 5 commits September 2, 2026 11:05
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
@owk-owk130 owk-owk130 changed the title fix/hil design gaps fix: HIL の設計の欠落 4〜9 を修正する Sep 2, 2026
owk-owk130 and others added 5 commits September 2, 2026 11:51
# 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
削除系の DB 操作を deleteWithCount 経由に揃える

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014kkNoEdkn1kVK4La79XFpc
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for web

Status Category Percentage Covered / Total
🟢 Lines 97.68% (🎯 96%) 1901 / 1946
🟢 Statements 96.59% (🎯 95%) 2016 / 2087
🟢 Functions 96.43% (🎯 95%) 812 / 842
🟢 Branches 89.47% (🎯 88%) 1317 / 1472
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
web/src/app/dashboard/components/CorrectionsPanel.tsx 95.12% 91.37% 92.3% 97.43%
web/src/app/dashboard/components/SourceCandidatesPanel.tsx 100% 81.81% 100% 100%
web/src/app/dashboard/components/SourcesPanel.tsx 100% 88.88% 100% 100%
web/src/app/dashboard/components/corrections/CorrectionEditor.tsx 100% 85.71% 100% 100%
web/src/app/dashboard/components/knowledge/sources/SourceRow.tsx 100% 80% 100% 100%
web/src/app/dashboard/components/knowledge/sources/helpers.ts 100% 100% 100% 100%
web/src/app/dashboard/components/review/ArchivedEvidence.tsx 100% 80% 100% 100%
web/src/app/dashboard/components/review/ReviewDetailModal.tsx 100% 85.41% 100% 100%
web/src/app/dashboard/components/review/helpers.ts 100% 100% 100% 100%
web/src/app/dashboard/hooks/keys.ts 93.33% 100% 92.85% 93.33%
web/src/app/dashboard/hooks/useCorrections.ts 100% 100% 100% 100%
web/src/app/dashboard/hooks/useKnowledgeSources.ts 100% 100% 100% 100%
web/src/app/dashboard/hooks/useReview.ts 96.29% 85.71% 100% 100%
web/src/components/ui/SearchBox.tsx 100% 100% 100% 100%
Generated in workflow #2113 for commit bc3a449 by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for widget

Status Category Percentage Covered / Total
🟢 Lines 99.62% (🎯 98%) 265 / 266
🟢 Statements 97.94% (🎯 96%) 286 / 292
🟢 Functions 98.11% (🎯 98%) 52 / 53
🟢 Branches 93.28% (🎯 92%) 139 / 149
File CoverageNo changed files found.
Generated in workflow #2113 for commit bc3a449 by the Vitest Coverage Report Action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant