ailia LLM でマルチモーダルプロジェクタを読み込み、画像・音声を入力する方法を説明します。構造体で渡すailiaLLMSetMultimodalPromptと、JSON履歴で渡すailiaLLMSetPromptJsonの2つの方法があります。
モデルを読み込んだ後、対応するprojectorをailiaLLMOpenMultimodalProjectorFileAで読み込みます。
画像と音声の対応状況はailiaLLMGetMultimodalCapabilitiesのvision_support / audio_supportで確認できます。メディア非対応のモデル/projectorや未読込の状態で画像・音声を指定するとINVALID_STATEです。
ailiaLLMSetMultimodalPrompt | ailiaLLMSetPromptJson | |
|---|---|---|
| メッセージ | AILIALLMMultimodalChatMessageの配列 | messages配列のJSON(UTF-8) |
| メディアの位置 | contentの<__media__>プレースホルダー | content配列の順序 |
| 入力形式 | ファイルパス、エンコード済みバッファ、Raw RGB | ファイルパス、Base64(エンコード済みファイル) |
| 生成結果 | ailiaLLMGetDeltaTextを連結 | ailiaLLMGetResponseJsonで取得 |
| Tool Use | ツール設定中はINVALID_STATE | toolsの有無にかかわらず使用可 |
画像・音声を入力するだけであればどちらの方法でも使用できます。Raw RGBを渡す場合はailiaLLMSetMultimodalPromptを、Tool Useと組み合わせる場合はailiaLLMSetPromptJsonを使用してください。
Tool Useの詳細はTool Use ガイドを参照してください。
メッセージのcontentに<__media__>プレースホルダーを含め、対応するメディアをmedia_dataに順番に設定し、media_countに個数を指定します。
メッセージの内容は内部でコピーされるため、呼び出し後に解放できます。
AILIALLMMediaDataのmedia_typeには"image"または"audio"を指定します。画像と音声のどちらも使用できます。メディアは次のいずれかで指定します。
file_path(UTF-8)。dataを指定した場合は無視されます。dataとdata_sizeを指定し、width / heightは0にします。画像はJPG、PNG、TGA、BMP、PSD、GIF、HDR、PIC、音声はWAV、MP3、FLAC等に対応します。dataにwidth * height * 3バイトのRGBデータを指定し、width / heightを設定します。画像のみ使用できます。画像・音声はuserのcontent配列に、textと並べて順番に指定します。
[{"role":"user","content":[
{"type":"text","text":"この画像と音声について説明してください。"},
{"type":"image","file_path":"/path/to/image.jpg"},
{"type":"audio","file_path":"/path/to/audio.wav"}
]}]
image/audioはfile_pathまたはdataをちょうど1つ指定します。dataは標準Base64(パディング付き)で
エンコードしたファイル内容です。JPEG/PNG、WAV/MP3/FLAC等を使用できます。URL取得、動画、生RGB/PCMには対応しません。
画像・音声を含む履歴は毎回再評価します。
モデルとprojectorを読み込み、構造体で画像を渡して応答を生成します。生成結果はGetDeltaTextで連結します。
#include "ailia_llm.h"
#include <cstdio>
#include <string>
#include <vector>
static int open_projector(AILIALLM* llm) {
int status = ailiaLLMOpenMultimodalProjectorFileA(llm, "/path/to/mmproj.gguf");
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
unsigned int vision = 0, audio = 0;
status = ailiaLLMGetMultimodalCapabilities(llm, &vision, &audio);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
// vision_support / audio_support で画像・音声の対応を確認します。
return vision ? AILIA_LLM_STATUS_SUCCESS : AILIA_LLM_STATUS_INVALID_STATE;
}
static int describe_image(AILIALLM* llm) {
AILIALLMMediaData media = {};
media.media_type = "image";
media.file_path = "/path/to/image.jpg"; // dataとdata_sizeでバッファも指定可
AILIALLMMultimodalChatMessage message = {};
message.role = "user";
message.content = "この画像について説明してください。 <__media__>";
message.media_data = &media;
message.media_count = 1;
int status = ailiaLLMSetMultimodalPrompt(llm, &message, 1);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
std::string text;
unsigned int done = 0;
while (!done) {
status = ailiaLLMGenerate(llm, &done);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
if (done) break;
unsigned int size = 0;
status = ailiaLLMGetDeltaTextSize(llm, &size);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
std::vector<char> delta(size);
status = ailiaLLMGetDeltaText(llm, delta.data(), size);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
text += delta.data();
}
printf("%s\n", text.c_str());
return AILIA_LLM_STATUS_SUCCESS;
}
int main() {
AILIALLM* llm = nullptr;
if (ailiaLLMCreate(&llm) != AILIA_LLM_STATUS_SUCCESS) return 1;
int status = ailiaLLMOpenModelFileA(llm, "gemma-4-E2B-it-Q4_K_M.gguf", 4096);
if (status == AILIA_LLM_STATUS_SUCCESS) status = open_projector(llm);
if (status == AILIA_LLM_STATUS_SUCCESS) status = describe_image(llm);
if (status != AILIA_LLM_STATUS_SUCCESS) {
fprintf(stderr, "error %d: %s\n", status, ailiaLLMGetErrorDetail(llm));
}
ailiaLLMDestroy(llm);
return status == AILIA_LLM_STATUS_SUCCESS ? 0 : 1;
}
同じ処理をJSON履歴で行います。履歴の編集にはnlohmann/jsonを使用します。
#include "ailia_llm.h"
#include <nlohmann/json.hpp>
#include <cstdio>
#include <string>
#include <vector>
using json = nlohmann::json;
static int open_projector(AILIALLM* llm) {
int status = ailiaLLMOpenMultimodalProjectorFileA(llm, "/path/to/mmproj.gguf");
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
unsigned int vision = 0, audio = 0;
status = ailiaLLMGetMultimodalCapabilities(llm, &vision, &audio);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
// vision_support / audio_support で画像・音声の対応を確認します。
return vision ? AILIA_LLM_STATUS_SUCCESS : AILIA_LLM_STATUS_INVALID_STATE;
}
static int describe_image_json(AILIALLM* llm) {
json messages = json::array({{{"role", "user"}, {"content", json::array({
{{"type", "text"}, {"text", "この画像について説明してください。"}},
{{"type", "image"}, {"file_path", "/path/to/image.jpg"}}
})}}});
std::string input = messages.dump();
int status = ailiaLLMSetPromptJson(llm, input.c_str());
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
unsigned int done = 0;
while (!done) {
status = ailiaLLMGenerate(llm, &done);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
}
unsigned int size = 0;
status = ailiaLLMGetResponseJsonSize(llm, &size);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
std::vector<char> output(size);
status = ailiaLLMGetResponseJson(llm, output.data(), size);
if (status != AILIA_LLM_STATUS_SUCCESS) return status;
try {
json response = json::parse(output.data());
printf("%s\n", response.at("content").get<std::string>().c_str());
} catch (const json::exception&) {
return AILIA_LLM_STATUS_INVALID_ARGUMENT;
}
return AILIA_LLM_STATUS_SUCCESS;
}
int main() {
AILIALLM* llm = nullptr;
if (ailiaLLMCreate(&llm) != AILIA_LLM_STATUS_SUCCESS) return 1;
int status = ailiaLLMOpenModelFileA(llm, "gemma-4-E2B-it-Q4_K_M.gguf", 4096);
if (status == AILIA_LLM_STATUS_SUCCESS) status = open_projector(llm);
if (status == AILIA_LLM_STATUS_SUCCESS) status = describe_image_json(llm);
if (status != AILIA_LLM_STATUS_SUCCESS) {
fprintf(stderr, "error %d: %s\n", status, ailiaLLMGetErrorDetail(llm));
}
ailiaLLMDestroy(llm);
return status == AILIA_LLM_STATUS_SUCCESS ? 0 : 1;
}