メインコンテンツまでスキップ

Direct API を使用して LLM を実行する

項目
カテゴリGenAI
難易度中級
推定所要時間10 minutes
ラベルgenai, llm, chat, history, streaming

従来の Model チュートリアルでは、.tar.gz MPK アーカイブを使用します。GenAI モデルでは、代わりに LLiMa モデルディレクトリと neat::genai API を使用します。最小限のリクエストから始めます。モデルをロードし、request.prompt を設定し、実行して、回答を出力します。それが機能したら、会話の状態が必要な場合は、request.messages に切り替えます。

ウォークスルー

モデルディレクトリをロードする

デプロイされた LLiMa モデルディレクトリを GenAIModel に指定します。このチュートリアルでは、GenAIModel を使用します。これは、ディレクトリが LLM、VLM、または ASR モデルであるかどうかを自動的に検出するためです。

モデルのパスから simaai::neat::genai::GenAIModel を構築します。

tutorials/019_run_an_llm/run_an_llm.cpp
genai::GenAIModel model(args.model);

1 つのプロンプトを送信する

prompt とトークン予算を使用して、GenerationRequest を構築します。これは、単発の質問、テスト、およびスクリプトにとって最も簡単な方法です。

tutorials/019_run_an_llm/run_an_llm.cpp
genai::GenerationRequest request;
request.prompt = "Give me three practical tips for designing a small REST API.";
request.max_new_tokens = 96;

const genai::GenerationResult first = model.run(request);
std::cout << "assistant: " << first.text << "\n\n";

システムプロンプトを定義する

短いシステム指示を使用して、モデルの動作を制御します。system_prompt を使用して、単純なプロンプト要求に添付できます。チャット履歴に切り替える場合は、同じ指示をメッセージリストの system メッセージとして保持します。

tutorials/019_run_an_llm/run_an_llm.cpp
const std::string system_prompt = "You are concise and practical.";

genai::GenerationRequest concise_request;
concise_request.system_prompt = system_prompt;
concise_request.prompt = "Give me one rule of thumb for designing a small REST API.";
concise_request.max_new_tokens = 64;

const genai::GenerationResult concise = model.run(concise_request);
std::cout << "assistant: " << concise.text << "\n\n";

メッセージに切り替える

チャットスタイルの要求の場合、prompt ではなく、messages を使用します。システムメッセージとユーザーメッセージから開始し、要求を実行し、アシスタントの応答を保存します。モデルは、それ自体で以前の run() 呼び出しを記憶しません。アプリケーションがメッセージ履歴を管理します。

tutorials/019_run_an_llm/run_an_llm.cpp
std::vector<genai::ChatMessage> messages;
messages.push_back(genai::ChatMessage{
.role = "system",
.content = system_prompt,
});
messages.push_back(genai::ChatMessage{
.role = "user",
.content = "Give me three practical tips for writing API documentation.",
});

genai::GenerationRequest chat_request;
chat_request.messages = messages;
chat_request.max_new_tokens = 96;

const genai::GenerationResult chat_result = model.run(chat_request);
std::cout << "assistant: " << chat_result.text << "\n\n";
messages.push_back(genai::ChatMessage{.role = "assistant", .content = chat_result.text});

履歴を使用してフォローアップを行う

別のユーザーメッセージを追加し、更新されたメッセージリストを送信し、回答を読み取ります。モデルは、アプリケーションが保持している完全な会話を認識します。

tutorials/019_run_an_llm/run_an_llm.cpp
messages.push_back(genai::ChatMessage{
.role = "user",
.content = "Which tip should I apply first for a prototype?",
});

genai::GenerationRequest follow_up;
follow_up.messages = messages;
follow_up.max_new_tokens = 96;

const genai::GenerationResult second = model.run(follow_up);
std::cout << "assistant: " << second.text << "\n\n";
messages.push_back(genai::ChatMessage{.role = "assistant", .content = second.text});

回答をストリーミングする

UI スタイルの出力の場合、stream() を呼び出し、返された GenerationStream を反復処理します。各トークンサンプルには、最新のテキストフラグメントが含まれます。

tutorials/019_run_an_llm/run_an_llm.cpp
messages.push_back(genai::ChatMessage{
.role = "user",
.content = "Turn that advice into a short checklist.",
});

genai::GenerationRequest streaming_request;
streaming_request.messages = messages;
streaming_request.max_new_tokens = 96;

