Conversation është një API e nivelit të lartë, që përfaqëson një bisedë të vetme me LLM dhe është pika e hyrjes e rekomanduar për shumicën e përdoruesve. Ai menaxhon në mënyrë të brendshme një Session dhe trajton detyra komplekse të përpunimit të të dhënave. Këto detyra përfshijnë ruajtjen e kontekstit fillestar, menaxhimin e përkufizimeve të mjeteve, përpunimin paraprak të të dhënave multimodale dhe zbatimin e shablloneve të kërkesave Jinja me formatim mesazhesh të bazuara në role.
Fluksi i Punës së API-së së Bisedës
Cikli jetësor tipik për përdorimin e API-t të bisedës është:
- Krijo një
Engine: Inicializoni njëEnginetë vetëm me shtegun dhe konfigurimin e modelit. Ky është një objekt i rëndë që mban peshat e modelit. - Krijo një
Conversation: PërdorEnginepër të krijuar një ose më shumë objekte të lehtaConversation. - Dërgo Mesazh : Përdorni metodat e objektit të
Conversationpër të dërguar mesazhe te LLM dhe për të marrë përgjigje, duke mundësuar në mënyrë efektive një bashkëveprim të ngjashëm me bisedën.
Më poshtë është mënyra më e thjeshtë për të dërguar mesazh dhe për të marrë përgjigje modeli. Rekomandohet për shumicën e rasteve të përdorimit. Pasqyron API-të e Gemini Chat .
-
SendMessage: Një thirrje bllokuese që merr të dhënat e përdoruesit dhe kthen përgjigjen e plotë të modelit. -
SendMessageAsync: Një thirrje jo-bllokuese që transmeton përgjigjen e modelit mbrapsht token-pas-token-i përmes thirrjeve kthyese.
Ja një shembull i fragmenteve të kodit:
Përmbajtje vetëm me tekst
#include "runtime/engine/engine.h"
// ...
// 1. Define model assets and engine settings.
auto model_assets = ModelAssets::Create(model_path);
CHECK_OK(model_assets);
auto engine_settings = EngineSettings::CreateDefault(
model_assets,
/*backend=*/litert::lm::Backend::CPU);
// 2. Create the main Engine object.
absl::StatusOr<std::unique_ptr<Engine>> engine = Engine::CreateEngine(engine_settings);
CHECK_OK(engine);
// 3. Create a Conversation
auto conversation_config = ConversationConfig::CreateDefault(**engine);
CHECK_OK(conversation_config)
absl::StatusOr<std::unique_ptr<Conversation>> conversation = Conversation::Create(**engine, *conversation_config);
CHECK_OK(conversation);
// 4. Send message to the LLM with blocking call.
absl::StatusOr<Message> model_message = (*conversation)->SendMessage(
JsonMessage{
{"role", "user"},
{"content", "What is the tallest building in the world?"}
});
CHECK_OK(model_message);
// 5. Print the model message.
std::cout << *model_message << std::endl;
// 6. Send message to the LLM with asynchronous call
// where CreatePrintMessageCallback is a users implemented callback that would
// process the message once a chunk of message output is received.
std::stringstream captured_output;
(*conversation)->SendMessageAsync(
JsonMessage{
{"role", "user"},
{"content", "What is the tallest building in the world?"}
},
CreatePrintMessageCallback(std::stringstream& captured_output)
);
// Wait until asynchronous finish or timeout.
*engine->WaitUntilDone(absl::Seconds(10));
Shembull CreatePrintMessageCallback
absl::AnyInvocable<void(absl::StatusOr<Message>)> CreatePrintMessageCallback(
std::stringstream& captured_output) {
return [&captured_output](absl::StatusOr<Message> message) {
if (!message.ok()) {
std::cout << message.status().message() << std::endl;
return;
}
if (auto json_message = std::get_if<JsonMessage>(&(*message))) {
if (json_message->is_null()) {
std::cout << std::endl << std::flush;
return;
}
ABSL_CHECK_OK(PrintJsonMessage(*json_message, captured_output,
/*streaming=*/true));
}
};
}
absl::Status PrintJsonMessage(const JsonMessage& message,
std::stringstream& captured_output,
bool streaming = false) {
if (message["content"].is_array()) {
for (const auto& content : message["content"]) {
if (content["type"] == "text") {
captured_output << content["text"].get<std::string>();
std::cout << content["text"].get<std::string>();
}
}
if (!streaming) {
captured_output << std::endl << std::flush;
std::cout << std::endl << std::flush;
} else {
captured_output << std::flush;
std::cout << std::flush;
}
} else if (message["content"]["text"].is_string()) {
if (!streaming) {
captured_output << message["content"]["text"].get<std::string>()
<< std::endl
<< std::flush;
std::cout << message["content"]["text"].get<std::string>() << std::endl
<< std::flush;
} else {
captured_output << message["content"]["text"].get<std::string>()
<< std::flush;
std::cout << message["content"]["text"].get<std::string>() << std::flush;
}
} else {
return absl::InvalidArgumentError("Invalid message: " + message.dump());
}
return absl::OkStatus();
}
Përmbajtja e të dhënave multimodale
// To use multimodality, the engine must be created with vision and audio
// backend depending on the multimodality to be used
auto engine_settings = EngineSettings::CreateDefault(
model_assets,
/*backend=*/litert::lm::Backend::CPU,
/*vision_backend*/litert::lm::Backend::GPU,
/*audio_backend*/litert::lm::Backend::CPU,
);
// The same steps to create Engine and Conversation as above...
// Send message to the LLM with image data.
absl::StatusOr<Message> model_message = (*conversation)->SendMessage(
JsonMessage{
{"role", "user"},
{"content", { // Now content must be an array.
{
{"type", "text"}, {"text", "Describe the following image: "}
},
{
{"type", "image"}, {"path", "/file/path/to/image.jpg"}
}
}},
});
CHECK_OK(model_message);
// Print the model message.
std::cout << *model_message << std::endl;
// Send message to the LLM with audio data.
model_message = (*conversation)->SendMessage(
JsonMessage{
{"role", "user"},
{"content", { // Now content must be an array.
{
{"type", "text"}, {"text", "Transcribe the audio: "}
},
{
{"type", "audio"}, {"path", "/file/path/to/audio.wav"}
}
}},
});
CHECK_OK(model_message);
// Print the model message.
std::cout << *model_message << std::endl;
// The content can include multiple image or audio data.
model_message = (*conversation)->SendMessage(
JsonMessage{
{"role", "user"},
{"content", { // Now content must be an array.
{
{"type", "text"}, {"text", "First briefly describe the two images "}
},
{
{"type", "image"}, {"path", "/file/path/to/image1.jpg"}
},
{
{"type", "text"}, {"text", "and "}
},
{
{"type", "image"}, {"path", "/file/path/to/image2.jpg"}
},
{
{"type", "text"}, {"text", " then transcribe the content in the audio"}
},
{
{"type", "audio"}, {"path", "/file/path/to/audio.wav"}
}
}},
});
CHECK_OK(model_message);
// Print the model message.
std::cout << *model_message << std::endl;
Përdorni bisedën me mjetet
Ju lutemi referojuni Përdorimit të Avancuar për Përdorimin e detajuar të Mjetit me API-në e Bisedës
Komponentët në bisedë
Conversation mund të konsiderohet si një delegim për përdoruesit për të mirëmbajtur Session dhe përpunimin e ndërlikuar të të dhënave përpara se të dërgohen të dhënat në Sesion.
Llojet e I/O-së
Formati bazë i hyrjes dhe daljes për API-në e bisedës është Message . Aktualisht, ky është implementuar si JsonMessage , i cili është një pseudonim tipi për ordered_json , një strukturë fleksibile e të dhënave çelës-vlerë e ndërthurur.
API-ja e Conversation funksionon në bazë të mesazhit brenda dhe jashtë, duke imituar një përvojë tipike të bisedës. Fleksibiliteti i Message u lejon përdoruesve të përfshijnë fusha arbitrare sipas nevojës nga shabllone specifike të kërkesave ose modele LLM, duke i mundësuar LiteRT-LM të mbështesë një gamë të gjerë modelesh.
Edhe pse nuk ka një standard të vetëm të ngurtë, shumica e shablloneve dhe modeleve të prompt presin që Message të ndjekë konventa të ngjashme me ato të përdorura në Gemini API Content ose në strukturën OpenAI Message .
Message duhet të përmbajë role , që përfaqëson se nga kush është dërguar mesazhi. content mund të jetë aq e thjeshtë sa një varg teksti.
{
"role": "model", // Represent who the message is sent from.
"content": "Hello World!" // Naive text only content.
}
Për futjen e të dhënave multimodale, content është një listë e part . Përsëri, part nuk është një strukturë e të dhënave e paracaktuar, por një lloj i të dhënave çift çelës-vlerë i renditur . Fushat specifike varen nga ajo që presin shablloni i kërkesës dhe modeli.
{
"role": "user",
"content": [ // Multimodal content.
// Now the content is composed of parts
{
"type": "text",
"text": "Describe the image in details: "
},
{
"type": "image",
"path": "/path/to/image.jpg"
}
]
}
Për part multimodale, ne mbështesim formatin e mëposhtëm të trajtuar nga data_utils.h
{
"type": "text",
"text": "this is a text"
}
{
"type": "image",
"path": "/path/to/image.jpg"
}
{
"type": "image",
"blob": "base64 encoded image bytes as string",
}
{
"type": "audio",
"path": "/path/to/audio.wav"
}
{
"type": "audio",
"blob": "base64 encoded audio bytes as string",
}
Shablloni i kërkesës
Për të ruajtur fleksibilitetin për modelet variante, PromptTemplate zbatohet si një mbështjellës i hollë rreth Minja-s . Minja është një zbatim C++ i motorit të shablloneve Jinja , i cili përpunon të dhënat hyrëse JSON për të gjeneruar kërkesa të formatuara.
Motori i shablloneve Jinja është një format i përdorur gjerësisht për shabllonet e prompteve LLM. Ja disa shembuj:
Formati i motorit të shabllonit Jinja duhet të përputhet në mënyrë strikte me strukturën e pritur nga modeli i akorduar sipas udhëzimeve. Zakonisht, versionet e modelit përfshijnë shabllonin standard Jinja për të siguruar përdorimin e duhur të modelit.
Shablloni Jinja i përdorur nga modeli do të ofrohet nga meta të dhënat e skedarit të modelit.
[!SHËNIM] Një ndryshim i vogël në kërkesë për shkak të formatimit të pasaktë mund të çojë në degradim të konsiderueshëm të modelit. Siç raportohet në Kuantifikimin e Ndjeshmërisë së Modeleve të Gjuhës ndaj Karakteristikave të Rreme në Dizajnin e Kërkesës ose: Si mësova të filloj të shqetësohem për formatimin e kërkesës
Parathënie
Preface përcakton kontekstin fillestar për bisedën. Mund të përfshijë mesazhe fillestare, përkufizime mjetesh dhe çdo informacion tjetër në sfond që i nevojitet LLM-së për të filluar bashkëveprimin. Kjo arrin funksionalitet të ngjashëm me Gemini API system instruction dhe Gemini API Tools
Parathënia përmban fushat e mëposhtme
messagesMesazhet në parathënie. Mesazhet siguruan sfondin fillestar për bisedën. Për shembull, mesazhet mund të jenë historiku i bisedës, udhëzime të menjëhershme të sistemit inxhinierik, shembuj të shkurtër, etj.toolsMjetet që modeli mund të përdorë në bisedë. Formati i mjeteve përsëri nuk është i fiksuar, por kryesisht ndjekGemini API FunctionDeclaration.extra_contextKonteksti shtesë që ruan zgjerueshmërinë për modelet për të personalizuar informacionin e kërkuar të kontekstit për të filluar një bisedë. Për shembuj,-
enable_thinkingpër modelet me modalitetin e të menduarit, p.sh. Qwen3 ose SmolLM3-3B .
-
Shembull parathënieje për të ofruar udhëzime fillestare të sistemit, mjete dhe për të çaktivizuar mënyrën e të menduarit.
Preface preface = JsonPreface({
.messages = {
{"role", "system"},
{"content", {"You are a model that can do function calling."}}
},
.tools = {
{
{"name", "get_weather"},
{"description", "Returns the weather for a given location."},
{"parameters", {
{"type", "object"},
{"properties", {
{"location", {
{"type", "string"},
{"description", "The location to get the weather for."}
}}
}},
{"required", {"location"}}
}}
},
{
{"name", "get_stock_price"},
{"description", "Returns the stock price for a given stock symbol."},
{"parameters", {
{"type", "object"},
{"properties", {
{"stock_symbol", {
{"type", "string"},
{"description", "The stock symbol to get the price for."}
}}
}},
{"required", {"stock_symbol"}}
}}
}
},
.extra_context = {
{"enable_thinking": false}
}
});
Histori
Biseda mban një listë të të gjitha shkëmbimeve të Mesazheve brenda seancës. Ky historik është thelbësor për paraqitjen e shpejtë të shabllonit, pasi shablloni i kërkesës jinja zakonisht kërkon të gjithë historikun e bisedës për të gjeneruar kërkesën e saktë për LLM-në.
Megjithatë, LiteRT-LM Session është gjendje-formë, që do të thotë se përpunon të dhënat hyrëse në mënyrë graduale. Për të kapërcyer këtë boshllëk, Conversation gjeneron kërkesën e nevojshme graduale duke e paraqitur shabllonin e kërkesës dy herë: një herë me historikun deri në kthesën e mëparshme dhe një herë duke përfshirë mesazhin aktual. Duke krahasuar këto dy kërkesa të paraqitura, ajo nxjerr pjesën e re që do t'i dërgohet Session .
Konfigurimi i bisedës
ConversationConfig përdoret për të inicializuar një instancë Conversation . Ju mund ta krijoni këtë konfigurim në disa mënyra:
- Nga një
Engine: Kjo metodë përdorSessionConfige parazgjedhur të lidhur me motorin. - Nga një
SessionConfigspecifik: Kjo lejon kontroll më të detajuar mbi cilësimet e sesionit.
Përtej cilësimeve të sesionit, mund ta personalizoni më tej sjelljen e Conversation brenda ConversationConfig . Kjo përfshin:
- Duke ofruar një
Preface. - Duke mbishkruar
PromptTemplatein e parazgjedhur. - Duke mbishkruar
DataProcessorConfig-un e parazgjedhur.
Këto mbishkrime janë veçanërisht të dobishme për modelet e përmirësuara, të cilat mund të kërkojnë konfigurime ose shabllone të ndryshme të kërkesave sesa modeli bazë nga i cili janë nxjerrë.
MesazhCallback
MessageCallback është funksioni i rikthimit të thirrjes që përdoruesit duhet të implementojnë kur përdorin metodën asinkrone SendMessageAsync .
Nënshkrimi i rikthimit të thirrjes është absl::AnyInvocable<void(absl::StatusOr<Message>)> . Ky funksion aktivizohet në kushtet e mëposhtme:
- Kur një pjesë e re e
Messagemerret nga Modeli. - Nëse ndodh një gabim gjatë përpunimit të mesazhit të LiteRT-LM.
- Pas përfundimit të nxjerrjes së përfundimit të LLM-së, thirrja mbrapsht aktivizohet me një
Messagebosh (p.sh.,JsonMessage()) për të sinjalizuar fundin e përgjigjes.
Referojuni thirrjes asinkrone të Hapi 6 për një shembull zbatimi.
[!E RËNDËSISHME]
Messagei marrë nga rikthimi i thirrjes përmban vetëm pjesën më të fundit të rezultatit të modelit, jo të gjithë historikun e mesazheve.
Për shembull, nëse përgjigja e plotë e modelit që pritet nga një thirrje bllokuese SendMessage do të ishte:
{
"role": "model",
"content": [
"type": "text",
"text": "Hello World!"
]
}
Thirrja mbrapsht në SendMessageAsync mund të thirret disa herë, çdo herë me një pjesë pasuese të tekstit:
// 1st Message
{
"role": "model",
"content": [
"type": "text",
"text": "He"
]
}
// 2nd Message
{
"role": "model",
"content": [
"type": "text",
"text": "llo"
]
}
// 3rd Message
{
"role": "model",
"content": [
"type": "text",
"text": " Wo"
]
}
// 4th Message
{
"role": "model",
"content": [
"type": "text",
"text": "rl"
]
}
// 5th Message
{
"role": "model",
"content": [
"type": "text",
"text": "d!"
]
}
Implementuesi është përgjegjës për grumbullimin e këtyre pjesëve nëse nevojitet përgjigja e plotë gjatë rrjedhës asinkrone. Nga ana tjetër, përgjigja e plotë do të jetë e disponueshme si hyrja e fundit në History pasi të përfundojë thirrja asinkrone.
Përdorimi i Avancuar
Dekodimi i Kufizuar
LiteRT-LM mbështet dekodimin e kufizuar, duke ju lejuar të zbatoni struktura specifike në daljen e modelit, siç janë skemat JSON, modelet Regex ose rregullat gramatikore.
Për ta aktivizuar, caktoni EnableConstrainedDecoding(true) në ConversationConfig dhe jepni një ConstraintProviderConfig (p.sh., LlGuidanceConfig për mbështetje regex/JSON/gramatike). Pastaj, kaloni kufizimet nëpërmjet OptionalArgs në SendMessage .
Shembull: Kufizim Regex
LlGuidanceConstraintArg constraint_arg;
constraint_arg.constraint_type = LlgConstraintType::kRegex;
constraint_arg.constraint_string = "a+b+"; // Force output to match this regex
auto response = conversation->SendMessage(
user_message,
{.decoding_constraint = constraint_arg}
);
Për detaje të plota, duke përfshirë mbështetjen për JSON Schema dhe Lark Grammar, shihni dokumentacionin e Dekodimit të Kufizuar .
Përdorimi i mjetit
Thirrja e mjeteve i lejon LLM-së të kërkojë ekzekutimin e funksioneve në anën e klientit. Ju përcaktoni mjetet në Preface e bisedës, duke i shkruar ato me emër. Kur modeli nxjerr një thirrje mjeti, ju e kapni atë, ekzekutoni funksionin përkatës në aplikacionin tuaj dhe ia ktheni rezultatin modelit.
Rrjedha e nivelit të lartë: 1. Deklarimi i mjeteve: Përcaktimi i mjeteve (emri, përshkrimi, parametrat) në Preface JSON. 2. Zbulimi i thirrjeve: Kontrollimi i model_message["tool_calls"] në përgjigje. 3. Ekzekutimi: Ekzekutimi i logjikës së aplikacionit tuaj për mjetin e kërkuar. 4. Përgjigju: Dërgoni një mesazh me role: "tool" që përmban rezultatin e mjetit përsëri te modeli.
Për detaje të plota dhe një shembull të plotë të ciklit të bisedës, shihni dokumentacionin e Përdorimit të Mjetit .