본문으로 건너뛰기

직접 GenAI API

LLM, VLM 또는 ASR 모델이 애플리케이션과 동일한 프로세스에서 실행될 때 Neat의 직접 GenAI API를 사용합니다. 배포된 LLiMa 모델 디렉터리를 로드하고, GenerationRequest를 생성한 다음, 완전한 결과가 나올 때까지 기다리거나 결과가 생성되는 대로 토큰을 스트리밍합니다.

브라우저, 서비스 또는 원격 클라이언트가 HTTP를 통해 모델을 호출해야 하는 경우 대신 GenAI 서버를 사용합니다. 두 애플리케이션 경계 중에서 선택하는 방법에 대한 도움은 GenAI 모델 개요를 참조하십시오.

적절한 핸들 선택

대부분의 애플리케이션의 경우 GenAIModel부터 시작합니다. 이 핸들은 모델 디렉터리에서 모델 작업을 자동으로 감지하고 다음과 같은 기능 확인을 제공합니다.

  • accepts_text()
  • accepts_image()
  • accepts_audio()
  • task()
  • model_id()

애플리케이션이 로드하는 내용을 알고 더 좁은 범위의 API를 사용하려는 경우 작업별 핸들을 사용합니다.

핸들사용 용도
genai::GenAIModel자동으로 감지된 LLM, VLM 또는 ASR 모델 디렉터리
genai::VisionLanguageModel텍스트 전용 LLM 및 이미지 처리 기능이 있는 VLM
genai::ASRModel음성-텍스트 모델

텍스트 요청 실행

#include "neat/genai.h"

#include <iostream>

int main() {
simaai::neat::genai::GenAIModel model(
"/media/nvme/llima/models/Qwen3-4B-Instruct-2507-GPTQ-a16w4");

simaai::neat::genai::GenerationRequest request;
request.prompt = "Explain what an API gateway is in one sentence.";
request.max_new_tokens = 64;

auto result = model.run(request);
std::cout << result.text << "\n";
}

run()은 동기 방식으로 작동합니다. 즉, 생성 작업이 완료된 후에 반환됩니다. 이는 테스트, 스크립트 및 요청/응답 애플리케이션 코드에 사용하기 가장 간단한 형태입니다.

생성된 토큰 스트림

호출자가 생성되는 대로 출력을 확인해야 하는 경우 stream()을 사용합니다. 각 항목은 최신 텍스트 조각, 현재 지표 및 생성 완료 시의 최종 상태를 포함하는 TokenSample입니다.

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

simaai::neat::genai::GenerationStream stream_handle = model.stream(request);
for (const auto& token : stream_handle) {
std::cout << token.text << std::flush;
}
std::cout << "\n";

사용자가 요청을 닫거나, 프롬프트를 변경하거나, 애플리케이션에서 이미지 생성 시간이 초과되면 스트림에서 cancel()을 호출합니다.

VLM에 이미지 추가

VLM은 텍스트와 하나 이상의 이미지를 입력으로 받습니다. 이미지는 간단한 프롬프트에 대해 GenerationRequest.images를 통해 전달되거나, 채팅 기록을 사용할 때 ChatMessage.images를 통해 전달됩니다.

Tensor 값으로 전달되는 이미지는 uint8 형식의 HWC RGB 텐서여야 합니다. OpenCV cv::Mat 입력은 Neat/OpenCV 규칙을 따릅니다. 즉, 3채널 행렬은 BGR로 처리되고 요청에 저장되기 전에 RGB로 변환됩니다.

simaai::neat::genai::VisionLanguageModel model(
"/media/nvme/llima/models/Qwen3-VL-4B-Instruct-GPTQ-a16w4");

cv::Mat image = cv::imread("scene.jpg");

simaai::neat::genai::GenerationRequest request;
request.prompt = "What is visible in this image?";
request.images = {image};
request.max_new_tokens = 128;

auto result = model.run(request);
std::cout << result.text << "\n";

동일한 이미지에 대한 반복적인 질문의 경우, VisionLanguageModel.encode(...)는 모델 내에서 이미지 임베딩을 캐시할 수 있습니다. 그런 다음 request.use_cached_images = true를 설정하거나 use_cached_images = true를 사용하여 채팅 메시지를 보냅니다.

LoRA 어댑터 전환

LLiMa의 LORA_BRANCH 모드로 컴파일된 모델은 npy_files/<adapter-name>에 저장된 호환 가능한 어댑터를 전환할 수 있습니다.

model.set_lora("customer-adapter");
auto adapted = model.run(request);
model.unset_lora();

