API サーバーとのやり取りにおいて、file_get_contents のところが問題になっているように思えて、curl を試してみました。その記録です。リクエストに長い時間がかかる場合の話です。通信は2段階あって、その最初が PHP から PHP です。続けて PHP から Python を起動して ollama API と通信します。返事をもらうまでにいまのところ2、3分必要です。それがうまくいかない。
最初の PHP から PHP への通信を検討してみました。もとは下記のように file_get_contents で書いていました。最初は timeout を書いていなかったのですが、あとで追加しています。timeout を書き加えるまでは、ちょうど1分で切れていました。PHP のデフォルトの時間のようです(www-data が実行)。
$postdata = http_build_query(array('base64data' => $tmpBase64, 'TAnsAndEtc' => $tmpBase64a));
$headers = array(
'Content-Type: application/x-www-form-urlencoded',
'Content-Length: '.strlen($postdata)
);
$context = stream_context_create([
'http' => ['method' => 'POST',
'header'=>implode( "\r\n", $headers),
'ignore_errors' => true,
'content' => $postdata,
'timeout' => 300
]
]);
$res_base64 = file_get_contents($server . '/phpSimpleQuestion/allAnswerTest.php', false, $context);
timeout を追加して多少改善されたと思ったのですが、それでもかなりの確率で失敗します。そこで curl を使った下記のような記述に改めました。これは TIMEOUT を指定しないと、無期限に待つことになります。
$param = array('base64data' => $tmpBase64, 'TAnsAndEtc' => $tmpBase64a);
$url = $server . '/phpSimpleQuestion/allAnswerTest.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($param));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
$res_base64 = curl_exec($ch);
curl_close($ch);
$res = base64_decode($res_base64);
上記も確実というわけではありませんが、まあだいぶ良くなりました。
(自分への注意書き:RequestToRemoteServer.php の中の記述。採点差配サーバーのファイルなので、あとで忘れずに修正をかける必要がある。)
ついでなので、この先の通信のことを追記します。下記の Python コードを 実行して ollama API と通信します。こちらも問題なのかもしれません。時々 ollama から返事が無い。いまのところ import ollama を避けて、requests でやっています。www-data で実行するのですが、そうすると import ollama をするためには、特別な環境を用意する(activate する)必要があると思って避けています。下記では画像を読み込んで一緒に送っています。
import requests
fullpath = foldername + '/sample.png'
with open(fullpath, "rb") as f:
img_base64 = base64.b64encode(f.read()).decode("utf-8")
url = "http://192.168.100.xxx:11434/api/chat"
payload = {
"model": "gemma4:12b",
"messages": [{
"role": "user",
"content": "この画像は物理学の問題です。解答するためのヒントを教えてください。",
"images": [img_base64],
}],
"stream": False,
}
response = requests.post(url, json=payload, timeout=300)
print(response.json()["message"]["content"])
現状それほど安定していないので、import ollama が利用できる特別な環境を準備して、ollama Python ライブラリーを試したほうが良いのかもしれません。その時はまた追記します。
20260922
繰り返しではありますが ollama Python ライブラリーを使うことを避けていた理由は、www-data でコードを実行するからです。www-data はグローバルな環境(こんな言い方で良いのかな?)でプログラムを動かします。グローバルな環境では pip install ollama とか実行できなくて、ollama ライブラリー が使用可能な環境をグローバルとは別に用意する必要があります。しかし用意したとしても www-data で pyhton を実行するときに、特別な環境で実行する方法が分かりません。それでとりあえず www-data からアクセス可能なところにライブラリーを配置することで対応してみました。グローバルとは別に用意した環境で ollama ライブラリーをインストールして、そのファイルのコピーを www-data がアクセス可能な場所に配置しただけです(sys.path.insert のところ)。
import sys, os
import base64
sys.path.insert(0, '/どこか/lib/python3.12/site-packages/') # ollama ライブラリーの場所
from ollama import Client, ResponseError, RequestError
def generateTextWithOllamaClient(content: str, imageFilePath, hostURL, modelName) -> str | None:
client = Client(host=hostURL, timeout=180)
try:
response = client.chat(
model=modelName,
messages=[{
'role': 'user',
'content': content,
'images': [imageFilePath]
}]
)
# 応答から生成されたテキストを取得
generated_text = response['message']['content'].replace("'", "\\'")
if generated_text:
return generated_text.strip()
else:
print("エラー: レスポンスに 'message' キーが含まれていません。")
print("レスポンス内容:", generated_text)
return None
except RequestError as e:
# 接続エラーなど、リクエスト自体の問題
print(f"エラー: Ollamaサーバー への接続またはリクエスト中にエラーが発生しました。")
print(f"詳細: {e}")
print("Ollamaサーバーが起動しているか、ネットワーク設定(ファイアウォール等)を確認してください。")
return None
except ResponseError as e:
# Ollamaサーバーからのエラー応答 (例: モデルが見つからない)
print(f"エラー: Ollamaサーバーからエラー応答がありました。")
print(f"ステータスコード: {e.status_code}")
print(f"エラー内容: {e.error}")
return None
except Exception as e:
# その他の予期せぬエラー
print(f"予期せぬエラーが発生しました: {e}")
return None
hostURL = 'http://192.168.100.206:11434'
modelName = 'gemma4:12b'
aContent = '画像は物理の練習問題です。問いを解くためのヒントを教えてください。正解は教えないでください。'
imageFile = './cutycapt.png'
generated_text = generateTextWithOllamaClient(aContent, imageFile, hostURL, modelName)
print(generated_text)
いまのところ、こちらのほうが失敗が少ないような気がします。でもまだ失敗することは普通にあるので、もっと経験を積む必要がありそうです。