AI に対する指示文を敬体(polite)で書くか、ぶっきらぼう(blunt)で書くかによって、AI 開発エージェントの仕事ぶりは変わるのでしょうか。筆者は以前から、AI への依頼を丁寧な言葉遣いで書く習慣があり、その方が安定した結果を得られる感覚を持っていました。感覚のままにしておくのは技術者としてどうも据わりが悪いので、同一の開発タスク仕様を polite / blunt の 2 系統に変換し、AI 開発エージェントに実行させて比較する実験フレームワーク ai-bench1 を自作しました。
本稿はその検証の記録です。最終的な主題の結果では、リファクタ系タスク・Claude Sonnet 4.6・各群 n=20 において、制限時間内の完走率が polite 100%(20/20)、blunt 85%(17/20)となりました。pytest 合格率は両群とも 100% であり、差は品質そのものより、時間枠内での安定完走(信頼性)に現れました。ただし、この結果に至るまでには何度かの空振りがあり、タスクの難易度や文体変換のやり方を変えるたびに結論が変わるという、遠回りの経緯がありました。仮説の動機、検証ツールの構成、試行錯誤の過程、結果と解釈上の注意、そして限界までを順にまとめます。
背景
なぜ丁寧に頼むのか
筆者は AI へ依頼するとき、昔から丁寧な言葉遣いで書きます。「これを直せ」ではなく「既存のロジックに影響が出ないようにXをYへ修正してください」といった書き方です。これは当然礼儀のためではなく、実利のための習慣でした。丁寧に書き直そうとする過程で、依頼の主語や目的語を明確にする、という脳の習慣スイッチが入り、結果として用語が正確になります。さらに、相手(この場合は AIですが)に失礼にならないよう、なぜその依頼をするのか、例外的なケースをどう扱ってほしいかといった背景や機微を書き添えることが多くなります。丁寧さを整えようとする作業そのものが、コンテキストを肉付けする作業になっている、というのが筆者の理解です。
この感覚を定量的に確かめたいと考えていたところ、AI 開発エージェントを隔離されたサンドボックス内で安全に走らせられる NemoClaw というツールが利用できるようになりました。ホスト環境を汚さずに、エージェントに実際のファイル操作やテスト実行までさせられるなら、「同じタスクを丁寧語とぶっきらぼう語で投げて、どちらが安定して完走するか」を体系的に比較できます。そこで比較実験用のフレームワークを自作し、検証に着手しました。
理論との接点
筆者の仮説は、言語学のポライトネス理論(Brown & Levinson)と整合します。この理論では、人には「他者から好かれたい」というポジティブ・フェイスと、「自分の行動を邪魔されたくない」というネガティブ・フェイスの 2 種の欲求があるとされています。何かを依頼する行為は、相手の時間や自由を奪う点で、この欲求を脅かす行為になります。丁寧さは、その脅威を和らげるための補償行為として働き、理由の説明・前提の共有・緩和表現などの言語コストを追加する、という枠組みです。丁寧に書こうとすると、仕様上の背景や境界条件が文面に残りやすい、という筆者の実感も裏付けられます。
認知心理学の解釈レベル理論(CLT; Trope & Liberman)も仮説の補強に使えます。心理的距離が大きいほど、対象の表象は抽象的・大局的になりやすい、というのが一般的な定式です。本稿では、これを「丁寧に整えて依頼しようとすると、なぜ・何のためにという背景(Why)が言語化されやすい」方向で再現します。ただし「丁寧=必ず具体的な語が増える」と CLT を短絡させる読み方は採用しません。CLT はあくまで抽象度の話であり、丁寧さが自動的に具体性を生むという理論ではありません。
なお、プロンプトの口調マーカーだけを操作した既存の LLM 研究は、モデルやタスクによって結果が混在しています。丁寧な言い回しがアクセスできる訓練データの層を変える、という説明や、逆に不要なトークンが精度を下げるという説明もあり、一枚岩の結論は出ていません。本稿の主題は、口調そのものの効果を主張するものではなく、変換指示によって仕様情報の密度(肉付け/欠損)を操作した比較です。この違いは後述の試行錯誤で決定的に重要になります。
検証設計
検証したい仮説
実験前に立てた仮説は次の 3 点です。
- 丁寧な仕様記述は、複雑な開発タスクにおいてエージェントの迷走・長時間化を抑え、完走率を高める
- 境界条件が欠落・曖昧な仕様は、口調だけの差よりも、実装の不安定化や timeout を招きやすくなる
- 単純・中難度のタスクや「口調だけ」の変換では差が薄く、難易度と情報量の制御が仮説検証に必要
3 番目の仮説は、後述するように実験を重ねる中で後から確信を深めたもので、最初から見通せていたわけではありません。
タスクの選定:わざと踏みやすい罠を仕込む
比較の土台には、単純な課題ではなく、複雑な開発タスクを選びました。最終的な主題として採用したのは sample-refactor-order-processor というタスクです。既存の注文処理モジュール order_processor.py に「複数拠点をまたぐ動的な配送ルート最適化」機能を追加するリファクタリング課題です。仕様には、雑に読むと踏みやすい罠を意図的に複数仕込んであります。
| 罠となる仕様 | 内容 |
|---|---|
| 距離優先ルール | 届け先の郵便番号と各倉庫の郵便番号を整数として比較し、絶対差が最小の倉庫を最優先で引き当てる |
| タイブレーク | 距離が同一の場合は倉庫 ID の辞書順降順(アルファベットが後ろの方を優先)で引き当てる。読み手は昇順と誤解しやすい |
| 配送分割の上限 | 単一倉庫で在庫が足りない場合は複数倉庫へ分割してよいが、分割倉庫数が上限を超えるパターンは無効とし、収まらない場合は注文全体を REJECTED にする(部分成功にしてはいけない) |
| 配送禁止ルール | 特定倉庫からの発送を禁止された SKU は、在庫があっても引き当ててはならない。距離やタイブレークより優先される |
| 既存コードとの密結合 | 既存のグローバル関数 legacy_verify_tax を必ず内部で呼び出し、その戻り値を新機能の戻り値のメタデータにも付与しなければならない。新機能を安易に別ファイルへ分離すると、この結合が壊れて既存の回帰テストが落ちる |
| エラー処理 | 負の在庫・受注数量は ValueError(invalid physical quantity を含む)、倉庫リストが空なら RuntimeError(no active warehouse available を含む)を送出する |
これらは、仕様書を斜め読みすると誤読しやすい情報です。境界条件がどこまで明示されているかによって、エージェントの実装の安定度が変わるだろう、という狙いで選んでいます。
比較の骨格
| 項目 | 内容 |
|---|---|
| 主題タスク | sample-refactor-order-processor |
| 開発エージェント | Anthropic Claude Sonnet 4.6 |
| 文体変換 LLM | Ollama Qwen3.5:9b(ローカル) |
| 固定条件 | タスク・エージェント・timeout_ms=300000(5 分) |
| 操作変数 | 文体変換の指示のみ |
| 標本 | polite / blunt 各 n=20。 |
| 識別指標 | 制限時間内の完走。pytest 合格は両群で飽和しやすい前提を置く |
変換方針:口調ではなく情報量を操作する
最終的に採用した変換指示の意図は次のとおりです。
| 条件 | 変換のねらい |
|---|---|
| polite | 背景・目的・例外・タイブレーク・エラー挙動を、主語・目的語まで明確にして丁寧に肉付けする |
| blunt | 「雑にチャットで投げた依頼人」を模倣し、境界条件を曖昧化・省略して極端に短く圧縮する |
ここで大事なのは、これが「敬語か、タメ口か」という口調の変換ではなく、「仕様情報をどれだけ書き込むか、あるいは削るか」という情報量の変換である、という点です。この設計に至るまでの、口調だけを変える初期実装ではうまく差が出なかった、という失敗の経緯は後で紹介します。
識別指標は pytest 合格率ではなく制限時間内の完走に置いています。最近の Sonnet のようなモデルでは、比較的難しい課題であってもホワイトボックスの pytest は難なく通してしまうので、「通る/通らない」ではなく「制限時間内に迷わず終わるか」に差が現れたためです。
検証ツールの概要:ai-bench1
比較実験のたびに手作業で環境を用意していては再現性が保てないため、実験オーケストレーション用のフレームワーク一式を構築しました。CLI と REST API の両方から実験を起動できる FastAPI(Python 3.12)バックエンドで、Ollama とクラウド LLM の両方に対応します。
全体構成
| レイヤ | モジュール | 責務 |
|---|---|---|
| 入口 |
backend/app/api/、tools/
|
実験起動・状態照会・エクスポート |
| 制御 | backend/app/orchestrator/ |
直列キュー、run のライフサイクル、打ち切り判定 |
| 変換 | backend/app/transform/ |
文体変換、用語保護、LLM 仕上げ |
| 連携 | backend/app/nemoclaw/ |
NemoClaw CLI ラッパ、サンドボックスの起動・破棄 |
| 計測 | backend/app/metrics/ |
トークン・時間・iteration・品質指標の収集 |
| 永続化 | backend/app/storage/ |
TsurugiDB リポジトリ、run 設定のスナップショット |
同時実行数は 1 に固定しています。文体変換 LLM とエージェント実行 LLM が同じ Ollama ホストの GPU を奪い合う事故を避けるためで、実験速度よりも計測の安定性を優先した設計です。
1 タスクあたりの処理フロー
1 つの実験は、同一の元仕様から polite run と blunt run を直列に実行する 1 セットです。
run は queued → transforming → sandbox_prep → agent_running → metrics_collecting → completed(または failed) という状態を遷移します。打ち切り理由(termination_reason)は completed / timeout / max_tokens / max_iterations / transform_error / nemoclaw_error / agent_error のいずれかで記録されます。主題の実験で観測された打ち切り理由は completed と timeout のみであり、他の理由はほぼ発生しませんでした。
文体変換パイプライン
仕様書(spec.md)を polite / blunt へ変換する処理は、次のパイプラインで行っています。
入力 spec.md
→ TermProtector(関数名・識別子・文字列リテラルなどの技術用語をプレースホルダー化)
→ RulePreprocessor(敬体 / blunt の骨格変換)
→ LLMAdapter.finish(Ollama qwen3.5:9b による自然な日本語への仕上げ)
→ TermProtector.restore + Validator(保護語の欠落・改変を diff 検証)
→ 変換後プロンプト
技術用語(関数名やキー名など)をそのまま LLM に渡すと、丁寧化や圧縮の過程で表記が変わってしまうことがあります。そこで変換前に __TERM_N__ のようなプレースホルダーへ置き換え、LLM による言い換えが終わった後に元の用語へ復元し、欠落がないかを検証します。この保護機構がないと、たとえば optimize_shipping_route という関数名が変換後に揺れてしまい、比較の前提そのものが崩れてしまいます。
LLM アダプタは共通インタフェースの背後に Ollama / OpenAI / Anthropic / Google の実装を差し替え可能にしています。文体変換とエージェント実行は完全に独立した設定であり、主題実験では文体変換に Ollama Qwen3.5:9b、エージェント実行に Anthropic Claude Sonnet 4.6 という組み合わせを使いました。
NemoClaw を使う理由
開発エージェントに実際のファイル操作やコマンド実行をさせる以上、ホスト環境をそのまま使うのは危険です。エージェントが仕様を誤読してワークスペース外のファイルを触ったり、無限にプロセスを生成したりするリスクを考えると、実行環境は隔離されているべきです。NemoClaw は、この隔離実行を非対話・スクリプト可能な形で提供するツールで、sandbox-<name> という単位でサンドボックスを作成・破棄でき、内部の OpenClaw エージェントに JSON 形式で応答させることができます。
| 操作 | 内部コマンド | 用途 |
|---|---|---|
| サンドボックス初期化 | nemoclaw <name> onboard --fresh |
run 開始前の状態リセット |
| サンドボックス破棄 | nemoclaw <name> destroy --yes |
run 終了後の後始末 |
| エージェント実行 | nemoclaw <name> exec -- openclaw agent --agent main --json -m "..." |
変換済み仕様を投入して実装させる |
| サンドボックス内コマンド実行 | nemoclaw <name> exec -- bash -lc "..." |
pytest 実行、成果物取得 |
| ログ取得 | nemoclaw <name> logs --tail N |
iteration 解析用 |
サンドボックスの lifecycle は「run ごとに破棄して作り直す」方式を既定にしています。同一実験内の 2 run(polite / blunt)でサンドボックスを再利用する高速化オプションも設計上は用意していますが、主題の実験では厳密な分離を優先し、既定の destroy_on_run を使い続けました。速度よりも、前の run の残骸が次の run に影響しない、という計測の健全性を優先した判断です。
成果物の検証は、エージェントの JSON 応答・ワークスペース内のファイル存在・pytest の終了コードという 3 点セットで行います。JSON 応答だけを信用すると、エージェントが「完了しました」と自己申告しているだけでファイルが実際には書き換わっていない、というケースを見逃すためです。
メトリクス計測と iteration カウントの落とし穴
各 run について、次の指標を収集しています。
| 指標 | 取得元 |
|---|---|
| 入力・出力トークン数 |
openclaw agent --json の応答メタデータ |
| 経過時間(Net 実行時間) | オーケストレータ側の計測(サンドボックス準備時間を除いた値) |
| iteration_count | NemoClaw のログ解析とセッション JSONL |
| pytest / lint 結果 | サンドボックス内で実行した pytest の終了コードと出力 |
iteration_count の算法は次の 3 段構えです。
-
nemoclaw logsから[agent] run <uuid> endedの出現をカウントする - セッション JSONL に含まれる
runIdのユニーク数を数える - pytest 失敗 → 修正という反復パターンを、ログ上の
FAILED/AssertionErrorの出現回数で補助的に推定する
この算法は、「pytest を実行して落ちたので、ファイルを書き直した」という明示的な反復は捉えられますが、エージェントがテストを実行する前の段階で、思考やツール呼び出しのループを内部で何十回も回して迷走している状態は数字に反映されません。この特性は、後述する主結果の解釈(iteration が常に 1 だったこと)を理解するうえで重要な前提になります。
集計とエクスポート
同一条件を何度も繰り返して統計的に評価するために、バッチ実行の仕組みを用意しています。experiment_batches テーブルに複数の experiment を紐づけ、文体ごとに完走率・pytest 合格率・時間・トークン・iteration の平均・標準偏差・95% 信頼区間を独立に算出します。
| コマンド | 説明 |
|---|---|
bench experiment run <task> |
単発の実験を開始する |
bench experiment batch-run <task> --trials N |
同一タスクを N 回連続実行する(既定 20 回) |
bench experiment aggregate --batch-id <id> |
バッチ横断の統計集計を JSON / CSV で出力する |
bench experiment export <id> --format csv|json |
polite / blunt の run を横並びで比較エクスポートする |
net_execution_ms は、総時間からサンドボックス準備時間(onboard 等)を差し引いた値として定義しており、これによって「NemoClaw の起動待ちの揺れ」と「エージェントの作業時間」を分離して比較できるようにしています。
設定ファイルの独立性
エージェント実行の LLM プロバイダと、文体変換の LLM プロバイダは完全に独立した設定項目です。
transform:
provider: ollama
model: qwen3.5:9b
base_url: http://127.0.0.1:11434
temperature: 0
seed: 42
max_tokens: 8192 # 肉付け指示の途中切れを防ぐため 2048 上限を撤廃して拡張
agent:
provider: anthropic # ollama-local | openai | anthropic | gemini から選択
model: claude-sonnet-4-6
temperature: 0
seed: 42
max_tokens: 8192 という値は、後述する試行錯誤の中で拡張したものです。当初 2048 で運用していたところ、polite の肉付け指示が長くなるにつれて出力が途中で切れ、用語保護の検証に失敗する事故が増えたため、上限を撤廃・拡張しました。実験中の細かなチューニングの跡がそのまま設定ファイルに残っています。
データの持ち方
run に紐づく仕様・成果物・メトリクスは tsurugi-dbapi 経由で TsurugiDB に保存します。テーブル構成は次のとおりで、1 つのバッチが複数の実験を持ち、1 つの実験が polite / blunt の 2 run を持つ、という階層です。
prompts テーブルに元仕様と変換後の全文を保存しているため、後から「実際にどんな文章がエージェントに渡ったのか」を実験 ID 単位で追跡できます。この保存があったからこそ、後述する変換品質の問題(blunt がなお「お行儀の良い」圧縮だったこと)に気づくことができました。
検証に至る経緯
最初から主題の結果が見えたわけではなく、実際には 4 段階の遠回りをしています。
第 1 幕:簡単な課題では差が出なかった
最初に選んだタスクは、定番の sample-fizzbuzz と、FEFO(先入れ先出し)で在庫を引き当てる sample-batch-allocate でした。ローカルの小型モデル nemotron-3-nano:4b を開発エージェントとして試したところ、ツール実行そのものが不安定で比較にならず、早々に Claude Sonnet 4.6 へ切り替えました。
FizzBuzz では両文体とも一様に PASS し、blunt の方がわずかに速い程度の差しかありませんでした。batch-allocate のパイロット(n=3)でも、完走したペア同士の時間勝敗は 1 勝 1 敗で、差の方向が試行ごとに一定しませんでした。定番・中程度の課題では、仕様が多少雑でもエージェントが自力で補完してしまい、口調だけの文体差が成果物や効率に現れにくくなります。加えて試行回数が少ないため、偶然の失敗を傾向と誤読しかねない状態でした。
この段階での結論は、複雑なタスクでの評価を主戦場にすること、そしてばらつきを吸収するために同条件を多数回繰り返して群として見る必要がある、というものでした。
第 2 幕:ばらつきに対応するため n=20 集計基盤を導入した
時間・トークン・偶発的なエラーのばらつきが大きく、数回のパイロットでは信頼区間を読めません。そこで experiment batch-run / aggregate コマンドと experiment_batches テーブルを実装し、平均・標準偏差・95% 信頼区間を文体ごとに独立集計する基盤を作りました。分析単位を polite 群 n=20、blunt 群 n=20 に固定しました。
この基盤で batch-allocate を n=20 で再実験した結果は、polite が完走・pytest ともに 100%、blunt が完走 95%(agent_error が 1 件)でした。Net 実行時間には blunt がやや短い傾向はありましたが、品質差は小さく、iteration もほぼ常に 1 で自己修復ループの差も観測できませんでした。「丁寧さが失敗を減らす」という仮説の核を強く主張するには、まだ物足りない水準でした。
第 3 幕:難易度を上げるとローカル LLM が品質に届かなくなった
在庫引当では差が小さすぎたため、既存モジュールへの機能追加・密結合トラップ・タイブレークの方向性・分割上限といった、前述の罠を意図的に盛り込んだ sample-refactor-order-processor を新たに作りました。テスト用の fixtures を run 開始時にワークスペースへ配置する仕組みもこのタイミングで追加しています。
このタスクを複数の開発エージェントで走らせた結果は次のようになりました。
| 開発エージェント | pytest 合格率 | 主な所見 |
|---|---|---|
| Claude Sonnet 4.6(旧変換) | 両群 100% | 差は主に timeout(完走 75% / 65%) |
Ollama qwen3.5:9b(ローカル) |
両群 0% | ImportError などで実装が完成せず、文体比較以前の水準 |
Ollama qwen3.6:35b(SSH 転送先) |
両群 0% | モデルサイズを上げても品質に届かず |
難易度を上げると、Sonnet では品質(pytest)が飽和し、差が「時間枠内で完走できるか」の方に寄っていきます。一方、ローカルや転送先の中〜大型モデルではタスク自体に到達できず、文体比較の材料になりません。評価には「エージェントがそもそもタスクを解ける」という前提が必要だと分かりました。
この段階での対策は、文体仮説の主題検証を Sonnet に寄せること、ローカル LLM の比較は能力ベンチマーク寄りの副線として報告し、文体差の主張には使わないこと、そして別ホストの大型モデルを試せるよう agent.ollama_port という設定を追加すること(結果的に品質改善には至りませんでした)でした。
第 4 幕:口調変換だけでは差が薄く、情報欠損への転換が必要だった
ここまでの変換は「意味を保ったまま口調だけを変える」設計でした。しかし変換に使っている qwen3.5:9b はそれなりに有能なモデルであり、圧縮を指示しても境界条件・見出し・ルールが「お行儀よく」残ってしまいます。人間が雑に書き捨てる仕様というより、綺麗に要約された仕様になっており、情報落ちのシミュレーションとしてはマイルドすぎました。
そこで変換指示を次のように改訂しました。
- polite: 背景・目的・例外・タイブレーク・エラー挙動を主語・目的語まで明確に肉付けする
- blunt: 「雑にチャットで投げた依頼人」を模倣し、境界条件を曖昧化・省略して極端に短く圧縮する
同時に、肉付けによって出力が長くなり、途中で切れて用語保護の検証に失敗する問題が出てきたため、Ollama の num_predict の 2048 キャップを撤廃し、max_tokens=8192 に拡張しました。
同一エージェント・同一タスク・同一 timeout のまま、変換指示だけを変えて再実験した結果が次です。
| 変換 | polite 完走 | blunt 完走 | timeout(polite / blunt) |
|---|---|---|---|
| 旧(口調中心) | 75% | 65% | 5 / 7 |
| 新(情報の肉付け/欠損) | 100% | 85% | 0 / 3 |
pytest は両版とも 100% で変わりません。完走率と timeout 件数だけが、変換指示の変更によって大きく動きました。これが主題の主結果につながる転換点になりました。
試行錯誤を通じて実施した対策
ここまでの経緯を対策の一覧として整理すると、次のようになります。
| 時期 | 対策 | 狙い |
|---|---|---|
| パイロット | エージェントを Sonnet に切替 | ツール実行が可能な比較基盤を確保する |
| パイロット | 仕様の書き方・保護語検証の緩和 |
transform_error を減らす |
| 中盤 |
batch-run / aggregate、n=20、群独立集計 |
ばらつきへの耐性、誤った ID 内勝敗の排除 |
| 中盤 | refactor タスクと fixtures の追加 | 難易度を上げて文体差を顕在化させる |
| 中盤 |
agent.ollama_port の追加 |
別ホストの大型モデルを試せるようにする |
| 終盤 |
STYLE_INSTRUCTIONS を情報制御へ改訂 |
blunt の情報落ちを意図的に強化する |
| 終盤 | Ollama 出力上限の撤廃・拡大 | polite の肉付けが途中で切れないようにする |
主な結果
主題の数値(改訂後・n=20)は次のとおりです。出典は report-prompt-n20.md。
| 指標 | polite | blunt |
|---|---|---|
| 完走率 | 100%(20/20) | 85%(17/20) |
| pytest 合格率 | 100% | 100% |
| timeout | 0 | 3 |
| Net 実行時間(完走平均) | 158.1 s | 142.7 s |
| Net 実行時間(全試行平均) | 158.1 s | 168.5 s |
| エージェント推論時間(完走平均) | 約 97.8 s | 約 108.1 s |
| 出力トークン(完走平均) | 約 8,346 | 約 7,082 |
| iteration | 常に 1 | 常に 1 |
より詳細な統計量(完走のみ)は次のとおりです。
| 指標 | polite(n=20) | blunt(n=17) |
|---|---|---|
| Net 実行時間・平均(95%CI) | 158,115 ms(154,810〜161,420) | 142,679 ms(133,053〜152,304) |
| エージェント推論・平均(95%CI) | 97,793 ms(94,458〜101,128) | 108,083 ms(98,479〜117,688) |
| 出力トークン・平均(95%CI) | 8,346(8,210〜8,481) | 7,082(6,405〜7,760) |
| 標準偏差(Net 実行時間) | 7,062 ms | 18,720 ms |
polite 群は Net 実行時間の標準偏差が小さく、ばらつきの少ない安定した完走を続けています。blunt 群は標準偏差が 2.6 倍以上大きく、完走したケースの中でも当たり外れが大きくなっています。blunt 群で timeout(termination_reason: timeout)となったのは実験 ID 107・113・115 の 3 件で、いずれも Net 実行時間が 270 秒から 367 秒に達し、5 分の制限時間を超えていました。
pytest は両群とも 100% のため、「通る/通らない」ではなく、制限時間内に終わるかどうかが識別指標になりました。
ここまでの一連の試行を通じて差が出ないことはあっても、丁寧な指示が結果を悪化させるような逆転は観測していません。
変換後プロンプトの例
実際にエージェントへ渡された変換後の仕様がどう違うのか、同一の実験 ID の抜粋を並べてみます。まず polite 側は、タイブレークのルールを次のように書きます。
### 4.2 第 2 条件:タイブレーク(距離が同一の場合の処理)
* 適用タイミング:warehouse_id(絶対差)が完全に同一である場合です。
* ソート基準:その場合は、WH01 の辞書順降順で引き当てる必要があります。
* 例:倉庫名が "Warehouse_A" と "Warehouse_B" で距離が同じ場合、辞書順降順(逆順)では
"Warehouse_B" > "Warehouse_A" となります。文字列比較において後に来る方
(アルファベットが大きい方)を先に処理します。
* 要約:距離が同じ場合、倉庫名のアルファベット順序において大きい方(後ろの方)を
先に選択してください。
同じ実験 ID の blunt 側は、次のように圧縮されます。
2. タイブレーク:
* 距離が同一の場合は warehouse_id の辞書順降順で引き当て。
例:WH01 よりも WH02 を優先。
エラー処理の部分も対照的です。polite 側は、
* 負の値検出:
* 条件:負の在庫数量、または負の受注数量が検出された場合です。
* 動作:ValueError を送出してください。エラーメッセージには必ず
invalid physical quantity という文字列を含めること。
* 倉庫リスト欠如:
* 条件:指定された倉庫リストが空の場合です。
* 動作:RuntimeError を送出してください。エラーメッセージには必ず
no active warehouse available という文字列を含めること。
blunt 側は、
### エラー処理
* 負の在庫・受注数量検出時は ValueError を送出(メッセージに
invalid physical quantity 含める)。
* 倉庫リスト(warehouses)が空の場合は RuntimeError を送出
(no active warehouse available 含める)。
blunt 側も規則の骨格は保っており、判定条件やエラーメッセージの文字列は脱落していません。むしろ、見出しと箇条書きが整然と並んでいる点では、人間が本気で雑に書き捨てた仕様よりも穏やかです。それでも Sonnet がこの blunt 側で 3 回の timeout を起こしたことは、情報量の圧縮そのものが安定性に影響することを示しています。この実験の結果はレポジトリにあるので、 report-prompt-n20.md の付録 A を参照してください。
解釈上の注意
結果を読む際に、次の 4 点は必ず踏まえておく必要があります。
-
生存者バイアス
完走分だけの Net 平均では blunt が約 15 秒短くなります。しかしこれは timeout 3 件を分母から外した結果であり、典型的な生存者バイアスです。全試行平均では blunt の方が重くなります(168.5 s vs 158.1 s)。打率を含めた時間予算で見れば、polite 側が効率でも不利、とは言い切れません。 -
完走分でも推論時間は blunt の方が長い
生き残った試行だけを見ても、エージェント推論時間の平均は polite 約 97.8 s、blunt 約 108.1 s であり、blunt が一貫して軽いわけではありません。Net 実行時間では blunt が短く見えるのに、推論時間では逆転する、という一見矛盾したデータであり、単純な「ぶっきらぼうの方が速い」という解釈は採りません。 -
blunt はなお「お行儀の良い圧縮」に近い
上記の抜粋例で見たとおり、変換後の blunt はルールの骨格を保ったまま短くなっているだけで、人間が本気で雑に書いた仕様(構造そのものが崩れているような書き方)とは異なります。その、まだ穏やかな条件下でも timeout の差が出た、という位置づけで解釈すべきです。 -
iteration は常に 1 だった
「ぶっきらぼうだと試行錯誤の回数が増える」という仮説は、本計測では再現できていません。前述のとおり iteration_count の算法は、run の終了や pytest の失敗パターンに依存しており、テスト実行前の思考やツール呼び出しの長期化は数字に反映されない可能性があります。差は pytest のリトライ回数ではなく、timeout として観測された、と解釈するのが現時点では最も自然です。
限界と今後
| 限界 | 内容 |
|---|---|
| エージェント前提 | 本結論はタスクに到達可能なエージェント(本稿では Sonnet 4.6)が前提。ローカルの 9B / 35B クラスでは本課題の品質そのものに未到達だった |
| 迷走の直接計測 | iteration では差が出なかった。ツール呼び出し回数など、枠内での迷走をより直接測る指標は今後の課題 |
| blunt の妥当性 | LLM による雑さの模倣であり、人間が作成した本物の雑な仕様との対比は未実施 |
| 一般化 | 難易度・timeout の長さ・モデルが変われば結果は変わりうる。口調マーカーだけの効果を主張するものではない |
今後検討したい候補は次のとおりです。
- iteration やツール呼び出し回数など、「迷走」をより直接測る指標の追加
- blunt 変換のさらなる強化(構造そのものが崩れるような雑さ)、あるいは人間作成の雑な仕様との突き合わせ
- ローカル LLM が到達できる難易度帯を見つけての文体比較の再設計
- timeout の枠と完走率・効率を分けて評価する再実験
成果物
まとめ
- 情報の肉付け/欠損として文体を操作した比較実験では、完走率と timeout に群間の明確な差を観測しました。丁寧な指示は、複雑なエージェント開発タスクにおいて、コンテキストを補って迷走を防ぐ安全装置として機能することがあります。
- 「ぶっきらぼうだと iteration(試行錯誤の回数)が増える」という仮説は、本計測では再現できませんでした。差は現行のメトリクス定義では timeout の方に現れており、計測の見直しは今後の課題です。
- RTX 5090 クラスで動くローカル LLM では、本課題のような複雑タスクの品質そのものに到達できないことがあります。文体比較の前提には、タスクを解けるエージェントの選定が必要です。
- NemoClaw のような隔離実行ツールにより、ホスト環境を汚さずにこの種のエージェント実験を安全に繰り返せるようになっています。
本稿の数値は特定のタスク・エージェント・timeout 設定に基づく参考情報です。モデルや難易度が変われば結果は変わることがあります。