Transformers documentation
Yanıt Çözümleme
Yanıt Çözümleme
Sohbet modellerinin tek bir yanıt dizgesi yerine yapılandırılmış çıktılar üretmesi giderek daha yaygın hale gelmektedir. Örneğin, bir reasoning model akıl yürütme izini içeren bir düşünce zinciri üretebilirken, bir tool calling modeli işlev adları ve argümanları üretebilir.
Ancak yapılandırılmış çıktılardaki sorun, LLM çıktılarının doğası gereği yapılandırılmış olmamasıdır. LLM API'leri genellikle role, content ve reasoning_content gibi anahtarlara sahip mesaj sözlüklerini kabul eder ve döndürür; ancak LLM'ler içsel olarak aslında tek bir token dizisini devam ettirir. Kullanıcıya yönelik API'yi modelin gerçek token akışına bağlamak için bir yapıştırıcı katmanı kullanırız. Girdileri bir token akışına dönüştürmek için, diğer belgelerde ele alınan chat_templates'i kullanırız. Bu belge, o yapıştırıcı katmanının diğer yarısı olan, modelin ürettiği tokenları tekrar yapılandırılmış bir yanıt sözlüğüne dönüştüren Response templates sistemini konu almaktadır.
Birçok açıdan, yanıt şablonları sohbet şablonlarının tersine bir işlemi gerçekleştirir. Sohbet şablonlarında bir mesaj listesi girilir ve modele girdi olarak verilmeye hazır token'lar elde edilir. Yanıt şablonlarında ise ham model çıktı token'ları girilir ve yapılandırılmış bir mesaj elde edilir. Sohbet şablonlarında olduğu gibi, yanıt şablonları da kullanıcıların bir modelin hangi belirli formatları ve kontrol token'larını beklediğine dair karmaşık detayları görmezden gelmesine ve herhangi bir modelle çalışan evrensel bir mesaj sözlüğü API'sini kullanmasına olanak tanır.
Yanıt şablonlarını anlamak için en iyi yol, onları uygulamada görmektir. Ana giriş noktası, tek bir dizi veya bir parti kabul eden parse_response() yöntemidir:
from transformers import AutoModelForCausalLM, AutoTokenizer
checkpoint = "HuggingFaceTB/SmolLM3-3B"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForCausalLM.from_pretrained(checkpoint, dtype="auto", device_map="auto")
messages = [{"role": "user", "content": "Summarize the end of the Cold War, very briefly."}]
input_ids = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")["input_ids"].to(model.device)
outputs = model.generate(input_ids, max_new_tokens=1024)[0, input_ids.shape[1]:]
out_text = tokenizer.decode(outputs)
print(tokenizer.parse_response(out_text, prefix=input_ids[0]))
# Outputs a structured dict: {"role": "assistant", "reasoning_content": "...", "content": "..."}Bir tokenizer'ın response_template'i varsa, parse_response metodu bir çıktı mesajını sohbet eklenmeye hazır, yapılandırılmış bir dict'e temiz bir şekilde dönüştürür. Bu metoda prefix'i (istek token'ları) de iletmemiz gerekir. Bunun nedeni, birçok sohbet şablonunun modelin yanıtına başlamasına izin vermeden önce mesajları başlatması veya düşünme bloklarını açmasıdır; bu yüzden ayrıştırıcımızın mesajı anlamak için isteği görmesi gerekir. Son turdan önceki tüm prefix atılır; bir seferde yalnızca bir mesaj ayrıştırılır. Yalnızca son mesajın tamamını gördüğümüzden ve önceden doldurulmuş alanları kaçırmadığımızdan emin olmak için prefix'e ihtiyacımız var!
Eksik bir önek, önceden doldurulmuş bir mesajın sessizce yanlış ayrıştırılmasına neden olabileceğinden prefix zorunludur. Bunu atlamak bir hata oluşturur. Üretim zaten tam mesajı içerdiğinden ve önek bağlamına gerek olmadığından emin olduğunuz nadir durumlarda, açıkça bu seçeneği devre dışı bırakmak için prefix="" (veya boş bir token kimliği listesi) geçirin.
Eğer tokenizer için bir yanıt şablonu ayarlanmamışsa, parse_response bir hata oluşturur. Daha fazla modele şablon eklemek için elimizden geldiğince hızlı çalışıyoruz!
Akış yanıtı ayrıştırma
Yukarıdaki örnekte, model yanıtı, üretim tamamlandıktan sonra tek seferde ayrıştırılır. Ancak çoğu zaman, özellikle modelin bitmesini beklerken bir-iki dakika boyunca statik bir sayfa göstermek istemediğimiz kullanıcıya yönelik uygulamalarda, mesajların üretilirken kısmi olarak ayrıştırılmasını isteyebiliriz.
Akış ayrıştırma istediğinizde, ResponseParser döndüren tokenizer.get_response_parser() çağrısını yapın.
parse_response ile olduğu gibi, ayrıştırıcının sohbet şablonu tarafından önceden doldurulan mesaj bölümlerinden haberdar olması için sohbet istemini prefix= olarak iletin. Döndürülen nesne, model metin ürettikçe içine metin besleyebileceğiniz durumlu bir ayrıştırıcıdır:
parser = tokenizer.get_response_parser(prefix=input_ids[0])
for event in parser.initial_events:
render(event) # Display the partial message to the user however you want to
for chunk in model_output:
for event in parser.feed(chunk):
render(event)
message, final_events = parser.finalize()
for event in final_events:
render(event)İstek araçları içeriyorsa, bunları da iletin (get_response_parser(..., tools=tools)).
Araç çağrısı argümanları, her bölge kapanırken çağrılan aracın JSON Şeması'ndan tip olarak alınır, bu nedenle
akış tüketicileri, yalnızca finalize() sonrasında değil, region_close anında şema tipine sahip argümanları görür.
Ayrıntılar için Araç çağrısı argümanlarını tiplendirme bölümüne bakın.
Ayrıştırıcı, üretim sürecinden gelen metinle birlikte olayları yayınlar. Bu, şu anda üretilen bölgenin hangisi olduğunu gösterir. Bölge tamamlandığında, tamamen ayrıştırılmış içerikle birlikte ayrı bir olay olarak yayınlanır. Üretimin sonunda finalize() yöntemi, kalan tüm metni boşaltır ve son olayları ile tam mesaj sözlüğünü yayınlar.
parse_response toplu verileri (batch) kabul edebilse de, akış halinde ayrıştırma (streamed parsing) her zaman tek dizilimlidir: her ResponseParser bir üretimin (generation) durumunu izler.
Aynı anda birden fazla üretimi akış halinde almak istiyorsanız, dizilim başına bir ResponseParser oluşturun.
Olayların akışı
Her akışlanan ayrıştırma olayı, type anahtarı içeren bir sözlüktür. Üç tür vardır:
| Tür | Açıklama | İçindekiler |
|---|---|---|
region_open | Modelin content veya reasoning_content gibi yeni bir bölgeye başladığını belirtir. | field (str): alan adı. |
region_chunk | Mevcut bölge için bir metin parçası. | field (str): alan adı. text (str): yeni parça. dirty (bool): parça, ayrıştırılması gereken ham metinse True. |
region_close | Bir bölgenin tamamlandığını ve anahtarın artık kesinleştirildiğini belirtir. | field (str): alan adı. value (any): bölge için tamamen ayrıştırılmış değer |
region_chunk olayları, baytlar geldikçe her bölge için yayımlanır; bu sayede bir akış arayüzü, yapılandırılmış bölgeler için bile ilerlemeyi görüntüleyebilir. Metin benzeri bölgelerde (text, int, float, bool) parçalar dirty=False olarak işaretlenir: her parça zaten nihai değerin bir parçasıdır (kapanışta kaldırılan sonda boşluklar hariç). JSON biçimli araç çağrıları gibi yapılandırılmış bölgelerde ise parçalar dirty=True olarak işaretlenir. Bu, metnin ham, ayrıştırılmamış gövde olduğu anlamına gelir; kademeli olarak görüntülenmesi güvenlidir ancak parsed değeri (bir dict, list vb.) yalnızca eşleşen region_close olayında gelir. Her durumda, bir bölgenin kesinleşmiş değeri her zaman region_close tarafından taşınır; dolayısıyla ara görüntülemeyle ilgilenmeyen tüketiciler region_chunk olaylarını basitçe yok sayabilir.
Sohbet prefix mesajına herhangi bir şey yazdıysa (ör. şablon bir düşünme bloğu açtıysa veya bir asistan ön-doldurma, modele devretmeden önce bir yanıt başlattıysa), ayrıştırıcı bu olayları parser.initial_events olarak açığa çıkarır; bu, model çıktısını işleme vermeden önce görüntüleyicinize yeniden oynatabileceğiniz bir listedir. Önek içinde hem de açılıp hem de kapatılmış bölgeler, tam bir region_open / region_chunk / region_close dizisi üretir ve ayrıştırılmış değerleri çıktı sözlüğüne, sanki modelin kendisi yazmış gibi yerleşir.
Tipik bir olay akışı şu şekilde görünebilir:
{"type": "region_open", "field": "reasoning_content"}
{"type": "region_chunk", "field": "reasoning_content", "text": "I should ", "dirty": False}
{"type": "region_chunk", "field": "reasoning_content", "text": "greet the user", "dirty": False}
{"type": "region_close", "field": "reasoning_content", "value": "I should greet the user"}
{"type": "region_open", "field": "tool_calls"}
{"type": "region_chunk", "field": "tool_calls", "text": '{"name": "greet_user", ', "dirty": True}
{"type": "region_chunk", "field": "tool_calls", "text": '"arguments": {"greeting": "Hi!"}}', "dirty": True}
{"type": "region_close", "field": "tool_calls", "value": {"type": "function", "function": {"name": "greet_user", "arguments": {"greeting": "Hi!"}}}}reasoning_content'in neden dirty=False ile yayınlandığına dikkat edin; çünkü reasoning_content ve content gibi alanlar genellikle ham metindir. Bu, parçacıkları geçerli bir “kısmi çıktı” olarak ele alabileceğiniz anlamına gelir. Ancak tool_calls, ham metnin önemli bir temizlik işlemine ihtiyaç duyması nedeniyle dirty olarak işaretlenir; araç çağrıları genellikle JSON veya başka bir biçimde ayrıştırılıp yeniden yapılandırılarak nihai araç çağrısı sözlüğünün oluşturulması gerekir. Sonuç olarak, bu bölgeler için nihai çıktı, ham metinden genellikle çok, çok farklı görünür. Bu nihai ayrıştırma işlemi yalnızca region_close noktasına ulaşıldığında gerçekleşir. O zamana kadar dirty parçacıklarla ne yapmak istediğiniz size kalmıştır; kullanıcılara “ham” çıktıyı göstermek için olduğu gibi görüntüleyebilir veya görüntülenecek temiz bir şey olana kadar basitçe bekleyebilirsiniz.
Bu, yanıt şablonlarını kullanmak için bilmeniz gerekenlerin çoğunu kapsar. Bu belgenin geri kalanı, ayrıştırma sisteminin iç yapısına ve yanıt şablonları yazma yöntemine odaklanmaktadır. Bu bölüm büyük ölçüde geliştiriciler ve model yazarları için ilgilidir. Çoğu kişi burada güvenle durabilir!
Gelişmiş: Yanıt şablonu yazma
Bir yanıt şablonu yazmanın nasıl yapıldığını anlamak için en iyi yol, somut bir örnek seçmektir. SmolLM'den gelen ham bir yanıtın nasıl görünebileceği aşağıdaki gibidir:
<think>
I should greet the user
</think>
<tool_call>{"name": "greet_user", "arguments": {"greeting": "Hi!"}}</tool_call>Bu çıktıyı standart mesaj sözlüğü biçiminde ayrıştırdığımızda, aşağıdaki gibi görünmelidir:
{
"role": "assistant",
"reasoning_content": "I should greet the user",
"tool_calls": [
{"type": "function", "function": {"name": "greet_user", "arguments": {"greeting": "Hi!"}}}
]
}Ve işte onu ayrıştıran şablon. Korkmayın - çoğu kısmı oldukça açıklayıcı!
{
"defaults": {"role": "assistant"},
"start_anchor": "<|im_start|>assistant\n",
"fields": {
"reasoning_content": {"open": "<think>", "close": "</think>", "content": "text"},
"tool_calls": {
"open": "<tool_call>",
"close": "</tool_call>",
"repeats": True,
"content": "json",
"transform": {"type": "function", "function": "{content}"},
},
"content": {
"close": "<|im_end|>",
"content": "text",
},
},
}Temelde, şablon alanları ve ayraçları tanımlar. Her alan, çıktı sözlüğündeki bir anahtara karşılık gelir. Alanlar, ayraçları içindeki metni ayrıştırmak için bilgi de içerir. Bir nüans var: content alanının open’ı yoktur, çünkü SmolLM’de (ve birkaç diğer modelde) özel bir token ile işaretlenmez. Bunun yerine, content, diğer bölgelerin ardından, ancak dizinin sonundan önceki boşlukta saklanır. Şablonumuzda bunu, başka bir bölge tarafından alınmamış herhangi bir metni toplayan örtük / kalan bir alan olarak temsil ediyoruz.
Alan adları, modelin sohbet şablonunun okuduğu anahtarlarla eşleşmelidir; böylece ayrıştırılmış bir mesaj sohbete eklenebilir ve yeniden işlenebilir. Özellikle, akıl yürütme alanını reasoning_content olarak adlandırın. Bu, mesaj sözlüklerinde akıl yürütme için standart anahtardır ve neredeyse tüm sohbet şablonlarının akıl yürütme bilgisini okuduğu anahtardır.
fields alanlarının yanı sıra, şablon iki üst düzey anahtar destekler:
defaults(isteğe bağlı) — Çıktıda önceden doldurulmuş değerlerin sözlüğü (ör.{"role": "assistant"}). Buradaki anahtarlar, hiçbir alan bunlara yazmasa bile ayrıştırılmış çıktıda her zaman korunur; diğer anahtarlar, alanları hiçbir şey yakalamadığında atılır.start_anchor(str) /start_anchor_pattern(str regex) — Bir sohbet isteminde (chat prompt) mevcut asistan mesajının nerede başladığını işaretler.parse_responseveyaget_response_parserfonksiyonlarınaprefix=parametresini geçirdiğinizde, parser bu öneki (prefix) işlemeye başlamadan önce, bu çaprazın (anchor) son göründüğü noktadan itibaren sağdan kısaltır (right-truncate); böylece çok turlu bir sohbetteki önceki turlar, mevcut mesajın durumunu kirletmez. Çapraz yalnızcaprefixüzerine uygulanır, ayrıştırdığınız yanıt/üretim (response/generation) üzerine asla uygulanmaz — bazı biçimler, mesajın ortasında bunu meşru olarak yeniden yazar (örneğin gpt-oss harmony çıktısı her kanalı<|start|>assistantile açar); bu nedenle yanıtı çaprazdan itibaren kırpmanın, modelin kendi akıl yürütmelerini ve araç çağrılarını (tool calls) düşürmesine neden olur. Üretim (generation) tek başına geçmiş sızıntısına (history bleed) karşı koruma sağlamak için asla yeterli değildir: istemiprefix=olarak geçirin. ChatML tarzı modellerde çapraz genellikle"<|im_start|>assistant\n"şeklindedir.start_anchorveyastart_anchor_patternalanlarından tam olarak biri ayarlanmalıdır.
Örneğin, bu çok turlu önekle (ön ek) (not: iki asistan turu):
<|im_start|>user Hi<|im_end|> <|im_start|>assistant Hello!<|im_end|> <|im_start|>user Again?<|im_end|> <|im_start|>assistant
ayrıştırıcı, son <|im_start|>assistant\n'e kadar olan her şeyi kısaltır ve önceki "Hello!" turunu atar. Kural şudur: son asistan turu dışındaki her şey her zaman atılır.
Sohbet şablonlarında olduğu gibi, yanıt şablonları da tokenizer öznitelikleri olarak saklanır ve tokenizer ile birlikte kaydedilir. Sohbet şablonlarından farklı olarak, bunları ayrı bir dosya olarak değil, tokenizer_config.json içinde saklarız; çünkü bir sohbet şablonu Jinja betiğinin aksine, formatları JSON'a doğal olarak uyar.
tokenizer.response_template = template
tokenizer.save_pretrained(...) # Written as a key in tokenizer_config.jsonGelişmiş: Alan API Referansı
Her alan birkaç anahtarı destekler. Bunları iki tipe ayırabiliriz. İlk olarak, alanın nasıl yakalanacağını tanımlayan anahtarlar vardır:
| Anahtar | Tür | Amaç |
|---|---|---|
open | str veya list[str] | Bu bölgeyi açan literal dize. Dize listesi, “bunlardan herhangi biriyle eşleş” anlamına gelir. |
open_pattern | str (regex) | open için Regex alternatifi. Adlandırılmış gruplar, transform için kullanılabilir yakalama değişkenlerine dönüşür. |
close | str veya list[str] | Bu bölgeyi kapatan literal dize (veya dize listesi). Akışın sonuna kadar çalıştırmak için atlayın. |
close_pattern | str (regex) | close için Regex alternatifi. Adlandırılmış gruplar, transform için kullanılabilir yakalama değişkenlerine dönüşür. |
repeats | bool | Doğruysa, alan bir listedir ve her eşleşme eklenir. Varsayılan false. |
join | str | repeats ile eşleşmeler, bir liste yerine bu ayraçla tek bir dizeye birleştirilir. |
optional | bool | false ise ve bölge asla eşleşmezse, bir hata oluşturulur. Varsayılan true. |
Bir alanın ya open ya da open_pattern olmalı, ikisi birden olamaz; aynı durum close ve close_pattern için de geçerlidir.
Bir alan close/close_pattern değerlerini tamamen atlayabilir; bu durumda bölge, üretilen metnin sonuna kadar açık kalır. Bu, mesajın sonuna kadar uzanan son bir alan için kullanışlıdır.
Ne open ne de open ya da open_pattern alanı içeren bir alan, örtük alandır: açık bir bölge olmadıkça her zaman etkin olur, bu nedenle kalan metni yakalar. En fazla bir alan örtük olabilir. Bu, content özel belirteç etiketlerine sahip olmadığında, diğer alanlardan sonra düz metin olarak yazıldığında en sık kullanılan durumdur.
Açılış ve kapanış sınırlayıcılarının yanı sıra, alanın bir liste olduğunu ve sınırlayıcıların birden fazla kez eşleşebileceğini gösteren repeats değerini de belirleyebilirsiniz. Bu, bir modelin aynı anda birden çok araç çağrısı ürettiği paralel araç çağrılarında en yaygın kullanılan durumdur:
'<tool_call>{"name": "a", ...}</tool_call><tool_call>{"name": "b", ...}</tool_call>'
# Returns `"tool_calls": [{... "a" ...}, {... "b" ...}]` in a template with repeats: trueAncak her tekrarlanan alan bir listeye dönüştürülmemelidir. Bazı modeller tek bir mesajda birden fazla reasoning_content veya content bloğu üretir ve bunlar için doğal çıktı tek bir birleştirilmiş dizgedir. join (genellikle yalnızca "" olan bir ayraç dizgesi) ayarlandığında, repeats alanı bir liste toplamak yerine eşleşenlerini birleştirir:
field = {"reasoning_content": {"open": "<think>", "close": "</think>", "repeats": True, "join": "\n"}}
input = "<think>first</think>...<think>second</think>"
# Returns: {"reasoning_content": "first\nsecond"}Bir join alanının her eşleşmesi bir dizeye ayrıştırılmalıdır. Akış sırasında region_close yine her bloğun kendi değerini taşır; ayraç yalnızca son mesaj sözlüğünde görünür.
Son olarak, varlığı zorunlu olan alanlar için optional: false belirtmek mümkündür. Böyle bir alan eksikse, yalnızca bu alanı içermeyen bir mesaj sözlüğü döndürmek yerine bir hata oluşturulur.
Üretim sonu, kapanış ayıracı görülmemiş olsa bile açık olan tüm bölgeleri kapatır ve sonlandırır.
Bir alanın içeriğini ayrıştırma
Bir alanın nasıl yakalanacağını tanımladıktan sonra, o yakalamadaki ham metnin nasıl ayrıştırılacağını da belirtmemiz gerekir. Bunu kontrol eden dört anahtar vardır:
| Anahtar | Tür | Amaç |
|---|---|---|
content | str | Bu bölgenin içindeki içerik türü. Varsayılan olarak "text"'tir. Her türün kendi ayrıştırıcısı vardır. |
content_args | dict | Bu bölge için içerik ayrıştırıcısına iletilecek argümanlar. |
transform | dict/list | Ayrıştırılmış gövdeyi yeniden şekillendiren isteğe bağlı ayrıştırma sonrası şablon (bkz. Transform). |
transform_each | bool | Doğruysa, ayrıştırılan içerik bir liste olmalı ve transform öğe bazında uygulanır. |
İlk (ve en önemli) anahtar content'tir. Bu, alanın içerik türünü belirtir ve alanda yakalanan ham metni nihai çıktıya dönüştürmek için kullanılacak ayrıştırıcıyı (parser) belirler. content_args, ayrıştırıcıyı yapılandırmak için kullanılır ve özel kod gerektirmeden çeşitli biçim tuhaflıklarını desteklememizi sağlar.
Sırasıyla her tür ayrıştırıcıyı ve argümanlarını inceleyeceğiz.
Temel türler
text, int, float ve bool temel tiplerdir. Bu içerik tiplerinin tümü yalnızca boşlukları temizler ve gerekirse basit bir tip dönüşümü yapar. text hariç olmak üzere hiçbirinin content_args değeri yoktur; text ise yakalanan metnin başından ve sonundan boşlukları temizleyen, varsayılan değeri true olan strip argümanını destekler.
field = {"count": {"open": "<n>", "close": "</n>", "content": "int"}}
input = "<n> 42 </n>"
# Returns: {"count": 42}json
json ayrıştırıcısı, yakalanan metni JSON olarak ayrıştırır. Araç çağrısı argümanları ve iç içe geçmiş yapıya sahip diğer her şey için işin atıdır. Gerçek dünyada modellerin JSON’u bozma biçimlerini ele almak için birkaç isteğe bağlı content_args kabul eder:
unquoted_keys(bool, varsayılanfalse): Anahtar adları tırnak içinde değilse etkinleştirin (ör.{name: "foo"}). Katı JSON yerine Javascript tarzı nesne sözdizimleri üreten modeller için faydalıdır.string_delims([open, close]çiftlerinden oluşan liste, isteğe bağlı):"..."yerine özel ayraçlarla sarılan string değerleri için modellerde kullanılır. Her çift, bir açılış ve bir kapanış işaretçisi verir.allow_non_json(bool, varsayılanfalse): ayrıştırma başarısız olursa, hata fırlatmak yerine budanmış ham metni döndürür. Modelin genellikle JSON üretip ara sıra düz metne düştüğü alanlar için bir yedek çözüm olarak faydalıdır.
unquoted_keys ve string_delims, standart dışı, neredeyse JSON ama tam olarak JSON olmayan çıktı üreten modelleri işlemek için mevcuttur; bu nedenle bunlara yalnızca birkaç model için ihtiyaç duymanız gerekir.
field = {"args": {"open": "<args>", "close": "</args>", "content": "json", "content_args": {"unquoted_keys": True}}}
input = '<args>{city: "London"}</args>'
# Returns: {"args": {"city": "London"}}xml-inline
xml-inline ayrıştırıcısı, düz bir XML benzeri etiket dizisinden oluşan bölgeler içindir; her etiket bir sözlükte bir girdiye dönüşür. En sık, argümanlarını JSON bloğu olarak değil de her birini kendi etiketiyle çıkaran modellerde tool_calls alanı içinde kullanılır:
tag_pattern(str, zorunlu): tek bir etiketle eşleşen regex. Sonuçtaki sözlük anahtarı olankeyve sözlük değeri haline gelen ham metin olanvalueadlandırılmış gruplarını içermelidir.value_parser(dict, isteğe bağlı): yakalanan hervalue'e uygulanan iç içe içerik ayrıştırıcısı.name(ayrıştırıcı, örn."json","int") ve isteğe bağlıargs(onuncontent_args'ı) içeren bir sözlük. Atlanırsa, değerler ham dizeler olarak kalır.merge_duplicates(bool, varsayılanfalse): Aynı anahtar birden fazla kez görünürse, sonraki eşleşmelerin öncekileri üzerine yazmasına izin vermek yerine değerleri bir listeye toplayın.
Örneğin, Qwen3 her araç çağrısı argümanını kendi <parameter> etiketi olarak yazar ve biz bunu şu şekilde ayrıştırırız:
"tool_calls": {
"open_pattern": r"<tool_call>\s*<function=(?P<name>\w+)>",
"close": "</tool_call>",
"repeats": True,
"content": "xml-inline",
"content_args": {
"tag_pattern": r"<parameter=(?P<key>\w+)>\s*(?P<value>.*?)\s*</parameter>",
"value_parser": {"name": "json", "args": {"allow_non_json": True}},
},
"transform": {"type": "function", "function": {"name": "{name}", "arguments": "{content}"}},
}İç içe geçmiş value_parser'a dikkat edin: her parametre değeri, json ayrıştırıcısından (düz dizgilerin de geçebilmesi için allow_non_json ile) geçirilir. Yukarıdaki tool_calls alanını bu girdiye beslemek:
input = "<tool_call><function=get_weather><parameter=city>London</parameter><parameter=units>celsius</parameter></function></tool_call>"
# Returns: {"tool_calls": [{"type": "function", "function": {"name": "get_weather", "arguments": {"city": "London", "units": "celsius"}}}]}kv-lines
The kv-lines parser handles line-delimited key: value pairs (think YAML-ish metadata or .env files). Each line becomes one entry in the resulting dict. All arguments are optional:
line_sep(str, varsayılan"\n"): çiftler arasındaki ayraç.kv_sep(str, varsayılan":"): tek bir satır içinde bir anahtar ile değeri arasındaki ayraç. Bölme noktası olarak yalnızca ilk geçiş kullanılır, bu nedenle değerlerin kendisi ayraç içerebilir.strip(bool, varsayılantrue): Her anahtar ve değerin çevresindeki boşlukları kaldırır.value_parser(dict, isteğe bağlı):xml-inlineiçin olduğu gibi{"name": ..., "args": ...}biçiminde, her değere uygulanan iç içe içerik ayrıştırıcısı. Atlanırsa, değerler ham dizeler olarak kalır.
Boş olan veya kv_sep içermeyen satırlar sessizce atlanır, bu nedenle yakalanan bölgedeki gereksiz boş satırlar tolere edilir.
field = {"metadata": {"open": "<meta>", "close": "</meta>", "content": "kv-lines"}}
input = "<meta>name: alice\nage: 30</meta>"
# Returns: {"metadata": {"name": "alice", "age": "30"}}Not age, "30" değerini bir dize olarak korur; 30 olarak ayrıştırması için value_parser olarak {"name": "int"} ekleyin.
Araç çağrısı argümanlarını biçimlendirme
Bazen, yanıt ayrıştırıcısı model çıktılarını yanlış türle ayrıştırabilir. Örneğin,
1.5 gibi bir ondalık sayıyı “1.50” olarak ayrıştırabilir. Araç çağrılarında, araçlar bir argümanı belirli bir türde beklerken farklı bir türde alıyorsa bu sorunlara yol açabilir. Bunu önlemek için,
isteklerin tools değerini parse_response() veya ResponseParser içine geçirerek her aracın JSON Şeması parameters alanını kullanarak bunları dönüştürebilirsiniz.
Araçlar, apply_chat_template() ile aynı formatta kabul edilir: JSON
şemaları veya şemalara otomatik olarak dönüştürülen, tip ipuçları ve doküman dizeleri içeren Python işlevleri.
tools = [{
"type": "function",
"function": {
"name": "set_alarm",
"parameters": {
"type": "object",
"properties": {
"hour": {"type": "integer"},
"enabled": {"type": "boolean"},
"label": {"type": "string"},
},
},
},
}]
message = parse_response(model_out, template, prefix="", tools=tools)
# message["tool_calls"][0]["function"]["arguments"] ==
# {"hour": 7, "enabled": True, "label": "wake up"}Dönüştür
Çoğu alan için transform anahtarı gereksizdir. Ayrıştırılan gövdenin nihai çıktıya dönüştürülmesi gerektiğinde veya ayraçlardan gelen bilgilerin sonuca birleştirilmesi gerektiğinde kullanılır. Genellikle karmaşık yapıya sahip oldukları için tool_calls alanlarında görülür.
transform bir şablondur: çıktı şeklini tanımlayan bir sözlük (veya liste), burada "{name}" biçimindeki herhangi bir dize, karşılık gelen değerle değiştirilir. Değerlere, bu bölgenin ayrıştırılmış gövdesi olan content ve open_pattern / close_pattern tarafından yakalanan adlandırılmış gruplardan erişilebilir. Çok yaygın bir kullanım senaryosu, bunların standart araç çağrısı biçimimizin bir parçası olması nedeniyle, bir araç çağrısı sözlüğünü function anahtarı içeren dış bir sözlükte sarmalamaktır:
"tool_calls": {
"open": "<tool_call>",
"close": "</tool_call>",
"repeats": True,
"content": "json",
"transform": {"type": "function", "function": "{content}"},
},Yani bu ham çıktı:
<tool_call>{"name": "greet_user", "arguments": {"greeting": "Hi!"}}</tool_call>şeklinde olur (repeats: True, tool_calls'in bir liste olmasını sağlar):
[{"type": "function", "function": {"name": "greet_user", "arguments": {"greeting": "Hi!"}}}]"{content}" gibi tüm dizeyi kapsayan bir yer tutucu, aranan değeri türü korunarak döndürür — yukarıdaki örnekte, ayrıştırılan JSON sözlüğü function değerinin yerine doğrudan yerleştirilir. Bir yer tutucunun dizenin tamamı olması gerekir: metin ve yer tutucuların karıştırılması ("abc {name} def") izin verilmez. Bunlar f-dizeleri değildir!
Placeholder'lar, "{content.args}" gibi noktalı bir yol da alabilir; bu, content anahtarını arar ve ardından ayrıştırılan sözlüğün args anahtarına iner. Bu, anahtar adları standart formatımızla eşleşmeyen araç çağrısı gövdelerini yeniden şekillendirmenin yoludur. Örneğin, {"name": ..., "args": ...} üreten bir modelde args anahtarının arguments olarak yeniden adlandırılması gerekir:
"tool_calls": {
"open": "<tool>",
"close": "</tool>",
"repeats": True,
"content": "json",
"transform": {"type": "function", "function": {"name": "{content.name}", "arguments": "{content.args}"}},
},Çözümleme yapılamayan bir yol (burada, gövdesinde args bulunmayan bir araç çağrısı) hatalı bir araç çağrısı yayınlamak yerine bir hata oluşturur.
transform oldukça çok yönlüdür ve model çıktısının standart API’mizden tamamen farklı bir formata sahip olması durumunda bu özellik gereklilik halini alır. GPT-OSS bunun iyi bir örneğidir; fonksiyon adını JSON gövdesinde değil, kanal başlığında barındırır. Bu nedenle onu open_pattern içinde adlandırılmış bir grupla yakalayıp transform içinde content ile birleştirmemiz gerekir. open_pattern ve close_pattern içindeki tüm adlandırılmış gruplar, content ile birlikte değişken olarak kullanılabilir hale gelir:
"tool_calls": {
"open_pattern": r"<\|channel\|>commentary to=functions\.(?P<name>\w+).*?<\|message\|>",
"close": "<|call|>",
"repeats": True,
"content": "json",
"transform": {"type": "function", "function": {"name": "{name}", "arguments": "{content}"}},
},Fonksiyon adı JSON gövdesinde değil, kanal başlığında yer alır, bu yüzden:
<|channel|>commentary to=functions.get_current_weather <|constrain|>json<|message|>{"location": "San Francisco, CA"}<|call|>şu hale gelir:
[{"type": "function", "function": {"name": "get_current_weather", "arguments": {"location": "San Francisco, CA"}}}]Bazen bir alanın ayrıştırılmış içeriği kendisi bir kayıt listesidir ve her birini yeniden şekillendirmek istersiniz. Cohere şablonu bunun iyi bir örneğidir: Tüm araç çağrılarını tek bir JSON dizisi içinde üretir, bu nedenle dönüştürmeyi öğe başına uygulamak için transform_each: True ayarlanır. Her dizi öğesinin anahtarları şablon kapsamına açılır, bu yüzden "{tool_name}" geçerli öğede tool_name değerini arar:
"tool_calls": {
"open": "<|START_ACTION|>",
"close": "<|END_ACTION|>",
"content": "json",
"transform_each": True,
"transform": {"type": "function", "function": {"name": "{tool_name}", "arguments": "{parameters}"}},
},Bu, aşağıdaki gibi bir çıktıyı dönüştürecektir:
[
{"tool_name": "greet_user", "parameters": {"greeting": "Hi!"}},
{"tool_name": "search", "parameters": {"query": "weather tomorrow"}}
]Standart API'mize uygun, aşağıdaki gibi bir çıktıya dönüştürülür:
[
{"type": "function", "function": {"name": "greet_user", "arguments": {"greeting": "Hi!"}}},
{"type": "function", "function": {"name": "search", "arguments": {"query": "weather tomorrow"}}}
]transform_each bayrağı yalnızca content zaten bir liste olduğunda gereklidir; her eşleşmenin bir öğe katkısı sağladığı ve repeats: True ile bunların biriktiği daha yaygın durumda, dönüştürme varsayılan olarak her öğeye uygulanır.
Framework geliştiricileri: Regex taşınabilirliği
open_pattern, close_pattern ve start_anchor_pattern regex dizeleridir. Çoğu kullanıcı için ve hatta çoğu model yazarı için bu bir sorun olmamalıdır, ancak başka bir dilde yanıt ayrıştırma uygulaması yazan bir geliştiriciyseniz, uygulama detaylarımızın farkında olmalısınız. Bu bölüm, Python dışı sohbet şablonlamayı çalıştırmak için tüm bir Jinja ayrıştırıcı uygulamak zorunda kalan herkese adanmıştır - aşağıdaki basit yönergeleri takip ederseniz, yanıt şablonlarının çok daha az zahmetli olacağını umuyoruz:
- Tüm sohbet ayrıştırma işlemlerinde kullanılan düzenli ifadeler için Python’un
regexmodülünü kullanıyoruz. Tüm Python3 dizgeleri unicode olduğu için düzenli ifade eşleşmelerimizin tümü unicode farkındalıklıdır. Bu durum özellikle\wgibi yaygın karakterleri etkiler. Motorunuzda ilgili unicode bayraklarını ayarladığınızdan emin olun. - Tüm regex'ler
re.DOTALLetkin vere.MULTILINEdevre dışı olacak şekilde derlenir; bu nedenle.\nile eşleşir ancak^ve$yalnızca tüm girdinin başını/sonunu eşleştirir, satır sonlarını değil. - Adlandırılmış gruplar için
(?P<name>...)sözdizimini kullanırız. Diğer regex uygulamalarının adlandırılmış yakalama grubu sözdizimleri çok farklıdır, bu nedenle bu deseni regexlerde aramanız ve yerel uygulamanıza uygun şekilde yeniden yazmanız gerekebilir. - Veri yayımlayabileceğimiz zamanı belirlemek için kısmi regex eşleşmesi kullanırız. Bu, bir bölgenin sonu birden fazla token'dan oluşabileceğinden ve bölge sonu ayıracını yayımlamak istemediğimizden gereklidir; bu nedenle, tokenların bölgenin bir parçası olduğunu ve sınırın bir parçası olmadığını kesinleştirene kadar tokenları geri tutarız. Bu, bir bölge sonu regex'inin kısmi bir eşleşmeye sahip olduğu her durumda, regex eşleşene ya da başarısız olana kadar verileri geri tuttuğumuz anlamına gelir. Regex motorunuz kısmi eşleşmeyi desteklemiyorsa, yine de yanıt şablonları uygulayabilirsiniz, ancak bu soruna başka bir çözüm bulmanız gerekebilir. Basit bir yaklaşım, bu bölgeleri kesin olarak son ayıracı görene kadar geri tutmak veya
dirtyolarak yayımlamaktır. - Düzenli ifade motorunuz (nadir durumlarda)
(?!...)gibi bakış pencerelerini (lookarounds) desteklemeyebilir. Bunlar yanıt şablonlarında yaygın olarak kullanılmamakla birlikte, şablonlarda yer alabilir ve biz bunları destekliyoruz! Bu durumlarda bir hata fırlatmanız ya da düzenli ifade motoru olası bir eşleşme bulduğunda bakış pencerelerini manuel olarak çıkarıp kodunuzda bunları uygulamanız gerekebilir. - Backreferences, atomic groups, possessive quantifiers, recursion gibi diğer gelişmiş özellikler genellikle yanıt şablonlarında kullanılmaz. Model yazarlarını bunları kullanmaktan vazgeçirmeye çalışacağız, bu sayede muhtemelen güvenle yok sayabilirsiniz.