最近ずっと、AIエージェントが外部操作で止まったあと、その未解決状態を誰へ返すべきかを詰めています。
最初は「危なければ人間へ判断を返せばよい」で足りそうに見えました。
ところが実装してみると、APIの結果確認待ち、route破損、receiver不適格、高影響操作を全部同じ人間向け経路へ押し込むのはかなり粗い。
一見安全そうなのに、むしろ「人間は何を判断すればいいのか」が分かりにくくなります。
Responsibility Pathway Runtime(RPR)0.1.0a6では、この問題を Responsibility Routing として実装しました。
GitHub release:
PyPI:
python -m pip install responsibility-pathway-runtime==0.1.0a6
この記事では、a6で追加したroute modelを、なぜ分けたのか、どう実装したのか、どこまで検証したのかという順番で整理します。
まずRPRが扱っている問題
RPRは、AIエージェントや自動化処理の外部操作を、Authority、Human Gate、実行履歴、結果不明、readback、reconciliation、repair、resume、Residual Ownerまで含む責任経路へ通すMIT LicenseのPython Runtimeです。
以前の記事では、RPRの全体像や、Public Alpha公開後にWindows / UTF-8 BOM不具合をどう閉じたかを書きました。
Zenn:
Qiitaでは、責任経路全体の設計整理はこちらです。
Windows / BOM修正の実例はこちらです。
今回のa6は、その続きです。
ここまで「止める」「確認する」「結果不明を保持する」「修復する」「再開する」を作っていくと、次に残ったのが未解決状態を次の担当へどう渡すかでした。
「fail closed」と「Human Return」は同じではない
外部操作やroutingで異常が起きたとき、先へ進めないようにするのは重要です。
ただし、
fail closed
と、
human return
は同じではありません。
たとえば次のケースを考えます。
1. API writeの結果が確認できない
2. route definitionが壊れている
3. receiverが受取条件を満たさない
4. adapterが落ちた
5. 高影響操作なので人間判断が必要
全部「先へ進めない」という意味ではfail closedです。
でも、5以外まで即座にHuman Returnへ送ると、単なるシステム障害と、人間の判断が本当に必要な状態を区別できなくなります。
そこでa6では、主に次のroute outcomeを分けました。
bounded_human_return
hold_for_reconciliation
neutral HOLD
eligible delegated receiver
stop / preserve
日本語で言えば、
人間に判断を返す
確認待ちで止める
壊れた経路をneutralに止める
条件を満たす別担当へ渡す
未解決状態を保持したまま止める
を別々に扱っています。
route metadataを「宛先」だけにしない
単純なdispatcherなら、routeはdestinationだけでも作れます。
{
"destination": "operator"
}
でも責任経路では足りません。
RPR a6のroute modelは、少なくとも次の情報を持ちます。
receiver eligibility
Authority class
delegation scope
unresolved payload
allowed next actions
closure / reevaluation condition
optional expiry
Residual Owner
実装上の意味は、次のようになります。
誰なら受け取れるか
何をしてよいか
何が未解決か
次に何だけ許されるか
いつ再評価するか
元の責任主体は誰か
つまり、routeは「どこへ送るか」だけではなく、何を未解決のまま、どんな境界付きで渡すかまで持ちます。
a6へ移行した際の互換設計ベースラインは、履歴記録として保存しています。
receiver capabilityからAuthorityを推測しない
ここは実装時にかなり重要でした。
capability != authority
あるreceiverが技術的にreconciliationできるからといって、そのreceiverがreconciliationを実行してよいとは限りません。
同様に、
route selected != authority granted
visible != authorized
evidence present != authority granted
です。
RPRでは、route destination、receiver capability、evidence、visibilityからexecution / reconciliation Authorityを推測しません。
この原則はMCP連携でも維持しています。
MCP公式仕様では、Toolsはserverが公開する実行可能な機能として定義されています。
ただし、Toolが公開されていることと、そのToolを特定の業務文脈で実行してよいことは別です。
RPRのread-only MCP inspectionでは、route visibilityを次のtoolで取得できます。
rpr.get_route_visibility(pathway_id)
出力上も、Authorityを自動推論しないことを明示します。
{
"authority_inferred": false
}
現行MCP integration:
単なる障害を、誤って「人間へ責任を返すべき状態」にしない
実装中に特に避けたかったのがこれです。
routeが壊れた。
receiverが条件を満たさなかった。
adapterが落ちた。
それだけで「では人間へ返そう」とすると、一見fail closedには見えます。
でも、それは停止と責任移転を同じものとして扱っています。
現在は、次のようなgeneric failureを原則neutral HOLDとして扱います。
route definition invalid
receiver eligibility failure
RPE unavailable
adapter error
REST unavailable
RPE contract mismatch
まず止める。
原因を確認する。
必要ならrouteを修復する。
そのうえでreevaluationする。
error
↓
HOLD
↓
原因確認 / route修復 / reevaluation
高影響操作など、人間判断が独立に必要な場合だけbounded Human Returnへ送ります。
write result unknownはreconciliationへ寄せる
RPRが以前から扱っている write_status_unknown は、a6では互換route上 hold_for_reconciliation として扱います。
外部write直後に通信が切れた場合、先にやるべきことは「人間に全部投げる」ことではありません。
まず外部状態をreadbackして、何が起きたのかを確定する必要があります。
write dispatched
↓
response lost
↓
write_status_unknown
↓
hold_for_reconciliation
↓
readback
↓
confirmed / repair_required / unresolved
ここで再送を先にすると、二重登録の可能性があります。
逆に成功として閉じると、証拠のない完了記録になります。
だから、結果が分からない状態を結果不明のまま保持します。
Human Returnはboundedにする
Human Return自体は必要です。
ただしa6では、generic escape hatchにはせず、bounded Human Returnとして扱います。
最低限、
identified receiver
unresolved state
allowed next actions
closure / reevaluation condition
を残します。
「人間に返した」でtaskを完了扱いにはしません。
returned to human != resolved
です。
人間が受け取った時点でも、何が未解決か、何を判断する必要があるか、次に何をしてよいかが残っている必要があります。
route destinationとResidual Ownerを分ける
もう一つ重要なのがownershipです。
別receiverへrouteしたからといって、Residual Ownerまで自動変更しません。
route destination changed
!=
Residual Owner changed
たとえばreconciliation担当へ一時的に調査を渡しても、元の責任主体が消えるわけではありません。
人間同士でも、「調査をお願いした」と「責任者を変更した」は別ですよね。
RPRでも同じように分けています。
ownershipを変更するなら、それは別途認可された変更として扱います。
この分離がないと、handoffするたびに責任主体が暗黙に移動してしまいます。
read-only visibilityで何を確認できるか
Responsibility Routingはinspection surfaceから確認できます。
目的はrouteを変更することではなく、現在のroute declarationを外から検査できるようにすることです。
確認対象は例えば次です。
current destination
receiver eligibility
Authority class
delegation scope
unresolved payload
allowed next actions
closure / reevaluation condition
Residual Owner
authority_inferred == false
このinspectionはstate mutationを行わない前提でテストしています。
browser E2Eでも同じRuntimeを使う
a6では英語・日本語のbrowser surfaceにもResponsibility Routingを追加しました。
ここは画面専用mockではありません。
CIでbuildしたRPR wheelをPyodideへ読み込み、browser内でRuntime本体を動かします。
Responsibility Routingに加えて、経路状態、Evidence、restart / reconciliation、重複dispatch防止も確認できます。
release前にどこまで検証したか
0.1.0a6のrelease evidenceでは、routingだけのfocused testで終わらせていません。
確認範囲には次が含まれます。
unit
component
integration
product
browser / Pyodide E2E
API / MCP / CLI
persistence / recreation
EN / JA public surface
claim / test registry
clean wheel installation
reproducible artifact verification
Lean 4 / cross-model parity
standalone suiteは477 tests、production-grade demo testsは4 testsが通過しています。
詳細:
verification surface:
ただし、ここから次を主張しません。
production-ready
enterprise-ready
legal / compliance certified
all customer environments verified
universal exactly-once
full formal verification
テストが多いことと、保証範囲が無限に広がることは別です。
PyPI Trusted Publishingとattestation
0.1.0a6はGitHub prereleaseとPyPIに公開済みです。
PyPI publicationはTrusted Publishing経路を利用し、公開artifactにはdigital attestationsがあります。
PyPI公式docs:
Trusted PublishingはOIDCベースの短命credentialを使う公開方式です。
PyPIのattestationは、各wheel / sdistをcryptographic digestとpublisher identityへ結び付けます。
ここもclaim boundaryを分けます。
artifact provenance evidence
!=
software safety proof
つまり、attestationが示すのは公開経路・artifact identityに関する証拠です。
softwareそのものの安全性まで証明するわけではありません。
0.1.0a6を触る
固定versionで入れるなら次です。
python -m pip install responsibility-pathway-runtime==0.1.0a6
rpr --help
rpr-mcp --help
GitHub:
Release:
PyPI:
Japanese docs:
まとめ
RPR 0.1.0a6で追加したResponsibility Routingは、単に次の担当者を決めるdispatcherではありません。
未解決状態
↓
receiver eligibilityを確認
↓
Authorityを推測せず保持
↓
許されたnext actionだけ渡す
↓
closure / reevaluation conditionを残す
↓
Residual Ownerを保持
という境界付きhandoffです。
今回いちばん大事だった整理はこれでした。
fail closed != human return
capability != authority
route selected != authority granted
returned to human != resolved
route destination changed != ownership changed
AIエージェントの外部操作を止めるだけなら、Human Gateだけでも一定の効果はあります。
でも、止まったあとに誰が何を判断するのかまで扱うなら、それだけでは足りません。
今回のa6では、失敗を全部「人間へ戻す」に丸めず、未解決状態を境界付きで次へ渡せるようにしました。
実装してみて分かったのは、止めることよりも、そのあとをどう残すかの方が難しい、ということでした。