Class: Chocomint::LLM::PrimaryClient
- Inherits:
-
Object
- Object
- Chocomint::LLM::PrimaryClient
- Defined in:
- lib/chocomint/llm/primary_client.rb
Overview
主 LLM にツール提案をさせる (DESIGN §5 Step1)。 tool_use ブロックを優先し、無ければテキストから JSON を抽出する (LLM 出力形式に依存しない堅牢性: DESIGN Goals)。
Constant Summary collapse
- WORKSPACE_RULES =
共通の作業ルール (パス・workspace の扱い)。両プロンプトで共有する。
"\u30D1\u30B9\u3092\u6307\u5B9A\u3059\u308B\u30C4\u30FC\u30EB\u3067\u306F\u3001\u5FC5\u305A\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9 (\u4F8B: workspace/foo.txt) \u3092\n\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002/workspace \u3084 /home/user/workspace \u306E\u3088\u3046\u306A\u7D76\u5BFE\u30D1\u30B9\u306F\u8A31\u53EF\u3055\u308C\u307E\u305B\u3093\u3002\nrun_command / bash / powershell \u306E\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30EB\u30FC\u30C8\u3067\u3059\u3002\n\u6210\u679C\u7269\u306F\u5FC5\u305A workspace/ \u914D\u4E0B\u306B\u4F5C\u3063\u3066\u304F\u3060\u3055\u3044\u3002uv \u306A\u3069\u51FA\u529B\u5148\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u53D6\u308B\u30B3\u30DE\u30F3\u30C9\u306F\n\u5FC5\u305A --directory workspace/... \u306E\u3088\u3046\u306B workspace \u914D\u4E0B\u3092\u660E\u793A\u3057\u3066\u304F\u3060\u3055\u3044\n(\u4F8B: run_command uv init --directory workspace/myproj)\u3002\n\u6700\u65B0\u60C5\u5831\u3084\u5916\u90E8\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u53C2\u7167\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F fetch_url \u30C4\u30FC\u30EB\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002\n".strip
- FINISH_TOOL =
完了宣言用の疑似ツール。実際には実行されず、Planner がループ終了の合図として使う。
"finish"- FINISH_TOOL_DEFINITION =
{ "name" => FINISH_TOOL, "description" => "ユーザー要求がすべて達成され、もう実行すべきツールが残っていないときに呼ぶ。", "input_schema" => { "type" => "object", "properties" => { "reason" => { "type" => "string", "description" => "完了と判断した理由 (任意)" }, "message" => { "type" => "string", "description" => "ユーザーへ伝える最終メッセージ (任意)" } } } }.freeze
Class Method Summary collapse
-
.environment_prompt ⇒ Object
実行環境 (現在日時・OS・シェル) の説明。ローカルモデルが「今日の日付」や 「どの OS 上で動いているか」を誤解しないよう、システムプロンプトに毎回埋め込む。 シェルは PowerShell を優先させる (Windows ネイティブ環境のため)。.
-
.host_os_description ⇒ Object
RbConfig の host_os を人間可読な OS 名に整える。.
-
.step_system_prompt ⇒ Object
マルチステップの逐次実行を促すシステムプロンプト。 1 回の応答で「次に実行すべき 1 ツール」だけを選ばせ、 やることが残っていなければ finish ツールで完了を宣言させる。.
- .system_prompt ⇒ Object
Instance Method Summary collapse
-
#initialize(base_client:, tool_definitions:) ⇒ PrimaryClient
constructor
A new instance of PrimaryClient.
-
#propose(request, feedback: nil, trace_id: nil, history: nil) ⇒ Object
戻り値: { "tool" => name, "arguments" => Hash } feedback: 初回〜前回までの全試行履歴 (再提案時に引数を変えさせるため / DESIGN §6)。 [{ attempt:, proposal:, error: }, ...] 形式。旧来の単一 Hash も許容する。 history: これまでの会話ターン (propose_step と同様。messages の先頭に積む)。.
-
#propose_step(request, done_steps = [], trace_id: nil, history: nil) ⇒ Object
マルチステップ用: これまで完了したステップを踏まえ、次に実行すべき 1 ツールを提案する。 完了していれば tool="finish" を返す (Planner がループ終了に使う)。 戻り値: { "tool" => name, "arguments" => Hash } done_steps: [{ tool:, arguments:, result: }, ...] 形式の完了済みステップ列。 history: これまでの会話ターン [{ "role" => "user"/"assistant", "content" => String }, ...] (request より前の文脈として messages の先頭に user/assistant のまま積む)。.
Constructor Details
#initialize(base_client:, tool_definitions:) ⇒ PrimaryClient
Returns a new instance of PrimaryClient.
93 94 95 96 |
# File 'lib/chocomint/llm/primary_client.rb', line 93 def initialize(base_client:, tool_definitions:) @client = base_client @tool_definitions = tool_definitions end |
Class Method Details
.environment_prompt ⇒ Object
実行環境 (現在日時・OS・シェル) の説明。ローカルモデルが「今日の日付」や 「どの OS 上で動いているか」を誤解しないよう、システムプロンプトに毎回埋め込む。 シェルは PowerShell を優先させる (Windows ネイティブ環境のため)。
15 16 17 18 19 20 21 22 23 24 |
# File 'lib/chocomint/llm/primary_client.rb', line 15 def self.environment_prompt " \u5B9F\u884C\u74B0\u5883:\n - \u73FE\u5728\u65E5\u6642: \#{Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')}\n - OS: \#{host_os_description}\n - \u30B7\u30A7\u30EB: PowerShell (pwsh) \u3092\u512A\u5148\u3057\u3066\u4F7F\u3046\u3053\u3068\u3002\n \u30B3\u30DE\u30F3\u30C9\u5B9F\u884C\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F\u3001\u539F\u5247\u3068\u3057\u3066 powershell \u30C4\u30FC\u30EB (\u307E\u305F\u306F run_command) \u3092\u4F7F\u3044\u3001\n PowerShell \u306E\u69CB\u6587\u3067\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\u3002bash \u56FA\u6709\u306E\u69CB\u6587\u306F\u907F\u3051\u3066\u304F\u3060\u3055\u3044\u3002\n ENV\nend\n".strip |
.host_os_description ⇒ Object
RbConfig の host_os を人間可読な OS 名に整える。
27 28 29 30 31 32 33 34 35 |
# File 'lib/chocomint/llm/primary_client.rb', line 27 def self.host_os_description host = RbConfig::CONFIG["host_os"] case host when /mswin|mingw|cygwin/ then "Windows (#{host})" when /darwin/ then "macOS (#{host})" when /linux/ then "Linux (#{host})" else host.to_s end end |
.step_system_prompt ⇒ Object
マルチステップの逐次実行を促すシステムプロンプト。 1 回の応答で「次に実行すべき 1 ツール」だけを選ばせ、 やることが残っていなければ finish ツールで完了を宣言させる。
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/chocomint/llm/primary_client.rb', line 63 def self.step_system_prompt " \u3042\u306A\u305F\u306F\u30E6\u30FC\u30B6\u30FC\u8981\u6C42\u3092\u9054\u6210\u3059\u308B\u305F\u3081\u306B\u3001\u30C4\u30FC\u30EB\u3092 1 \u3064\u305A\u3064\u9806\u756A\u306B\u5B9F\u884C\u3057\u3066\u3044\u304F\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u3067\u3059\u3002\n \u3053\u308C\u307E\u3067\u306B\u5B8C\u4E86\u3057\u305F\u30B9\u30C6\u30C3\u30D7\u306E\u4E00\u89A7\u304C\u4E0E\u3048\u3089\u308C\u307E\u3059\u3002\u305D\u308C\u3092\u8E0F\u307E\u3048\u3001\n \u300C\u6B21\u306B\u5B9F\u884C\u3059\u3079\u304D\u30C4\u30FC\u30EB\u3092 1 \u3064\u3060\u3051\u300D\u9078\u3073\u3001\u305D\u306E\u5F15\u6570\u3092\u6C7A\u3081\u3066\u304F\u3060\u3055\u3044\u3002\n\n \u30E6\u30FC\u30B6\u30FC\u8981\u6C42\u304C\u3059\u3079\u3066\u6E80\u305F\u3055\u308C\u3001\u3082\u3046\u5B9F\u884C\u3059\u3079\u304D\u3053\u3068\u304C\u6B8B\u3063\u3066\u3044\u306A\u3044\u5834\u5408\u306F\u3001\n finish \u30C4\u30FC\u30EB\u3092\u547C\u3073\u51FA\u3057\u3066\u5B8C\u4E86\u3092\u5BA3\u8A00\u3057\u3066\u304F\u3060\u3055\u3044 (\u305D\u308C\u4EE5\u5916\u306E\u30C4\u30FC\u30EB\u306F\u547C\u3070\u306A\u3044\u3053\u3068)\u3002\n \u307E\u3060\u6B8B\u4F5C\u696D\u304C\u3042\u308B\u5834\u5408\u306F finish \u3092\u547C\u3070\u305A\u3001\u6B21\u306B\u5FC5\u8981\u306A\u30C4\u30FC\u30EB\u3092 1 \u3064\u3060\u3051\u547C\u3093\u3067\u304F\u3060\u3055\u3044\u3002\n\n \#{environment_prompt}\n\n \#{WORKSPACE_RULES}\n PROMPT\nend\n" |
.system_prompt ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/chocomint/llm/primary_client.rb', line 48 def self.system_prompt " \u3042\u306A\u305F\u306F\u30C4\u30FC\u30EB\u5B9F\u884C\u3092\u63D0\u6848\u3059\u308B\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u3067\u3059\u3002\n \u30E6\u30FC\u30B6\u30FC\u8981\u6C42\u3092\u6E80\u305F\u3059\u305F\u3081\u306B\u547C\u3073\u51FA\u3059\u3079\u304D\u30C4\u30FC\u30EB\u3092 1 \u3064\u3060\u3051\u9078\u3073\u3001\n \u305D\u306E\u5F15\u6570\u3092\u6C7A\u3081\u3066\u304F\u3060\u3055\u3044\u3002\u30C4\u30FC\u30EB\u3092\u4F7F\u3063\u3066\u5FDC\u7B54\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n\n \#{environment_prompt}\n\n \#{WORKSPACE_RULES}\n PROMPT\nend\n" |
Instance Method Details
#propose(request, feedback: nil, trace_id: nil, history: nil) ⇒ Object
戻り値: { "tool" => name, "arguments" => Hash } feedback: 初回〜前回までの全試行履歴 (再提案時に引数を変えさせるため / DESIGN §6)。 [{ attempt:, proposal:, error: }, ...] 形式。旧来の単一 Hash も許容する。 history: これまでの会話ターン (propose_step と同様。messages の先頭に積む)。
126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/chocomint/llm/primary_client.rb', line 126 def propose(request, feedback: nil, trace_id: nil, history: nil) response = @client.( system: self.class.system_prompt, tools: @tool_definitions, messages: Array(history) + (request, feedback), trace_id: trace_id ) from_tool_use(response) || from_text(response) || raise(InvalidProposalError, "proposal contains no valid tool call") end |
#propose_step(request, done_steps = [], trace_id: nil, history: nil) ⇒ Object
マルチステップ用: これまで完了したステップを踏まえ、次に実行すべき 1 ツールを提案する。 完了していれば tool="finish" を返す (Planner がループ終了に使う)。 戻り値: { "tool" => name, "arguments" => Hash } done_steps: [{ tool:, arguments:, result: }, ...] 形式の完了済みステップ列。 history: これまでの会話ターン [{ "role" => "user"/"assistant", "content" => String }, ...] (request より前の文脈として messages の先頭に user/assistant のまま積む)。
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/chocomint/llm/primary_client.rb', line 104 def propose_step(request, done_steps = [], trace_id: nil, history: nil) response = @client.( system: self.class.step_system_prompt, tools: @tool_definitions + [FINISH_TOOL_DEFINITION], messages: Array(history) + (request, done_steps), trace_id: trace_id ) # ツール提案があればそれを優先。無ければモデルの自然文応答を「finish + メッセージ」 # として扱う。天気の問い合わせで場所を聞き返すなど、モデルがツールではなく # テキストで応答するのは正当なので、これを失敗にせずユーザーへ返す (聞き返し対応)。 # ここでは JSON 抽出の失敗を致命エラーにしない (strict: false)。自然文に壊れた # JSON 断片が混じっていても、聞き返し (from_text_reply) にフォールバックさせる。 from_tool_use(response) || from_text(response, strict: false) || from_text_reply(response) || raise(InvalidProposalError, "step proposal contains no valid tool call") end |