Class: Chocomint::Planner
- Inherits:
-
Object
- Object
- Chocomint::Planner
- Defined in:
- lib/chocomint/planner.rb
Overview
マルチステップの逐次実行オーケストレータ。
単一ツールで完結する要求だけでなく、「uv があるか確認 → python インストール → 依存追加 → 仮想環境作成」のように複数のツールを順に実行して達成する要求に対応する。
各ステップで主 LLM に「次に実行すべき 1 ツール」を提案させ (propose_step)、 Orchestrator#run_step で実行・機械検証し、副 LLM (verifier) が 「要求全体が達成されたか」を判定する。達成なら完了、未達成なら次ステップへ進む。 LLM は提案者・監査者に限定し、実行・判定・ループ制御・記録は Ruby が担う (DESIGN §20)。
Defined Under Namespace
Classes: Result
Instance Method Summary collapse
-
#initialize(primary:, orchestrator:, verifier:, logger:, max_steps: 10, step_retry: 3) ⇒ Planner
constructor
max_steps: 実行を許すステップ数の上限 (暴走防止)。max_retry を流用する。 step_retry: 1 ステップが machine 検証で失敗したとき、引数を変えて再提案する回数。.
-
#run(request, expectations: nil, on_event: nil) ⇒ Object
要求を達成するまでツールを 1 つずつ実行する。 成功時は Result(status: "PASS")。max_steps 到達で達成できなければ RetryLimitExceededError を送出する (それまでの成功ステップは監査ログに残る)。.
Constructor Details
#initialize(primary:, orchestrator:, verifier:, logger:, max_steps: 10, step_retry: 3) ⇒ Planner
max_steps: 実行を許すステップ数の上限 (暴走防止)。max_retry を流用する。 step_retry: 1 ステップが machine 検証で失敗したとき、引数を変えて再提案する回数。
26 27 28 29 30 31 32 33 34 |
# File 'lib/chocomint/planner.rb', line 26 def initialize(primary:, orchestrator:, verifier:, logger:, max_steps: 10, step_retry: 3) @primary = primary @orchestrator = orchestrator @verifier = verifier @logger = logger @max_steps = max_steps @step_retry = step_retry end |
Instance Method Details
#run(request, expectations: nil, on_event: nil) ⇒ Object
要求を達成するまでツールを 1 つずつ実行する。 成功時は Result(status: "PASS")。max_steps 到達で達成できなければ RetryLimitExceededError を送出する (それまでの成功ステップは監査ログに残る)。
on_event: 進捗を逐次通知するコールバック (任意)。UI のリアルタイム表示に使う。 呼ばれるイベント (いずれも Hash): { type: "step_start", step: n, tool:, label:, target: } … ツール実行の直前 { type: "step_done", step: n, tool:, label:, target:, status:, exit_code:, output: } … 実行後 コールバック内の例外は握りつぶす (通知失敗で実行本体を止めないため)。
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/chocomint/planner.rb', line 45 def run(request, expectations: nil, on_event: nil) trace_id = SecureRandom.uuid done_steps = [] total_attempts = 0 @max_steps.times do |i| step_no = i + 1 proposal, error = propose_and_execute(request, done_steps, trace_id, step_no, on_event) total_attempts += 1 if proposal.nil? # ステップ内リトライを使い切っても実行できなかった。ここで打ち切る。 @logger.record(trace_id: trace_id, attempt: step_no, request: request, proposal: { "error" => error.to_s }, result: {}, status: "FAIL") raise RetryLimitExceededError, "step #{step_no} failed after #{@step_retry} retries: #{error}" end tool = proposal["tool"] result = proposal.delete(:__result) # finish 提案はループ終了の合図 (実行はしない)。 if tool == LLM::PrimaryClient::FINISH_TOOL return finish(request, done_steps, trace_id, step_no, total_attempts, expectations, message: (proposal)) end step = { tool: tool, arguments: proposal["arguments"], result: result, status: "PASS" } done_steps << step @logger.record(trace_id: trace_id, attempt: step_no, request: request, proposal: proposal, result: result, status: "STEP") # 全体達成判定。達成なら完了。未達成なら次ステップへ。 pass, verifier_output = verify_overall(request, done_steps, expectations, trace_id) next unless pass @logger.record(trace_id: trace_id, attempt: step_no, request: request, proposal: proposal, result: result, status: "PASS", verifier_output: verifier_output) return Result.new(status: "PASS", trace_id: trace_id, steps: done_steps, attempts: total_attempts) end # max_steps 到達。要求を達成しきれなかった。 @logger.record(trace_id: trace_id, attempt: @max_steps, request: request, proposal: { "error" => "max steps reached" }, result: {}, status: "FAIL") raise RetryLimitExceededError, "step limit exceeded (#{@max_steps})" end |