어댑터 이름은 경로가 아닌 단일 디렉터리 이름이어야 합니다. 전환 작업은 현재 진행 중인 모든 생성 작업이 완료될 때까지 기다린 후 다음 요청 전에 언어 모델의 캐시된 토큰 상태를 지웁니다. ASR, 추론 디코딩 패키지 또는 영구적으로 병합된 LORA_MERGED 가중치에 대해서는 동적 전환을 사용할 수 없습니다.

오디오 텍스트 변환

ASR 모델은 동일한 요청/결과 형식을 사용하지만, 요청에는 오디오가 포함되어야 합니다. 파일 경로를 사용하려면 audio_file을, 오디오 텐서를 사용하려면 audio를 사용하십시오.

simaai::neat::genai::ASRModel model("/media/nvme/llima/models/whisper-model");

simaai::neat::genai::GenerationRequest request;
request.audio_file = "meeting.wav";

auto result = model.run(request);
std::cout << result.text << "\n";
std::cout << "language=" << result.language << "\n";

기본 언어는 auto이므로, 다국어 Whisper 모델은 원본 언어를 감지합니다. 원본 언어를 알고 있을 때는 request.language를 지원되는 언어 코드 또는 이름으로 설정합니다. 또한, 로드된 Whisper 아티팩트가 해당 정보를 제공하는 경우 결과에는 no_speech_probavg_logprob가 표시됩니다. no_speech_prob 값이 높을수록 입력에 음성이 포함될 가능성이 낮습니다. avg_logprob 값이 높을수록(음수가 적을수록) Whisper가 생성된 토큰에 더 높은 평균 확률을 할당했음을 의미합니다.

음성을 영어로 번역하려면 번역 작업을 선택하세요.

request.asr_task = simaai::neat::genai::ASRTask::Translate;
auto result = model.run(request);

GenAI를 그래프로 구성하기

대부분의 GenAI 애플리케이션에서 직접 run() / stream()을 호출하는 것이 가장 빠른 방법입니다. GenAI가 더 큰 Neat 그래프의 한 단계일 경우, 공개 그래프 조각을 사용하세요.

  • genai::graphs::VisionLanguage(...)
  • genai::graphs::SpeechTranscriber(...)

이러한 조각은 명명된 그래프 엔드포인트를 통해 GenAI 단계를 노출하므로 나머지 Neat에서 사용하는 동일한 GraphRun 모델과 함께 구성할 수 있습니다.

ASR 그래프의 경우, SpeechTranscriberOptions는 기본적으로 자동 소스 언어 감지 및 전사를 사용합니다. 명시적으로 번역을 선택하세요.

auto model = std::make_shared<simaai::neat::genai::ASRModel>(
"/media/nvme/llima/models/whisper-small-a16w8");

simaai::neat::genai::SpeechTranscriberOptions options;
options.task = simaai::neat::genai::ASRTask::Translate;
options.streaming = true;

auto fragment = simaai::neat::genai::graphs::SpeechTranscriber(model, options);

이 프래그먼트는 audioaudio_path를 허용합니다. 최종 done 번들에는 모델이 프로브 출력을 제공할 때 text, finish_reason, language, no_speech_probavg_logprob가 포함됩니다.

auto model = std::make_shared<simaai::neat::genai::VisionLanguageModel>(
"/media/nvme/llima/models/Qwen3-VL-4B-Instruct-GPTQ-a16w4");

simaai::neat::genai::VisionLanguageOptions options;
options.max_new_tokens = 128;
options.streaming = true;

simaai::neat::Graph fragment =
simaai::neat::genai::graphs::VisionLanguage(model, options, "vlm");

요청 규칙

GenerationRequest는 의도적으로 명확하게 정의되어 있습니다.

  • prompt 또는 messages 중 하나만 사용하고, 둘 다 사용하지 마십시오.
  • system_promptprompt와 함께만 사용하십시오.
  • 이미지는 prompt를 사용하여 직접 첨부하고, 메시지별 이미지는 ChatMessage.images를 통해 첨부하십시오.
  • 직접 이미지 또는 캐시된 이미지 중 하나만 사용하고, 둘 다 사용하지 마십시오.
  • ASR 요청은 텍스트 또는 이미지 필드가 아닌 오디오 필드를 사용합니다.

Qwen3 또는 Gemma 4 E2B/E4B와 같은 추론 모델의 경우 GenerationRequest.enable_thinking를 설정하여 추론을 활성화하십시오. 완전한 결과는 GenerationResult.reasoning에 추론 내용을, 최종 답변은 GenerationResult.text에 저장합니다. 스트리밍되는 TokenSample 객체도 마찬가지로 reasoningtext를 사용합니다.

이러한 규칙을 통해 Neat는 모호한 프롬프트를 런타임에 전송하는 대신 명확한 요청 오류와 함께 조기에 실패할 수 있습니다.

다음 단계