Conversation, LLM ile tek bir durum bilgili etkileşimi temsil eden üst düzey bir API'dir ve çoğu kullanıcı için önerilen giriş noktasıdır. Dahili olarak bir Session yönetir ve karmaşık veri işleme görevlerini yerine getirir. Bu görevler arasında ilk bağlamı koruma, araç tanımlarını yönetme, çok formatlı verileri önceden işleme ve role dayalı mesaj biçimlendirmesiyle Jinja istem şablonlarını uygulama yer alır.
Conversation API İş Akışı
Conversation API'nin tipik kullanım yaşam döngüsü şöyledir:
Engineoluşturma: Model yolu ve yapılandırmayla tek birEnginebaşlatın. Bu, model ağırlıklarını tutan ağır bir nesnedir.Conversationoluşturma: Bir veya daha fazla hafifConversationnesnesi oluşturmak içinEngineöğesini kullanın.- Mesaj Gönderme: LLM'ye mesaj göndermek ve yanıt almak için
Conversationnesnesinin yöntemlerini kullanarak sohbet benzeri bir etkileşim sağlayın.
Aşağıda, mesaj göndermenin ve model yanıtı almanın en basit yolu açıklanmaktadır. Çoğu kullanım alanı için önerilir. Gemini Chat API'lerini yansıtır.
SendMessage: Kullanıcı girişini alan ve modelin yanıtını eksiksiz olarak döndüren bir engelleme çağrısı.SendMessageAsync: Modelin yanıtını geri çağırmalar aracılığıyla jeton jeton geri aktaran, engellemeyen bir çağrı.
Aşağıda örnek kod snippet'leri verilmiştir:
Yalnızca metin içeren içerik
#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));
Örnek 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();
}
Çok formatlı veri içeriği
// 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;
Araçlarla Sohbet'i kullanma
Conversation API ile ayrıntılı Araç Kullanımı için lütfen Gelişmiş Kullanım bölümüne bakın.
Görüşmedeki Bileşenler
Conversation, kullanıcıların Session ve karmaşık veri işlemeyi sürdürmesi için bir temsilci olarak kabul edilebilir. Bu işlem, veriler oturuma gönderilmeden önce yapılır.
G/Ç Türleri
Conversation API'nin temel giriş ve çıkış biçimi Message'dir. Şu anda bu, esnek bir iç içe yerleştirilmiş anahtar/değer veri yapısı olan ordered_json için tür takma adı olan JsonMessage olarak uygulanmaktadır.
Conversation API, mesaj-içinde-mesaj-dışında temeline göre çalışır ve tipik bir sohbet deneyimini taklit eder. Message esnekliği, kullanıcıların belirli istem şablonları veya LLM modelleri tarafından gerektiği şekilde rastgele alanlar eklemesine olanak tanır. Bu sayede LiteRT-LM, çok çeşitli modelleri destekleyebilir.
Tek bir katı standart olmasa da çoğu istem şablonu ve modeli, Message'nin Gemini API İçeriği veya OpenAI Mesaj yapısında kullanılanlara benzer kurallara uymasını bekler.
Message, iletinin kimden gönderildiğini gösteren role değerini içermelidir. content, metin dizesi kadar basit olabilir.
{
"role": "model", // Represent who the message is sent from.
"content": "Hello World!" // Naive text only content.
}
Çok formatlı veri girişi için content, part listesidir. Yine part, önceden tanımlanmış bir veri yapısı değil, sıralı anahtar/değer çifti veri türüdür. Belirli alanlar, istem şablonunun ve modelin beklentisine bağlıdır.
{
"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"
}
]
}
Çok formatlı part için, data_utils.h tarafından işlenen aşağıdaki biçimi destekliyoruz.
{
"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",
}
İstem Şablonu
Varyant modellerinde esnekliği korumak için PromptTemplate, Minja etrafında ince bir sarmalayıcı olarak uygulanır. Minja, biçimlendirilmiş istemler oluşturmak için JSON girişini işleyen Jinja şablon motorunun C++ uygulamasıdır.
Jinja şablon motoru, LLM istem şablonları için yaygın olarak kullanılan bir biçimdir. Aşağıda birkaç örnek verilmiştir:
Jinja şablon motoru biçimi, talimat için ayarlanmış modelin beklediği yapıyla tam olarak eşleşmelidir. Genellikle, model yayınları, modelin uygun şekilde kullanılmasını sağlamak için standart Jinja şablonunu içerir.
Modelin kullandığı Jinja şablonu, model dosyası meta verileri tarafından sağlanır.
[!NOTE] Yanlış biçimlendirme nedeniyle istemde yapılan küçük bir değişiklik, modelin performansında önemli bir düşüşe neden olabilir. Quantifying Language Models' Sensitivity to Spurious Features in Prompt Design or: How I learned to start worrying about prompt formatting (İstem Tasarımında Sahte Özelliklere Karşı Dil Modellerinin Hassasiyetini Ölçme veya: İstem Biçimlendirmesi Hakkında Endişelenmeyi Nasıl Öğrendim) başlıklı makalede belirtildiği gibi
Önsöz
Preface, görüşmenin ilk bağlamını belirler. İlk mesajlar, araç tanımları ve LLM'nin etkileşimi başlatmak için ihtiyaç duyduğu diğer tüm arka plan bilgileri bu kapsamdadır. Bu, Gemini API system instruction ve Gemini API Tools ile benzer işlevler sağlar.
Önsöz aşağıdaki alanları içerir.
messagesÖnsözdeki mesajlar. Mesajlar, görüşmenin ilk arka plan bilgilerini sağladı. Örneğin, iletiler; ileti dizisi geçmişi, istem mühendisliği sistem talimatları, birkaç görevli örnek vb. olabilir.toolsModelin görüşmede kullanabileceği araçlar. Araçların biçimi yine sabit değildir ancak çoğunluklaGemini API FunctionDeclarationbiçimindedir.extra_contextModellerin, sohbete başlamak için gereken bağlam bilgilerini özelleştirmesine olanak tanıyan ek bağlam. Örneğin,enable_thinkingDüşünme moduna sahip modeller için (ör. Qwen3 veya SmolLM3-3B).
İlk sistem talimatını, araçları sağlayan ve düşünme modunu devre dışı bırakan önsöz örneği.
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}
}
});
Geçmiş
Sohbet, oturumdaki tüm mesaj alışverişlerinin listesini tutar. Jinja istem şablonu, LLM için doğru istemi oluşturmak üzere genellikle tüm görüşme geçmişini gerektirdiğinden bu geçmiş, istem şablonu oluşturma için çok önemlidir.
Ancak LiteRT-LM Oturumu durum bilgisine sahiptir. Bu da girişleri artımlı olarak işlediği anlamına gelir. Bu boşluğu doldurmak için Conversation, istem şablonunu iki kez oluşturarak gerekli artımlı istemi oluşturur: bir kez önceki dönüşe kadar olan geçmişle, bir kez de mevcut mesajla birlikte. Bu iki oluşturulmuş istemi karşılaştırarak Oturum'a gönderilecek yeni kısmı ayıklar.
ConversationConfig
ConversationConfig, Conversation örneğini başlatmak için kullanılır. Bu yapılandırmayı birkaç şekilde oluşturabilirsiniz:
Engineüzerinden: Bu yöntemde, motorla ilişkili varsayılanSessionConfigkullanılır.- Belirli bir
SessionConfig: Bu seçenek, oturum ayarları üzerinde daha ayrıntılı kontrol sağlar.
Oturum ayarlarının yanı sıra Conversation davranışını ConversationConfig içinde daha da özelleştirebilirsiniz. Bunlardan bazıları:
Prefacesağlama- Varsayılan
PromptTemplatedeğerinin üzerine yazma. - Varsayılan
DataProcessorConfigdeğerinin üzerine yazma.
Bu geçersiz kılmalar, özellikle temel modelden türetilmiş olmalarına rağmen farklı yapılandırmalar veya istem şablonları gerektirebilecek ince ayarlı modeller için kullanışlıdır.
MessageCallback
MessageCallback, kullanıcıların eşzamansız SendMessageAsync yöntemini kullandıklarında uygulamaları gereken geri çağırma işlevidir.
Geri arama imzası absl::AnyInvocable<void(absl::StatusOr<Message>)>.
Bu işlev aşağıdaki koşullarda tetiklenir:
- Modelden yeni bir
Messageparçası alındığında. - LiteRT-LM'nin mesaj işleme sırasında bir hata oluşursa.
- LLM'nin çıkarımı tamamlandığında geri çağırma, boş bir
Messageile tetiklenir (ör.JsonMessage()) karakterini kullanın.
Örnek uygulama için 6. Adım: Zaman uyumsuz çağrı bölümüne bakın.
[!IMPORTANT] Geri çağırma tarafından alınan
Messageyalnızca modelin çıkışının en son bölümünü içerir, ileti geçmişinin tamamını içermez.
Örneğin, engelleyici SendMessage çağrısından beklenen tam model yanıtı şu şekilde olmalıdır:
{
"role": "model",
"content": [
"type": "text",
"text": "Hello World!"
]
}
SendMessageAsync içindeki geri çağırma, birden çok kez çağrılabilir. Her seferinde metnin sonraki bir parçası kullanılır:
// 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!"
]
}
Uygulayıcı, eşzamansız akış sırasında yanıtın tamamı gerekirse bu parçaları biriktirmekten sorumludur. Alternatif olarak, tam yanıt, asenkron arama tamamlandığında History içindeki son giriş olarak kullanılabilir.
İleri Seviye Kullanım
Kısıtlanmış Kod Çözme (Constrained Decoding)
LiteRT-LM, kısıtlanmış kod çözmeyi destekler. Bu sayede, modelin çıkışında JSON şemaları, normal ifade kalıpları veya dilbilgisi kuralları gibi belirli yapıları zorunlu kılabilirsiniz.
Bu özelliği etkinleştirmek için ConversationConfig bölümünde EnableConstrainedDecoding(true) ayarını yapın ve ConstraintProviderConfig (ör. LlGuidanceConfig için normal ifade/JSON/dil bilgisi desteği). Ardından, SendMessage içinde OptionalArgs aracılığıyla kısıtlamaları iletin.
Örnek: Normal ifade kısıtlaması
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}
);
JSON şeması ve Lark Grammar desteği de dahil olmak üzere tüm ayrıntılar için Sınırlı Kod Çözme dokümanlarına bakın.
Araç Kullanımı
Araç çağrısı, LLM'nin istemci tarafı işlevlerinin yürütülmesini istemesine olanak tanır. Araçları, görüşmenin Preface bölümünde tanımlar ve adlarına göre tuşlarsınız. Model bir araç çağrısı çıkışı verdiğinde bunu yakalar, uygulamanızda ilgili işlevi yürütür ve sonucu modele döndürürsünüz.
Üst düzey akış:
1. Araçları Bildirme: Araçları (ad, açıklama, parametreler) Preface JSON'da tanımlayın.
2. Detect Calls: Yanıtta model_message["tool_calls"] işaretini kontrol edin.
3. Yürüt: İstenen araç için uygulama mantığınızı çalıştırın.
4. Yanıtla: Aracın çıktısını içeren role: "tool" iletisini modele geri gönderin.
Tüm ayrıntılar ve eksiksiz bir sohbet döngüsü örneği için Araç Kullanımı belgelerine bakın.