Перейти до основного вмісту

Запустіть велику мовну модель (LLM) за допомогою Direct API

ПолеЗначення
КатегоріяGenAI
СкладністьСередній
Орієнтовний час читання10 minutes
Міткиgenai, llm, chat, history, streaming

Класичні навчальні матеріали для Model використовують архіви MPK у форматі .tar.gz. Моделі GenAI використовують каталоги моделей LLiMa і API neat::genai замість цього. Почніть з найпростішого запиту: завантажте модель, встановіть request.prompt, запустіть її та виведіть відповідь. Коли це запрацює, перейдіть до використання request.messages, коли вам потрібен стан розмови.

Покроковий огляд

Завантаження каталогу моделі

Вкажіть GenAIModel на розгорнутий каталог моделі LLiMa. У цьому навчальному матеріалі використовується GenAIModel, оскільки він автоматично визначає, чи є каталог моделлю LLM, VLM або ASR.

Створіть simaai::neat::genai::GenAIModel на основі шляху до моделі.

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

Надіслати один запит

Створіть GenerationRequest із prompt і лімітом токенів. Це найшвидший спосіб для одноразових запитань, тестів і сценаріїв.

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";

Перехід до повідомлень

Для запитів у стилі чату використовуйте messages замість prompt: почніть із системного повідомлення та повідомлення користувача, виконайте запит, а потім збережіть відповідь асистента. Модель не запам’ятовує попередні виклики 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});

Отримайте відповідь у вигляді потоку

Для виведення у стилі інтерфейсу користувача викличте 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 завантажте LLM, наприклад Qwen3 4B, з Hugging Face, використовуючи 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;
}
}

Джерело