TL;DR
自律型トレーディングシステムで、投資目標の進捗に応じてリスクパラメータを動的に調整する機能を実装した。計算ロジックは正しく動いていたが、計算結果がエージェントのプロンプトに届いていなかった。プロンプト内の数値がプレーンテキストでハードコードされていたため、エージェントは常に保守的な固定値に従い続けていた。
背景
trader は日本株・ビットコインの自律型トレーディングシステムで、Claude をマルチエージェントとして使い、日次の投資提案を生成する。
システムには安全規約があり、エクスポージャー上限(60%)や現金比率下限(30%)などのリスクパラメータが定義されている。投資目標(goal)システムを導入し、目標への進捗ペースに応じてこれらのパラメータを動的に調整する機能を実装した。
何が起きたか
期待していた動作
| |
実際の動作
| |
goal の評価は正しく行われ、propose_adjustment() は適切な調整値を返していた。しかしエージェントが参照するプロンプトには、値がハードコードされていた:
| |
一方、同じプロンプト内の max_position_pct(1取引あたりポジション上限)は既にテンプレート変数化されていた:
| |
同じファイル内に、テンプレート化された値とハードコードされた値が混在していた。
原因分析
時系列
- PR #255 で
{{max_position_pct}}のテンプレート置換をorchestrator.pyに導入 - PR #257 で goal システムに
AdjustmentProposal(exposure_limit/cash_ratio_min)を実装 - しかし #257 でエージェントプロンプトへの注入パイプラインが未実装のままマージされた
構造的原因
| 原因 | 説明 |
|---|---|
| 接続の欠落 | goal システムは「評価→提案→レポート」として完結しており、「提案→プロンプト注入」が設計から漏れた |
| テンプレート化の不統一 | max_position_pct だけテンプレート化済み。同じパターンの適用が漏れた |
| テスト境界の問題 | propose_adjustment() の返り値テストはあったが、値がプロンプトに到達するかの結合テストがなかった |
| 自然言語ハードコード | .md ファイル内の日本語テキストに埋め込まれた数値は、コードレビューで「値の出所」を問われにくい |
最後の点が特に興味深い。プログラムコードなら EXPOSURE_LIMIT = 60 というハードコードはレビューで指摘されやすいが、自然言語プロンプト内の「エクスポージャー60%以内」は「説明文」として読み飛ばされやすい。
修正内容
1. プロンプトのテンプレート変数化
| |
対象ファイル: portfolio.md, risk.md, researcher.md
2. orchestrator.py での動的注入
| |
3. 週次レビューレポートの動的化
reporter.py の「現在値」カラムもハードコードから動的取得に変更。
4. テスト追加(4件)
| |
最後のテストは回帰防止テストで、プロンプトファイル内にエクスポージャーや現金比率のハードコード値が存在しないことを正規表現で検証する。
ペース別パラメータ
| ペース | エクスポージャー上限 | 現金比率下限 |
|---|---|---|
| ahead | 50% | 40% |
| on_track | 60% | 30% |
| behind | 70% | 20% |
| critical | 変更なし | 変更なし |
再発防止策
CLAUDE.md にルールを追加
| |
これは Claude Code の CLAUDE.md に追加したルールで、AI アシスタントが今後のコード変更時にこのパターンを自動的に適用する。
教訓
1. LLM プロンプトは「コード」として扱え
自然言語で書かれたプロンプトも、パラメータを含む以上はコードと同等に扱うべき。マジックナンバーの禁止、テンプレート変数の使用、テストによる検証 — ソフトウェアエンジニアリングの原則はプロンプトにも適用される。
2. 「生成元」と「消費先」の接続を検証せよ
値を生成するモジュールと消費するモジュールが分かれている場合、パイプラインの接続テストが必要。単体テストで各モジュールが正しく動いていても、接続が切れていれば意味がない。
3. 既存パターンの適用漏れに注意
max_position_pct でテンプレート変数のパターンが確立されていたのに、新しいパラメータに同じパターンを適用し忘れた。パターンを導入したら、同じカテゴリのすべての箇所に適用されているか確認するチェックリストが有効。
4. 回帰防止テストは「仕組み」で守る
「ハードコード禁止」をルールとして文書化するだけでなく、テストコードで機械的に検出する仕組みを入れた。人間(とAI)の注意力に頼らず、CI が自動的にキャッチする。