【体験談】DelphiからOllamaを叩いたら、指定したはずのパラメータが一切効いていなかった話

Delphi

結論から言うと、リクエストの中身が空だった

先に結論を書いておくと、原因は「JSONを組み立てるコードは書いたのに、そのオブジェクトを最終的なリクエストボディにアタッチし忘れていた」という、実にシンプルなバグだった。ただ、これに気づくまでの過程がなかなか厄介だったので記録しておく。

症状: num_ctxを変えても挙動が変わらない

DelphiのTNetHTTPClientを使ってOllamaのHTTP APIを叩くフロントエンドを実装していた。生成パラメータ(num_ctx、num_predict、temperatureなど)はDelphi側の設定画面から変更できるようにしていて、その値をJSONの options オブジェクトに詰めてリクエストに含める設計にしていた。

ところが、num_ctxをいくら変更しても、生成結果や挙動がまったく変わらない。長い文章を入力してもコンテキストが溢れているような挙動を示すし、temperatureを0に近づけても出力の揺れが収まらない。

最初はOllama側のバグを疑い、サーバーログを確認したり、curlで直接同じパラメータを叩いて比較したりした。curlで直接叩いた場合はちゃんとパラメータが反映される。つまり、Ollama側ではなく、Delphi側のリクエスト生成に問題があることになる。

原因調査: 実際に送信されているJSONを確認する

TNetHTTPClientはデフォルトだと送信内容がログに出ないため、Fiddlerでプロキシを噛ませて、実際に送信されているHTTPリクエストの中身を確認することにした。

すると、リクエストボディには modelprompt のキーしか入っておらず、options キー自体が存在しなかった。

コード上では、たしかに options に相当するTJSONObjectを生成し、num_ctxやtemperatureの値をAddPairで詰め込んでいた。ここまでは正しい。しかし、最終的にリクエスト全体を表すTJSONObjectに対して、この options オブジェクトをAddPairする行が、リファクタリングの過程でどこかのタイミングで抜け落ちていた。

つまり、パラメータを詰めたオブジェクトは正しく作られていたのに、それを親オブジェクトに「くっつける」処理そのものが存在しなかった、ということになる。

修正

// 修正前: optionsオブジェクトを作るだけで終わっていた
var
  RequestJson, OptionsJson: TJSONObject;
begin
  RequestJson := TJSONObject.Create;
  RequestJson.AddPair('model', ModelName);
  RequestJson.AddPair('prompt', PromptText);

  OptionsJson := TJSONObject.Create;
  OptionsJson.AddPair('num_ctx', TJSONNumber.Create(NumCtx));
  OptionsJson.AddPair('temperature', TJSONNumber.Create(Temperature));
  // ↑ここで終わっていて、RequestJsonに繋いでいなかった
// 修正後: 忘れずにAddPairで親オブジェクトに接続する
  RequestJson.AddPair('options', OptionsJson);

たった1行が抜けていただけで、UI上ではパラメータを変更できているように見えるのに、実際には何も送信されていない状態がずっと続いていた。

学んだこと

  • UIで値を変更できることと、その値がサーバーに実際に送信されていることは別問題
  • パラメータを変えても挙動が変わらない場合、まずクライアント側で組み立てているリクエストの実物を確認するべき(Fiddler、Wireshark、あるいはOllama側のログでも可)
  • curlなど別の方法で同じAPIを直接叩いて比較すると、クライアント側の問題かサーバー側の問題かを素早く切り分けられる
  • JSONオブジェクトを入れ子で組み立てる際は、子オブジェクトを作った時点で満足せず、親への接続まで含めて確認する習慣をつけたほうがいい

たった1行の抜け漏れが、何日分もの調査時間を生み出す。地味だが、こういう話こそ書き残しておく価値があると思っている。

コメント

タイトルとURLをコピーしました