genai::GenerationStream stream_handle = model.stream(streaming_request);
std::cout << "assistant: ";
for (const genai::TokenSample& token : stream_handle) {
std::cout << token.text << std::flush;
}
std::cout << "\n";

実行

Modalix DevKit で、Hugging Face から Qwen3 4B などの LLM を LLiMa CLI を使用してダウンロードします。

llima pull Qwen3-4B-Instruct-2507-GPTQ-a16w4

Modalixのチュートリアルを、DevKitのローカルモデルディレクトリを使用して実行します。

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_019_run_an_llm \
--model /media/nvme/llima/models/Qwen3-4B-Instruct-2507-GPTQ-a16w4

C++ (build from source):

./build.sh --target tutorial_019_run_an_llm
./build/tutorials-standalone/tutorial_019_run_an_llm \
--model /media/nvme/llima/models/Qwen3-4B-Instruct-2507-GPTQ-a16w4

期待される出力は、単純なプロンプトへの回答、システムプロンプトによる回答、文脈を考慮したフォローアップ、およびストリーミング形式の最終的な応答です。

実践

アプリケーションに必要な量のメッセージ履歴のみ保持します。長い履歴はコンテキストトークンを消費し、最初のトークンまでの時間を増加させます。永続的なチャットアプリケーションの場合、会話をモデルオブジェクトの外に保存し、各ターンでGenerationRequest.messagesを再構築します。

完全なソース

完全なソースプログラムを表示
tutorials/019_run_an_llm/run_an_llm.cpp
#include "neat/genai.h"

#include <filesystem>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

namespace genai = simaai::neat::genai;

struct Args {
std::filesystem::path model;
};

Args parse_args(int argc, char** argv) {
Args args;
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
if (arg == "--model" && i + 1 < argc) {
args.model = argv[++i];
} else {
throw std::runtime_error("usage: run_an_llm --model <llima_model_dir>");
}
}
if (args.model.empty()) {
throw std::runtime_error("missing required --model <llima_model_dir>");
}
return args;
}

int main(int argc, char** argv) {
try {
const Args args = parse_args(argc, argv);

genai::GenAIModel model(args.model);

genai::GenerationRequest request;
request.prompt = "Give me three practical tips for designing a small REST API.";
request.max_new_tokens = 96;

const genai::GenerationResult first = model.run(request);
std::cout << "assistant: " << first.text << "\n\n";

const std::string system_prompt = "You are concise and practical.";

genai::GenerationRequest concise_request;
concise_request.system_prompt = system_prompt;
concise_request.prompt = "Give me one rule of thumb for designing a small REST API.";
concise_request.max_new_tokens = 64;

const genai::GenerationResult concise = model.run(concise_request);
std::cout << "assistant: " << concise.text << "\n\n";

std::vector<genai::ChatMessage> messages;
messages.push_back(genai::ChatMessage{
.role = "system",
.content = system_prompt,
});
messages.push_back(genai::ChatMessage{
.role = "user",
.content = "Give me three practical tips for writing API documentation.",
});

genai::GenerationRequest chat_request;
chat_request.messages = messages;
chat_request.max_new_tokens = 96;

const genai::GenerationResult chat_result = model.run(chat_request);
std::cout << "assistant: " << chat_result.text << "\n\n";
messages.push_back(genai::ChatMessage{.role = "assistant", .content = chat_result.text});

messages.push_back(genai::ChatMessage{
.role = "user",
.content = "Which tip should I apply first for a prototype?",
});

genai::GenerationRequest follow_up;
follow_up.messages = messages;
follow_up.max_new_tokens = 96;

const genai::GenerationResult second = model.run(follow_up);
std::cout << "assistant: " << second.text << "\n\n";
messages.push_back(genai::ChatMessage{.role = "assistant", .content = second.text});

messages.push_back(genai::ChatMessage{
.role = "user",
.content = "Turn that advice into a short checklist.",
});

genai::GenerationRequest streaming_request;
streaming_request.messages = messages;
streaming_request.max_new_tokens = 96;

genai::GenerationStream stream_handle = model.stream(streaming_request);
std::cout << "assistant: ";
for (const genai::TokenSample& token : stream_handle) {
std::cout << token.text << std::flush;
}
std::cout << "\n";

return 0;
} catch (const std::exception& e) {
std::cerr << "error: " << e.what() << "\n";
return 1;
}
}

ソース