Conversation adalah API tingkat tinggi, yang mewakili satu percakapan stateful dengan LLM dan merupakan titik entri yang direkomendasikan untuk sebagian besar pengguna. API ini secara internal mengelola Session dan menangani
tugas pemrosesan data yang kompleks. Tugas ini mencakup mempertahankan konteks awal, mengelola definisi alat, melakukan pra-pemrosesan data multimodal, dan menerapkan template perintah Jinja dengan format pesan berbasis peran.
Alur Kerja Conversation API
Siklus proses umum untuk menggunakan Conversation API adalah:
- Membuat
Engine: Menginisialisasi satuEnginedengan jalur dan konfigurasi model. Ini adalah objek berat yang menyimpan bobot model. - Membuat
Conversation: MenggunakanEngineuntuk membuat satu atau beberapa objekConversationringan. - Mengirim Pesan: Menggunakan metode objek
Conversationuntuk mengirim pesan ke LLM dan menerima respons, sehingga memungkinkan interaksi seperti chat.
Berikut adalah cara paling sederhana untuk mengirim pesan dan mendapatkan respons model. Cara ini direkomendasikan untuk sebagian besar kasus penggunaan. Cara ini mencerminkan Gemini Chat API.
SendMessage: Panggilan pemblokiran yang mengambil input pengguna dan menampilkan respons model lengkap.SendMessageAsync: Panggilan non-pemblokiran yang menstreaming respons model kembali token demi token melalui callback.
Berikut adalah contoh cuplikan kode:
Konten teks saja
#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));
Contoh CreatePrintMessageCallback
CreatePrintMessageCallbackabsl::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();
}
🔴 Baru: Prediksi Multi-Token (MTP)
Prediksi Multi-Token (MTP) adalah pengoptimalan performa yang mempercepat kecepatan dekode secara signifikan. MTP direkomendasikan secara universal untuk semua tugas di backend GPU.
Untuk menggunakan MTP, Anda harus mengaktifkan dekode spekulatif di setelan lanjutan konfigurasi mesin.
// 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::GPU);
CHECK_OK(engine_settings);
// 2. Enable MTP via speculative decoding in advanced settings.
litert::lm::AdvancedSettings advanced_settings;
advanced_settings.enable_speculative_decoding = true;
engine_settings->GetMutableMainExecutorSettings().SetAdvancedSettings(
advanced_settings);
// 3. Create the main Engine object.
absl::StatusOr<std::unique_ptr<Engine>> engine = Engine::CreateEngine(
*engine_settings);
CHECK_OK(engine);
// The same steps to create Conversation and send messages as above...
Konten data multimodal
// 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;
Menggunakan Percakapan dengan Alat
Lihat Penggunaan Lanjutan untuk Penggunaan Alat mendetail dengan Conversation API
Komponen dalam Percakapan
Conversation dapat dianggap sebagai delegasi bagi pengguna untuk
mempertahankan Session dan pemrosesan data yang rumit sebelum mengirim
data ke Sesi.
Jenis I/O
Format input dan output inti untuk Conversation API adalah
Message. Saat ini, format ini diimplementasikan sebagai
JsonMessage, yang merupakan alias jenis untuk
ordered_json, struktur data key-value pair bertingkat yang fleksibel.
The Conversation API beroperasi berdasarkan pesan masuk-pesan keluar
basis, yang meniru pengalaman chat yang umum. Fleksibilitas
Message memungkinkan pengguna menyertakan kolom arbitrer sesuai kebutuhan
template perintah atau model LLM tertentu, sehingga LiteRT-LM dapat mendukung berbagai
model.
Meskipun tidak ada satu standar yang kaku, sebagian besar template dan model perintah
mengharapkan Message mengikuti konvensi yang mirip dengan yang digunakan dalam
Konten Gemini API atau struktur
Pesan OpenAI.
Message harus berisi role, yang menunjukkan dari siapa pesan dikirim. content bisa sesederhana string teks.
{
"role": "model", // Represent who the message is sent from.
"content": "Hello World!" // Naive text only content.
}
Untuk input data multimodal, content adalah daftar part. Sekali lagi, part bukanlah
struktur data yang telah ditentukan, tetapi
jenis data key-value pair yang diurutkan. Kolom tertentu bergantung pada apa yang diharapkan oleh template perintah dan model.
{
"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"
}
]
}
Untuk part multimodal, kami mendukung format berikut yang ditangani oleh
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",
}
Template Perintah
Untuk mempertahankan fleksibilitas model varian, PromptTemplate di implementasikan sebagai wrapper tipis di sekitar Minja. Minja adalah implementasi C++ dari mesin template Jinja, yang memproses input JSON untuk menghasilkan perintah yang diformat.
Mesin template Jinja adalah format yang banyak digunakan untuk template perintah LLM. Berikut beberapa contohnya:
Format mesin template Jinja harus sesuai dengan struktur yang diharapkan oleh model yang disesuaikan dengan petunjuk. Biasanya, rilis model menyertakan template Jinja standar untuk memastikan penggunaan model yang tepat.
Template Jinja yang digunakan oleh model akan disediakan oleh metadata file model.
Catatan: Perubahan kecil dalam perintah karena format yang salah dapat menyebabkan penurunan kualitas model yang signifikan. Seperti yang dilaporkan dalam Mengukur Sensitivitas Model Bahasa terhadap Fitur Palsu dalam Desain Perintah atau: Cara Saya Belajar untuk Mulai Mengkhawatirkan Format Perintah
Pengantar
Preface menetapkan konteks awal untuk percakapan. Konteks ini dapat mencakup pesan awal, definisi alat, dan informasi latar belakang lainnya yang diperlukan LLM untuk memulai interaksi. Hal ini mencapai fungsi yang mirip dengan
the
Gemini API system instruction
dan Gemini API Tools
Pengantar berisi kolom berikut
messagesPesan dalam pengantar. Pesan tersebut memberikan latar belakang awal untuk percakapan. Misalnya, pesan dapat berupa histori percakapan, petunjuk sistem rekayasa perintah, contoh beberapa kali, dll.toolsAlat yang dapat digunakan model dalam percakapan. Format alat tidak tetap, tetapi sebagian besar mengikutiGemini API FunctionDeclaration.extra_contextKonteks tambahan yang mempertahankan kemampuan model untuk menyesuaikan informasi konteks yang diperlukan untuk memulai percakapan. Misalnya,enable_thinkinguntuk model dengan mode berpikir, misalnya Qwen3 atau SmolLM3-3B.
Contoh pengantar untuk memberikan petunjuk sistem awal, alat, dan menonaktifkan mode berpikir.
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
Percakapan mempertahankan daftar semua pertukaran Pesan dalam sesi. Histori ini sangat penting untuk rendering template perintah, karena template perintah jinja biasanya memerlukan seluruh histori percakapan untuk menghasilkan perintah yang benar untuk LLM.
Namun, Sesi LiteRT-LM bersifat stateful, yang berarti sesi ini memproses input secara bertahap. Untuk menjembatani kesenjangan ini, Percakapan menghasilkan perintah inkremental yang diperlukan dengan merender template perintah dua kali: sekali dengan histori hingga giliran sebelumnya, dan sekali termasuk pesan saat ini. Dengan membandingkan kedua perintah yang dirender ini, perintah tersebut akan mengekstrak bagian baru yang akan dikirim ke Sesi.
ConversationConfig
ConversationConfig digunakan untuk menginisialisasi instance
Conversation. Anda dapat membuat konfigurasi ini dengan beberapa cara:
- Dari
Engine: Metode ini menggunakanSessionConfigdefault yang terkait dengan mesin. - Dari
SessionConfigtertentu: Hal ini memungkinkan kontrol yang lebih terperinci atas setelan sesi.
Selain setelan sesi, Anda dapat menyesuaikan lebih lanjut perilaku
Conversation dalam
ConversationConfig. Hal ini mencakup:
- Menyediakan
Preface. - Menimpa
PromptTemplatedefault. - Menimpa
DataProcessorConfigdefault.
Penimpaan ini sangat berguna untuk model yang disesuaikan, yang mungkin memerlukan konfigurasi atau template perintah yang berbeda dari model dasar yang digunakan.
MessageCallback
MessageCallback adalah fungsi callback yang harus diterapkan pengguna saat menggunakan metode SendMessageAsync asinkron.
Tanda tangan callback adalah absl::AnyInvocable<void(absl::StatusOr<Message>)>.
Fungsi ini dipicu dalam kondisi berikut:
- Saat bagian baru
Messagediterima dari Model. - Jika terjadi error selama pemrosesan pesan LiteRT-LM.
- Setelah penyelesaian inferensi LLM, callback akan dipicu dengan
kosong
Message(misalnya,JsonMessage()) untuk menandakan akhir respons.
Lihat panggilan asinkron Langkah 6 untuk contoh implementasi.
Catatan:
Message
yang diterima oleh callback hanya berisi bagian terbaru dari output model,
bukan seluruh histori pesan.
Misalnya, jika respons model lengkap yang diharapkan dari panggilan pemblokiran
SendMessage adalah:
{
"role": "model",
"content": [
"type": "text",
"text": "Hello World!"
]
}
Callback di SendMessageAsync mungkin dipanggil beberapa
kali, setiap kali dengan bagian teks berikutnya:
// 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!"
]
}
Implementer bertanggung jawab untuk mengumpulkan bagian-bagian ini jika respons lengkap diperlukan selama streaming asinkron. Atau, respons lengkap akan tersedia sebagai entri terakhir di History setelah panggilan asinkron selesai.
Penggunaan Lanjutan
Dekode Terbatas
LiteRT-LM mendukung dekode terbatas, yang memungkinkan Anda menerapkan struktur tertentu pada output model, seperti skema JSON, pola Regex, atau aturan tata bahasa.
Untuk mengaktifkannya, tetapkan EnableConstrainedDecoding(true) di ConversationConfig dan berikan ConstraintProviderConfig (misalnya, LlGuidanceConfig untuk dukungan regex/JSON/tata bahasa). Kemudian, teruskan batasan melalui OptionalArgs di SendMessage.
Contoh: Batasan 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}
);
Untuk mengetahui detail selengkapnya, termasuk dukungan Skema JSON dan Tata Bahasa Lark, lihat dokumentasi Dekode Terbatas.
Penggunaan Alat
Panggilan alat memungkinkan LLM meminta eksekusi fungsi sisi klien. Anda menentukan alat di Preface percakapan, dengan memberi kunci berdasarkan nama. Saat model menampilkan panggilan alat, Anda akan mengambilnya, menjalankan fungsi yang sesuai di aplikasi, dan menampilkan hasilnya ke model.
Alur tingkat tinggi:
- Mendeklarasikan Alat: Menentukan alat (nama, deskripsi, parameter) dalam JSON
Preface. - Mendeteksi Panggilan: Memeriksa
model_message["tool_calls"]dalam respons. - Menjalankan: Menjalankan logika aplikasi untuk alat yang diminta.
- Merespons: Mengirim pesan dengan
role: "tool"yang berisi output alat kembali ke model.
Untuk mengetahui detail selengkapnya dan contoh loop chat lengkap, lihat dokumentasi Penggunaan Alat.