エージェントの作業を1ステップずつ、手元のローカルLLMとクラウドのフロンティアモデルに振り分けるための部品です。 書籍『ローカルLLMで足りる仕事、Claude Codeに任せる仕事』(ken imoto、2026、Kindle) の付属コードです。
Route agent steps between a local LLM and a frontier model. Companion code for the book (Japanese). English summary below.
何が入っているか
| モジュール | 本の章 | 中身 |
|---|---|---|
steprouter/gate.py |
第9〜10章、第14章 | 入口の門番。正規表現 (gitleaks + 日本の個人情報ルール) → ローカルモデルの Y/N 確率 → 線で判定。引っかかったら「伏せて渡す / ローカルで済ませる / 人に戻す」に分ける |
steprouter/jp_rules.py |
第9章 | 携帯番号、口座、住所、生年月日、マイナンバー (検算つき)、カード番号 (Luhn)、「パスワードは〜」 |
steprouter/judge.py |
第6章 | Ollama のネイティブ API で1文字の答えの確率を読む。候補に乗った確率の合計 (mass) を返し、小さい判定は使わない |
steprouter/threshold.py |
第8章 | 自分のラベル付きデータから線を引く CLI。AUROC、線、5分割の交差検証の見逃しと誤検知 |
steprouter/router.py |
第2〜3章、第14章 | ステップの種類のルールで振り分ける。確率の判定はルールで決まらない残りだけ (既定はオフ) |
steprouter/typed_return.py |
第11〜12章、第14章 | ローカルの作業役が返す戻り値の形。検証の数、変えたファイル名、やったことの1〜2行。返す前に正規表現で伏せる |
examples/routing.yaml |
第14章 | 設定ファイルの例 |
data/ |
序章、第7〜10章 | 架空の値で作った評価セット (60件と324件) |
使い方
pip install -e . # Python 3.10+、依存は PyYAML だけ
pytest -q # Ollama なしで通るテスト
# 第9章: 正規表現の門番を評価セットに当てる (gitleaks があれば併用)
python3 scripts/eval_regex.py
# 第7〜8章: 60件をローカルモデルで判定し、線を引く (Ollama 0.12.11 以上)
python3 scripts/judge_set.py qwen3.5:4b en > judged.jsonl
steprouter-threshold judged.jsonl --margin 0.3
入口の門番をコードから呼ぶとき:
from steprouter import gate
r = gate.check(request_text, threshold=0.0071, model="qwen3.5:4b", lang="ja",
definition=open("my_secrets.md").read())
if r.route == "pass": ... # 段取り役 (フロンティア) に渡す
elif r.route == "redact_then_pass": ... # r.text (伏せた文) を渡す
elif r.route == "local": ... # 手元のモデルで済ませる (one_shot=True のとき)
else: ... # 人に戻す (r.hits と r.p_confidential を見せる)
数字について
- しきい値の値や設定ファイルの数字は、著者の環境 (RTX 4070 12GB、Ollama 0.20.2) のものです。自分のデータで引き直してください (第8章)
- 4B の判定役と、書き直した機密の定義の組み合わせは、まだ測っていません。戻り値の形と、戻り値への正規表現の検査も、本の結果からの提案で、そのものは測っていません
- 門番で伏せたあと、伏せた文をもう一度モデルに聞き、線を下回ったときだけ渡します (本文にない安全側の追加です)
データについて
評価セットの値は、すべて乱数で作った架空のものです。ただし形は本物と同じなので、そのまま置くと GitHub の秘密検出に止められます。
data/gate_set.jsonl(324件) はリポジトリに入れていません。data/build_gate_set.pyが乱数の種を固定して生成しますdata/requests60.template.jsonl(60件) は、トークンの接頭辞を{{GHP}}のような置き換えにしてあります。data/load.pyが読むときに戻します
著者自身の依頼やログ (本文の「実データ」の結果) は公開していません。
English summary
gate.check(): entrance gate before a request reaches a frontier orchestrator. Regex (gitleaks + Japanese PII rules) then a local Y/N judge read from Ollama logprobs, blocked at or above your threshold. Blocked requests are redacted and passed, kept local (one-shot tasks), or returned to a human.steprouter-threshold: draw the line from your own labelled examples (lowest P among positives x margin), with AUROC and 5-fold CV.Router: rules by step kind first (e.g. WebFetch -> local); a probability judge only for what rules leave open.typed_return.build(): what a local worker returns (verifier counts, file names, a 1-2 line summary; full log to a file), regex-redacted.- All evaluation data is made up. Numbers in the config are the author's; recalibrate on your own data.
License
MIT
