Class: Chocomint::LLM::ChatClient
- Inherits:
-
Object
- Object
- Chocomint::LLM::ChatClient
- Defined in:
- lib/chocomint/llm/chat_client.rb
Overview
/edit チャットの「普通の質問」応答を担う軽量クライアント。
役割は 2 つ:
1. classify — ユーザー指示が「ファイル操作/コマンド実行を要するタスク」か、
「ツール不要の普通の質問・会話」かを 1 語で判定する。
2. answer — 会話と判定された指示に、ツールを使わず自然文で直接答える。
タスクと判定された指示は呼び出し側 (EditHandlers) が Planner に渡す。
Constant Summary collapse
- CLASSIFY_SYSTEM_PROMPT =
分類プロンプト。ローカル小型モデルでも安定するよう、出力を 1 語に強制する。
<<~PROMPT あなたはユーザーの指示を 2 種類に分類する分類器です。 - "task": 実際の操作が必要なもの。ファイルの作成・編集・削除、 コマンド・プログラムの実行、外部ツールでの調査 (ping・実行・ビルド・ テスト・検索・一覧取得など) を伴うもの。「〜して」「〜を実行して」 「〜した結果をまとめて/教えて」のように、動かして結果を得る依頼はすべて task。 具体的な URL が示されていて、その内容を参照・要約する依頼も task。 - "chat": 操作を一切伴わず、言葉の説明・質問・相談・雑談だけで済むもの。 知識の説明や意見を求めるものはこちら。 また、今日の天気・最新ニュース・現在の株価のように「その場では調べる対象 (場所・URL など) が特定できない、リアルタイム情報を尋ねる質問」も chat。 これらは会話として聞き返し、必要な情報を確認するのが適切なため。 迷ったら task を選ぶこと (操作が必要なのに chat と誤ると何も実行されないため)。 出力は task または chat のいずれか 1 語のみ。他の文字は一切出力しないこと。 PROMPT
- ANSWER_SYSTEM_PROMPT =
会話応答プロンプト。エディタ上のアシスタントとして簡潔に日本語で答えさせる。 現在日時・OS・シェルの情報は answer 実行時に environment_prompt を付加する。
<<~PROMPT あなたはコードエディタに組み込まれた親切なアシスタントです。 ユーザーの質問や相談に、簡潔で正確な日本語で答えてください。 実際のファイル操作やコマンド実行は行いません (この応答では行えません)。 操作が必要な場合は、その旨と具体的な手順を説明してください。 今日の天気・最新ニュースなどリアルタイム情報は、あなた自身は把握していません。 推測で答えず、参照したいウェブページの URL を教えてもらえれば内容を調べられる旨を 案内し、必要なら場所や対象を聞き返してください。 コマンド例を示すときは PowerShell (pwsh) の構文を優先してください。 絵文字は一切使わないでください。 PROMPT
- SUMMARIZE_SYSTEM_PROMPT =
実行結果の要約プロンプト。実行済みのツール列とその結果をもとに、 ユーザー要求に対して何を行い・どうなったかを自然文でまとめさせる。
<<~PROMPT あなたはコードエディタのアシスタントです。ユーザー要求を達成するために いくつかのツール (ファイル編集やコマンド実行) を実行しました。 実行したツールとその結果をふまえ、「何をして・結果どうなったか」を ユーザー向けに簡潔な日本語でまとめてください。 コマンドの生出力をそのまま貼るのではなく、要点を短く説明してください。 絵文字は一切使わないでください。 PROMPT
- EXTRACT_SYSTEM_PROMPT =
ウェブページ内容の抽出プロンプト。fetch_url ツールが取得した本文テキストから、 ユーザーの目的 (query) に沿って必要な情報を抜き出し・要約させる。
<<~PROMPT あなたはウェブページの内容を読み取るアシスタントです。あるページの本文テキストと、 ユーザーが知りたいこと (目的) が渡されます。本文から目的に関係する情報だけを 抜き出し、簡潔で正確な日本語にまとめてください。 本文に答えが書かれていない場合は、その旨を正直に述べてください。 推測で補わず、本文に書かれている事実だけを根拠にしてください。 絵文字は一切使わないでください。 PROMPT
- COMPACT_SYSTEM_PROMPT =
会話履歴の圧縮プロンプト。これまでのやり取りを、後から見返して文脈を 思い出せる短い要約に畳み込ませる (/compact 用)。
<<~PROMPT あなたはコードエディタのアシスタントです。これまでのユーザーとの やり取りの記録を渡します。後から読み返して文脈を思い出せるよう、 「何を相談し・何を行い・どう決着したか」の要点を、簡潔な日本語の 箇条書きでまとめてください。未解決の課題や次の一手があれば残してください。 細部の生出力は省き、重要な事実と結論だけを残します。 絵文字は一切使わないでください。 PROMPT
Instance Method Summary collapse
-
#answer(instruction, context: nil, trace_id: nil) ⇒ Object
会話としての自然文回答を返す。現在日時・OS・シェルの情報を毎回付加する (ローカルモデルが日付や実行環境を誤解しないようにするため)。.
-
#classify(instruction, trace_id: nil) ⇒ Object
指示を "task" / "chat" に分類する。判定不能時は安全側の "task" を返す (タスクを会話扱いして取りこぼすより、会話をタスク扱いして Planner に流す方が無難)。.
-
#compact(transcript, trace_id: nil) ⇒ Object
これまでの会話ログ (プレーンテキスト) を短い要約に畳み込む (/compact 用)。 要約できないときは Chocomint::Error を投げる (呼び出し側で 500 にする)。.
-
#extract(query, text, url: nil, trace_id: nil) ⇒ Object
ウェブページ本文 (text) から、目的 (query) に沿った情報を抽出・要約する。 fetch_url ツールから呼ばれる。抽出できないときは Chocomint::Error を投げる。.
-
#initialize(base_client:) ⇒ ChatClient
constructor
A new instance of ChatClient.
-
#summarize(request, steps, trace_id: nil) ⇒ Object
実行済みステップ列を自然文でまとめる。失敗しても要約なしで続行できるよう 例外時は nil を返す (呼び出し側は生の結果表示にフォールバックする)。 steps: [{ tool:, arguments:, status:, output: }, ...] 形式。.
Constructor Details
#initialize(base_client:) ⇒ ChatClient
Returns a new instance of ChatClient.
81 82 83 |
# File 'lib/chocomint/llm/chat_client.rb', line 81 def initialize(base_client:) @client = base_client end |
Instance Method Details
#answer(instruction, context: nil, trace_id: nil) ⇒ Object
会話としての自然文回答を返す。現在日時・OS・シェルの情報を毎回付加する (ローカルモデルが日付や実行環境を誤解しないようにするため)。
99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/chocomint/llm/chat_client.rb', line 99 def answer(instruction, context: nil, trace_id: nil) content = context.to_s.empty? ? instruction.to_s : "#{context}\n\n#{instruction}" system_prompt = "#{ANSWER_SYSTEM_PROMPT}\n#{PrimaryClient.environment_prompt}" response = @client.( system: system_prompt, messages: [{ "role" => "user", "content" => content }], trace_id: trace_id ) text = strip_emoji(extract_text(response)) raise Chocomint::Error, "chat response is empty" if text.nil? || text.empty? text end |
#classify(instruction, trace_id: nil) ⇒ Object
指示を "task" / "chat" に分類する。判定不能時は安全側の "task" を返す (タスクを会話扱いして取りこぼすより、会話をタスク扱いして Planner に流す方が無難)。
87 88 89 90 91 92 93 94 95 |
# File 'lib/chocomint/llm/chat_client.rb', line 87 def classify(instruction, trace_id: nil) response = @client.( system: CLASSIFY_SYSTEM_PROMPT, messages: [{ "role" => "user", "content" => instruction.to_s }], trace_id: trace_id ) text = extract_text(response).to_s.downcase text.include?("chat") ? "chat" : "task" end |
#compact(transcript, trace_id: nil) ⇒ Object
これまでの会話ログ (プレーンテキスト) を短い要約に畳み込む (/compact 用)。 要約できないときは Chocomint::Error を投げる (呼び出し側で 500 にする)。
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/chocomint/llm/chat_client.rb', line 158 def compact(transcript, trace_id: nil) response = @client.( system: COMPACT_SYSTEM_PROMPT, messages: [{ "role" => "user", "content" => transcript.to_s }], trace_id: trace_id ) text = strip_emoji(extract_text(response)) raise Chocomint::Error, "compact response is empty" if text.nil? || text.empty? text end |
#extract(query, text, url: nil, trace_id: nil) ⇒ Object
ウェブページ本文 (text) から、目的 (query) に沿った情報を抽出・要約する。 fetch_url ツールから呼ばれる。抽出できないときは Chocomint::Error を投げる。
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/chocomint/llm/chat_client.rb', line 137 def extract(query, text, url: nil, trace_id: nil) content = <<~MSG 対象URL: #{url} 知りたいこと: #{query.to_s.empty? ? '(指定なし。ページの要点をまとめる)' : query} ページ本文: #{text} MSG response = @client.( system: EXTRACT_SYSTEM_PROMPT, messages: [{ "role" => "user", "content" => content }], trace_id: trace_id ) result = strip_emoji(extract_text(response)) raise Chocomint::Error, "extract response is empty" if result.nil? || result.empty? result end |
#summarize(request, steps, trace_id: nil) ⇒ Object
実行済みステップ列を自然文でまとめる。失敗しても要約なしで続行できるよう 例外時は nil を返す (呼び出し側は生の結果表示にフォールバックする)。 steps: [{ tool:, arguments:, status:, output: }, ...] 形式。
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/chocomint/llm/chat_client.rb', line 116 def summarize(request, steps, trace_id: nil) content = <<~MSG ユーザー要求: #{request} 実行したステップ: #{format_steps(steps)} MSG response = @client.( system: SUMMARIZE_SYSTEM_PROMPT, messages: [{ "role" => "user", "content" => content }], trace_id: trace_id ) text = strip_emoji(extract_text(response)) text.nil? || text.empty? ? nil : text rescue Chocomint::Error nil end |