Conversation, LLM ile tek bir durum bilgili görüşmeyi temsil eden üst düzey bir API'dir ve çoğu kullanıcı için önerilen giriş noktasıdır. Dahili olarak Session yönetir ve karmaşık veri işleme görevlerini gerçekleştirir. 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ı içeren ağır bir nesnedir.Conversationoluşturma: Bir veya daha fazla basitConversationnesnesi oluşturmak içinEnginesimgesini kullanın.- Mesaj Gönderme:
Conversationnesnesinin yöntemlerini kullanarak LLM'ye mesaj gönderin ve yanıt alın. Bu sayede sohbet benzeri bir etkileşim sağlanır.
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 olarak yayınlayan, 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 Görüşme'yi kullanma
Conversation API ile ayrıntılı Araç Kullanımı için 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 temsilci, verileri oturuma göndermeden önce bu işlemleri gerçekleştirir.
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 çifti 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ıyarak LiteRT-LM'nin çok çeşitli modelleri desteklemesini sağlar.
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.
Model tarafından kullanılan Jinja şablonu, model dosyası meta verileri tarafından sağlanır.
Not: 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ı, az görevli örnekler 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ş
İleti dizisi, oturumdaki tüm mesaj alışverişlerinin listesini tutar. Bu geçmiş, istem şablonu oluşturma için çok önemlidir. Çünkü jinja istem şablonu, LLM için doğru istemi oluşturmak üzere genellikle tüm görüşme geçmişini gerektirir.
Ancak LiteRT-LM Oturumu durum bilgisi içerir. Bu da girişleri artımlı olarak işlediği anlamına gelir. Bu açığı kapatmak 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: 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 türetildikleri temel modelden farklı yapılandırmalar veya istem şablonları gerektirebilecek ince ayarlı modeller için yararlı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, yanıtın sonunu belirtmek için geri çağırma boş bir
Message(ör.JsonMessage()) ile tetiklenir.
Örnek uygulama için 6. adım: Zaman uyumsuz arama bölümüne bakın.
Not: Geri çağırma tarafından alınan Message yalnızca modelin çıktısının en son bölümünü içerir, ileti geçmişinin tamamını içermez.
Örneğin, bir engelleme 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 işlevi 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, eşzamansız arama tamamlandığında History bölümündeki son giriş olarak kullanılabilir.
İleri Seviye Kullanım {#advanced-usage}
Kısıtlanmış Kod Çözme (Constrained Decoding)
LiteRT-LM, kısıtlanmış kod çözmeyi destekler. Bu sayede, modelin çıkışında belirli yapıları (ör. JSON şemaları, normal ifade kalıpları veya dilbilgisi kuralları) zorunlu kılabilirsiniz.
Özelliği etkinleştirmek için ConversationConfig içinde EnableConstrainedDecoding(true) ayarını yapın ve ConstraintProviderConfig (ör. regex/JSON/dil bilgisi desteği için LlGuidanceConfig) sağlayın. 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 dil bilgisi desteği de dahil olmak üzere tüm ayrıntılar için Kısıtlanmış 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 yakalarsınız, uygulamanızda ilgili işlevi yürütürsünüz ve sonucu modele döndürürsünüz.
Üst düzey akış:
- Araçları Bildirme:
PrefaceJSON'da araçları (ad, açıklama, parametreler) tanımlayın. - Yöntem Çağrılarını Algılama: Yanıtta
model_message["tool_calls"]işaretini kontrol edin. - Yürüt: İstenen araç için uygulama mantığınızı çalıştırın.
- 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.