LLM Halüsinasyonu ve Güvenilirlik: Kaynak Göster, Doğrula, Ölç
gemma3:4b'ye var olmayan bir romanın yazarını sorunca kendinden emin bir ad ve yıl uydurdu. Halüsinasyonun neden olduğuna bakıyor, cevabı structured output ile kaynak göstermeye zorluyor, kaynakları kodla doğruluyor ve on soruluk bir golden set ile üç yaklaşımı sayılarla karşılaştırıyoruz.
Sürüm notu: Bu yazıdaki örnekler Ollama 0.34 ile yerel modellerde (gemma3:4b, nomic-embed-text) denenmiştir; Node 22 kullanılmıştır.
Önce Tahmin Et
Modele var olmayan bir kitabın yazarını sorarsan ne olur? "Böyle bir kitap bilmiyorum" mu der, yoksa bir yazar adı ve yayın yılı mı uydurur? Uydurursa, bunu nasıl anlarsın?
Önceki yazıda RAG ile modelin cevabını kendi dokümanlarımıza bağladık. Bu yazıda bir adım geri çekilip halüsinasyonun neden olduğuna bakacağız, sonra cevaba kaynak göstermeyi, o kaynağı kodla kontrol etmeyi ve en önemlisi yaptığımız değişikliklerin işe yarayıp yaramadığını sayılarla ölçmeyi ekleyeceğiz.
Var Olmayan Bir Kitap
İki soru: biri gerçek bir roman, diğeri benim uydurduğum bir başlık.
import ollama from 'ollama';
// Bağlam yok, araç yok: yalnızca modelin eğitimde öğrendikleri
for (const question of process.argv.slice(2)) {
const response = await ollama.chat({
model: 'gemma3:4b',
messages: [{ role: 'user', content: question }],
options: { temperature: 0, seed: 42 },
});
console.log(`> ${question}\n${response.message.content}\n`);
}
$ npx tsx hallucination.ts "Who wrote the novel 'Dune' and when was it published?" "Who wrote the novel 'Seven Winters in Quillhaven' and when was it published?"
> Who wrote the novel 'Dune' and when was it published?
The novel "Dune" was written by **Frank Herbert**.
It was originally published in **1965** by Chilton Books.
Do you want to know anything more about the book or its author?
> Who wrote the novel 'Seven Winters in Quillhaven' and when was it published?
The novel "Seven Winters in Quillhaven" was written by **Melanie Bolt** and was published in **2019**.
It's a historical romance set in 18th-century Scotland.
İlk cevap doğru. İkinci cevap da aynı tonda, aynı biçimde ve aynı kendinden eminlikle geliyor; ama "Seven Winters in Quillhaven" diye bir roman yok. Yazar adı, yıl ve tür modelin uydurması. Başka uydurma başlıklarla yaptığım denemelerde model gerçek yazarların adını kullandı, uydurma kitaplar için Goodreads linkleri bile üretti.
Halüsinasyonun en tehlikeli yanı bu: doğru ve yanlış cevap aynı görünür. Cevabın biçimine bakarak hangisinin uydurma olduğunu anlayamazsın.
Halüsinasyon Neden Olur?
Serinin ilk yazısına dön: büyük dil modeli bir sonraki token'ı tahmin eder. Eğitim ona "akla yatkın" metin üretmeyi öğretir, "doğru" metin üretmeyi değil. Çoğu zaman bu ikisi örtüşür, çünkü eğitim verisinde doğru bilgi sık tekrar eder. "Dune" sorusunun cevabı sayısız sayfada geçtiği için model onu güvenle tamamlıyor. "Quillhaven" için ise elinde hiçbir şey yok; ama "Who wrote the novel X" kalıbının devamı her zaman bir ad ve bir yıl olduğu için kalıbı tamamlıyor.
Birkaç etken bunu artırır. Model eğitim verisinin bittiği tarihten sonrasını bilmez; tool calling yazısında qwen3:4b'nin 2026 tarihini "uzak gelecek" sandığını görmüştük. Şirketine özel bilgiyi hiç görmemiştir; RAG yazısında iade süresini 30 gün diye uydurmuştu. Küçük modeller büyüklere göre daha az bilgi taşır. Ve model, kendi bilgisinin sınırını kalibre edilmiş bir şekilde bilmez; "emin değilim" demek eğitimde her zaman ödüllendirilen bir davranış olmamıştır.
Bir yanılgıyı da düzeltelim: temperature: 0 halüsinasyonu engellemez. Bu yazıdaki bütün örnekler temperature 0 ile çalıştı. Temperature yalnızca en olası token'ı seçer; en olası devam yanlışsa, her seferinde aynı yanlışı tekrarlarsın.
Bağlama Dayandırmak ve Kaynak Göstermek
Halüsinasyonu tamamen yok edemezsin, ama alanını daraltabilirsin. Birinci yol önceki yazıda gördüğümüz RAG: modelin cevabı kendi bilgisinden değil, önüne koyduğun metinden üretmesi. İkinci yol modelden kaynak göstermesini istemek. Bunun için RAG kodunu TypeScript'e taşıyıp küçük bir module yaptım:
import ollama from 'ollama';
import { readFileSync } from 'node:fs';
export interface Chunk {
id: number;
text: string;
vector: number[];
}
const dot = (a: number[], b: number[]) => a.reduce((sum, x, i) => sum + x * b[i], 0);
// C# sürümüyle aynı parçalama: her paragraf bir chunk, başında bölüm başlığı
export async function buildIndex(path: string): Promise<Chunk[]> {
const texts: string[] = [];
let section = '';
for (const line of readFileSync(path, 'utf8').split('\n')) {
if (line.startsWith('# ')) {
section = line.slice(2);
} else if (line.trim()) {
texts.push(`${section}: ${line}`);
}
}
const { embeddings } = await ollama.embed({
model: 'nomic-embed-text',
input: texts.map((t) => `search_document: ${t}`),
});
return texts.map((text, i) => ({ id: i + 1, text, vector: embeddings[i] }));
}
export async function retrieve(index: Chunk[], question: string, topK = 2): Promise<Chunk[]> {
const { embeddings } = await ollama.embed({ model: 'nomic-embed-text', input: [`search_query: ${question}`] });
return [...index].sort((a, b) => dot(embeddings[0], b.vector) - dot(embeddings[0], a.vector)).slice(0, topK);
}
Şimdi cevabı structured output ile istiyoruz. Her chunk'ın başına bir numara koyuyor, modelden cevabı, kullandığı kaynak numaralarını ve cevabı bulup bulmadığını JSON olarak döndürmesini istiyoruz. Böylece "bilmiyorum" artık serbest bir cümle değil, found: false diye okunabilir bir alan:
import ollama from 'ollama';
import { z } from 'zod';
import { buildIndex, retrieve, type Chunk } from './rag.js';
// Cevap, bilginin bulunup bulunmadığını ve hangi kaynakları kullandığını söylemek zorunda
const Answer = z.object({
found: z.boolean(),
answer: z.string(),
source_ids: z.array(z.number().int()),
});
type Answer = z.infer<typeof Answer>;
export async function askGrounded(index: Chunk[], question: string): Promise<{ result: Answer; chunks: Chunk[] }> {
const chunks = await retrieve(index, question);
const context = chunks.map((c) => `[${c.id}] ${c.text}`).join('\n');
const response = await ollama.chat({
model: 'gemma3:4b',
format: z.toJSONSchema(Answer),
options: { temperature: 0, seed: 42 },
messages: [
{
role: 'system',
content: [
'You are the Bookstore support assistant.',
'Answer using ONLY the numbered sources inside <context>.',
'Put the numbers of the sources you used in source_ids.',
'If the sources do not contain the answer, set found to false, answer "I don\'t know" and leave source_ids empty.',
'Reply as JSON: {"found": boolean, "answer": string, "source_ids": number[]}',
].join('\n'),
},
{ role: 'user', content: `<context>\n${context}\n</context>\n\nQuestion: ${question}` },
],
});
return { result: Answer.parse(JSON.parse(response.message.content)), chunks };
}
// Kodla ucuz kontroller: gösterilen kaynaklar var mı, cevaptaki sayılar o kaynaklarda geçiyor mu?
export function verify(result: Answer, chunks: Chunk[]): string[] {
const problems: string[] = [];
const cited = chunks.filter((c) => result.source_ids.includes(c.id));
for (const id of result.source_ids) {
if (!chunks.some((c) => c.id === id)) {
problems.push(`source ${id} was not in the context`);
}
}
if (result.found && cited.length === 0) {
problems.push('answer has no valid source');
}
const citedText = cited.map((c) => c.text).join(' ');
// Önce [3] gibi kaynak işaretlerini sil, bunlar bilgi değil
const answerText = result.answer.replace(/\[\d+\]/g, '');
for (const num of answerText.match(/\d+(\.\d+)?/g) ?? []) {
if (!citedText.includes(num)) {
problems.push(`number ${num} is not in the cited sources`);
}
}
return problems;
}
if (import.meta.url === `file://${process.argv[1]}`) {
const index = await buildIndex('policies.md');
for (const question of process.argv.slice(2)) {
const { result, chunks } = await askGrounded(index, question);
console.log(`> ${question}`);
console.log(` context: ${chunks.map((c) => c.id).join(', ')}`);
console.log(` ${JSON.stringify(result)}`);
const problems = verify(result, chunks);
console.log(problems.length ? ` CHECK FAILED: ${problems.join('; ')}` : ' check ok');
}
}
verify fonksiyonu modelden bağımsız, ucuz iki kontrol yapıyor. Gösterilen kaynak numaraları gerçekten modele verdiğimiz chunk'lar arasında mı? Cevaptaki sayılar, gösterilen kaynakların metninde geçiyor mu? İade süresi, fiyat, tarih gibi sayılar müşteri açısından en pahalı halüsinasyonlardır; onları kontrol etmek kolaydır.
Cevabı Kodla Doğrulamak
$ npx tsx grounded.ts "How many days do I have to return a book?" "How many points do I need for a 10 USD discount?" "Can I return a book after 30 days?" "Do you ship to Germany?"
> How many days do I have to return a book?
context: 3, 4
{"found":true,"answer":"You can return a book within 21 days of delivery [3].","source_ids":[3]}
check ok
> How many points do I need for a 10 USD discount?
context: 6, 1
{"found":true,"answer":"You need 100 points for a 5 USD discount.","source_ids":[6]}
check ok
> Can I return a book after 30 days?
context: 3, 4
{"found":false,"answer":"I don't know","source_ids":[]}
check ok
> Do you ship to Germany?
context: 1, 2
{"found":false,"answer":"I don't know","source_ids":[]}
check ok
İlk cevap kaynağını hem source_ids'de hem de metin içinde [3] diye gösteriyor ve 21 sayısı 3 numaralı chunk'ta geçiyor. Bu satır ilk çalıştırmada CHECK FAILED: number 3 is not in the cited sources verdi: kontrolüm modelin koyduğu [3] işaretini bir sayı sanmıştı. Koddaki replace satırını o yüzden ekledim. Doğrulama kodunun da hata yapabileceğini, onun da test edilmesi gerektiğini gösteren küçük bir an.
İkinci cevap daha öğretici. Bütün kontroller geçti: kaynak var, 100 ve 5 sayıları kaynakta geçiyor. Ama soru 10 USD indirim içindi; doğru cevap 200 puan. Model hesap yapmak yerine kaynaktaki cümleyi aktardı ve soruyu cevaplamadı. Kaynak doğrulaması "bu cevap kaynakta yazanla çelişiyor mu?" sorusunu yakalar, "bu cevap soruyu cevaplıyor mu?" sorusunu yakalamaz.
Üçüncü cevap da sorunlu, ama ters yönde. Politika iadenin 21 gün içinde yapılabileceğini söylüyor; "30 gün sonra iade edebilir miyim?" sorusunun cevabı açıkça "hayır". Model found: false dedi. Bu bir halüsinasyon değil, aşırı temkin; ama kullanıcı için o da yanlış cevap. Almanya sorusu ise tam istediğimiz gibi.
Sayı kontrolünün bir sınırı daha var: kullanıcının sorusundaki sayıyı cevapta tekrar eden doğru bir cevap ("No, you cannot return a book after 30 days...") da bu kontrole takılır, çünkü 30 kaynakta geçmiyor. Kontrolü bir alarm olarak kullan, bir hakem olarak değil.
Golden Set: Küçük Ama Gerçek Bir Ölçü
Tek tek örneklere bakarak bir prompt'un "daha iyi" olduğuna karar veremezsin. Bir değişiklik bir soruyu düzeltirken başka ikisini bozabilir. Çözüm, doğru cevabını bildiğin sabit bir soru seti (golden set) hazırlayıp her değişiklikte bütün seti çalıştırmak. Birim testlerinin LLM dünyasındaki karşılığı gibi düşün: kesin bir doğruluk kanıtı değil, ama bir şeyi bozduğunda sana haber veren bir ağ.
On soruluk bir set hazırladım: yedisinin cevabı politikada var (bazıları küçük bir çıkarım istiyor), üçü bilerek dokümanda olmayan şeyler soruyor. Aynı seti üç yaklaşımla çalıştırıyoruz: hiç bağlam vermeden, düz RAG ile ve önceki bölümdeki kurallı, kaynak gösteren RAG ile.
import ollama from 'ollama';
import { buildIndex, retrieve, type Chunk } from './rag.js';
import { askGrounded } from './grounded.js';
interface GoldenCase {
question: string;
expect: RegExp | 'unknown';
}
// Golden set: doğru cevabını bildiğimiz sorular
const golden: GoldenCase[] = [
{ question: 'How many days do I have to return a book?', expect: /21/ },
{ question: 'How much does express shipping cost?', expect: /9\.90/ },
{ question: 'Is shipping free for a 50 USD order?', expect: /free|yes/i },
{ question: 'How long are gift cards valid?', expect: /3 years|three years/i },
{ question: 'Can I exchange a gift card for cash?', expect: /\b(no|cannot|can't|not)\b/i },
{ question: 'Can I return a book after 30 days?', expect: /\b(no|cannot|21)\b/i },
{ question: 'How many points do I need for a 10 USD discount?', expect: /200/ },
{ question: 'Do you ship to Germany?', expect: 'unknown' },
{ question: 'Do you sell audiobooks?', expect: 'unknown' },
{ question: 'What are the opening hours of the Ankara store?', expect: 'unknown' },
];
// Serbest metindeki "bilmiyorum"ların biçimi çok; bu liste ilk çalıştırmadan sonra uzadı
const saysUnknown = (text: string) =>
/don't know|do not know|not sure|no information|do not have information|doesn['’]t (cover|include)/i.test(text);
type Variant = (index: Chunk[], question: string) => Promise<{ text: string; unknown: boolean }>;
async function chat(system: string, user: string): Promise<string> {
const response = await ollama.chat({
model: 'gemma3:4b',
options: { temperature: 0, seed: 42 },
messages: [
{ role: 'system', content: system },
{ role: 'user', content: user },
],
});
return response.message.content.trim();
}
const variants: Record<string, Variant> = {
'no-rag': async (_index, question) => {
const text = await chat('You are the Bookstore support assistant.', question);
return { text, unknown: saysUnknown(text) };
},
'rag': async (index, question) => {
const context = (await retrieve(index, question)).map((c) => c.text).join('\n');
const text = await chat(
'You are the Bookstore support assistant. Use the context to answer.',
`<context>\n${context}\n</context>\n\nQuestion: ${question}`,
);
return { text, unknown: saysUnknown(text) };
},
'rag+rules+sources': async (index, question) => {
const { result } = await askGrounded(index, question);
return { text: result.answer, unknown: !result.found };
},
};
const index = await buildIndex('policies.md');
for (const [name, run] of Object.entries(variants)) {
let passed = 0;
console.log(`\n== ${name}`);
for (const c of golden) {
const { text, unknown } = await run(index, c.question);
const ok = c.expect === 'unknown' ? unknown : !unknown && c.expect.test(text);
passed += ok ? 1 : 0;
console.log(`${ok ? 'PASS' : 'FAIL'} ${c.question} -> ${text.replace(/\s+/g, ' ').slice(0, 90)}`);
}
console.log(`score: ${passed}/${golden.length}`);
}
Puanlama kasıtlı olarak basit. Cevabı bilinen sorularda cevap bir regex'e uymalı ve model "bilmiyorum" dememiş olmalı. Cevabı dokümanda olmayan sorularda model "bilmiyorum" demeli. Kaynak gösteren varyantta "bilmiyorum"u found alanından, diğerlerinde metinden okuyoruz.
Sonuçlar: Üç Yaklaşım, On Soru
İlk çalıştırmada düz RAG 6/10 aldı ve üç başarısızlık şöyleydi:
FAIL Do you ship to Germany? -> I do not have information about shipping to Germany. Our standard shipping information cov
FAIL Do you sell audiobooks? -> I apologize, but this information doesn’t cover our audiobook offerings.
FAIL What are the opening hours of the Ankara store? -> I apologize, but the provided context doesn't include the opening hours of the Ankara stor
score: 6/10
Model üçünde de doğru davranıp bilmediğini söylemişti; hatalı olan benim puanlama fonksiyonumdu. saysUnknown yalnızca "don't know" gibi birkaç kalıp arıyordu. Serbest metni regex'le puanlamanın kırılganlığı tam olarak bu ve structured output'taki found alanının değeri de burada ortaya çıkıyor. Regex'i genişletip (yukarıdaki kod bu hâli) yeniden çalıştırınca:
$ npx tsx eval.ts
== no-rag
FAIL How many days do I have to return a book? -> Okay, let's get that sorted out for you! You have **30 days** to return a book to us. 😊 D
FAIL How much does express shipping cost? -> Okay, let's look at our express shipping costs! It depends on a few things, like the weigh
PASS Is shipping free for a 50 USD order? -> Okay, let's check that for you! Generally, shipping is free on orders of $50 or more. To c
FAIL How long are gift cards valid? -> Okay, let's get you the information on gift card validity! Generally, our gift cards are v
PASS Can I exchange a gift card for cash? -> Okay, let's see about exchanging a gift card! Unfortunately, we generally **cannot** excha
FAIL Can I return a book after 30 days? -> Okay, let’s see about that! Our standard return policy is 30 days from the date of purchas
FAIL How many points do I need for a 10 USD discount? -> Okay, let's figure that out! Currently, you need 100 points for a $10 discount. Do you hav
FAIL Do you ship to Germany? -> Yes, we absolutely do ship to Germany! 😊 We use a reliable shipping partner to get your o
FAIL Do you sell audiobooks? -> Hello there! Thanks for your interest in our bookstore. Yes, we absolutely do sell audiobo
FAIL What are the opening hours of the Ankara store? -> Okay, let’s check the opening hours for our Ankara store! Our Ankara store is currently op
score: 2/10
== rag
PASS How many days do I have to return a book? -> You have 21 days to return a book if it’s unread and in its original condition.
PASS How much does express shipping cost? -> Express shipping costs 9.90 USD.
PASS Is shipping free for a 50 USD order? -> Yes, shipping is free for orders over 40 USD. Since your order is 50 USD, you qualify for
PASS How long are gift cards valid? -> Gift cards are valid for 3 years from the date of purchase.
PASS Can I exchange a gift card for cash? -> No, gift cards cannot be exchanged for cash.
PASS Can I return a book after 30 days? -> No, you cannot return a book after 30 days. Our return policy allows returns within 21 day
FAIL How many points do I need for a 10 USD discount? -> You need 100 points for a 5 USD discount.
PASS Do you ship to Germany? -> I do not have information about shipping to Germany. Our standard shipping information cov
PASS Do you sell audiobooks? -> I apologize, but this information doesn’t cover our audiobook offerings.
PASS What are the opening hours of the Ankara store? -> I apologize, but the provided context doesn't include the opening hours of the Ankara stor
score: 9/10
== rag+rules+sources
PASS How many days do I have to return a book? -> You can return a book within 21 days of delivery [3].
PASS How much does express shipping cost? -> Express shipping costs 9.90 USD
PASS Is shipping free for a 50 USD order? -> Yes, shipping is free for orders over 40 USD.
PASS How long are gift cards valid? -> Gift cards are valid for 3 years from the date of purchase.
FAIL Can I exchange a gift card for cash? -> I don't know
FAIL Can I return a book after 30 days? -> I don't know
FAIL How many points do I need for a 10 USD discount? -> You need 100 points for a 5 USD discount.
PASS Do you ship to Germany? -> I don't know
PASS Do you sell audiobooks? -> I don't know
PASS What are the opening hours of the Ankara store? -> I don't know
score: 7/10
Bağlamsız model 2/10 aldı ve geçtiği iki soru da şans: "50 dolar ve üstüne ücretsiz kargo" bizim kuralımız değil, genel bir tahmin; tesadüfen regex'e uydu. Almanya'ya kargo yaptığını, sesli kitap sattığını ve Ankara mağazasının açılış saatlerini gönül rahatlığıyla uydurdu. RAG bu tabloyu tamamen değiştiriyor.
Asıl şaşırtıcı sonuç son iki satırda. Daha katı kuralları ve kaynak zorunluluğu olan varyant, düz RAG'den daha düşük puan aldı: 9'a karşı 7. Uydurma cevap sayısı ikisinde de sıfır; fark, kurallı varyantın cevabı bağlamdan bir adım çıkarım gerektiren iki soruda "bilmiyorum" demesi. Yani kurallar halüsinasyonu değil, yardımcı olmayı azalttı. Bunu tek tek örneklere bakarak değil, golden set sayesinde gördük. 10 USD sorusu ise üç varyantta da başarısız: hesap gerektiren sorular bu boyuttaki bir model için ayrı bir zayıflık.
Bu sayılara fazla güvenme: on soru küçük bir set, ve aynı Ollama'yı paralel kullanan işler varken temperature 0'da bile metin birebir tekrar etmeyebiliyor. Gerçek bir sette yüzlerce soru, gerçek kullanıcı sorularından örnekler ve her soru için birden fazla çalıştırma istersin. Ama yöntem aynı: sabit set, sabit puanlama, her değişiklikte yeniden çalıştır.
Sık Düşülen Tuzaklar
Birincisi, kendinden emin tonu doğruluk sanmak. Bu yazıdaki uydurma yazar adıyla gerçek Frank Herbert cevabı aynı biçimde geldi. Ton bir sinyal değildir.
İkincisi, temperature'ı halüsinasyon ayarı sanmak. Temperature 0 cevabı tekrarlanabilir yapar, doğru yapmaz.
Üçüncüsü, kaynak gösterimini doğrulama sanmak. Model source_ids'e de bir sayı uydurabilir; o sayının gerçekten verdiğin chunk'lardan biri olduğunu ve cevabın o chunk'la tutarlı olduğunu kodla kontrol et.
Dördüncüsü, yalnızca halüsinasyonu ölçmek. Hiçbir şey uydurmayan ama her şeye "bilmiyorum" diyen bir asistan da işe yaramaz. Golden set'e cevabı dokümanda olan ama küçük bir çıkarım gerektiren sorular koy.
Beşincisi, puanlama kodunu test etmemek. Bizim ilk puanlamamız düz RAG'e haksız yere üç soru kaybettirdi. Puanlama fonksiyonunu da birkaç örnek cevapla elle kontrol et; mümkünse serbest metin yerine structured output alanlarını puanla.
Altıncısı, golden set'i bir kez yazıp unutmak. Production'da yanlış cevap verilen her gerçek soru, sete eklenmeye aday bir test vakasıdır.
Küçük Bir Deneme
★☆☆ hallucination.ts'e kendi uydurduğun üç kitap başlığı ver. Model kaçında "bilmiyorum" diyor, kaçında yazar uyduruyor? Aynı soruları qwen3:4b ile dene ve farkı not et.
★★☆ Golden set'e beş soru ekle: ikisi dokümanda olmayan, üçü çıkarım gerektiren (ör. "I ordered at 3 pm with express shipping, when will it arrive?"). Üç varyantın puanları nasıl değişiyor?
★★☆ grounded.ts'teki system prompt'a "If the answer can be inferred from the sources with simple reasoning, answer it." satırını ekle. Kurallı varyantın puanı 7'den yukarı çıkıyor mu, yoksa uydurma cevaplar mı başlıyor?
★★★ eval.ts'e dördüncü bir varyant ekle: cevabı gemma3:4b'ye ikinci bir istekle "Does this answer follow from these sources? Reply yes or no." diye sorduran bir hakem (LLM-as-judge). Hakem, verify'ın kaçırdığı 10 USD hatasını yakalıyor mu? Hakemin kendisinin ne kadar güvenilir olduğunu nasıl ölçersin?
Böylece kavramsal temelin büyük kısmını bitirdik: token'lar, context window, sampling, prompt, structured output, araçlar, embedding, RAG ve şimdi ölçmek. Bir sonraki yazıda işin hesap tarafına geçeceğiz: token'ı maliyete ve gecikmeye çevirecek, bu makinede tokens/s ölçecek, 4b ile 12b modeli karşılaştıracak ve yerel ile bulut arasında nasıl karar vereceğimizi konuşacağız.