Python ya da Node.js + Anthropic APIBitirme
Bitirme projesi: ders notlarından soru-cevap asistanı
Bir dersin notlarını (kendi dersin ya da bu sayfadaki sekiz not) okuyup kaynak göstererek cevap veren, notlarda olmayanı "bilmiyorum" diye bildiren, kişisel veriyi maskeleyen ve cevabı akışlı gösteren bir web asistanı.
Senaryo
Öğrenci "Dönem projesi ne zaman teslim edilir, iki gün geç kalırsam ne olur?" diye sorar. Asistan soruyu maskeleme süzgecinden geçirir, notlarda arar, en ilgili parçaları kaynak etiketleriyle prompta koyar, modelden JSON şemalı bir cevap (cevap, kaynaklar, güven) alır, doğrular ve metni tarayıcıya akışla yollar. Cevap notlarda yoksa bunu söyler; hesap gerekiyorsa bir hesap aracı çağırır. Her sürüm bir değerlendirme kümesiyle ölçülür.
Gereksinimler (test edilebilir)
- G1: Değerlendirme kümesindeki (en az 25 soru, en az 5'i notlarda olmayan) sorularda normalize doğruluk en az %80.
- G2: Notlarda olmayan soruların en az %90'ında asistan "notlarda yok" der, cevap uydurmaz.
- G3: Her cevap en az bir kaynak etiketi içerir ve dayanaklılık kontrolünden geçer (cevaptaki sayılar kaynakta var).
- G4: E-posta, telefon, T.C. kimlik no ve IBAN modele gitmeden maskelenir; kayıtlarda ham kişisel veri bulunmaz.
- G5: Enjeksiyon kümesindeki (en az 10 örnek) belgelerden hiçbiri sistem kurallarını değiştiremez.
- G6: 95. yüzdelik ilk token süresi 2 s altında; istek başı ortalama maliyet raporlanır ve bütçenin altındadır.
Mimari
- İçeri alma: notları parçalayan ve gömen çevrimdışı betik; dizin dosyası sürüm ve tarih bilgisiyle.
- Sunucu (Node.js ya da Python): kimlik doğrulama, hız sınırı, maskeleme, arama, Anthropic API çağrısı (claude-sonnet-5-5), şema doğrulama, SSE ile akış.
- Araçlar: not_ara ve hesapla; dar yetki, girdi doğrulama, is_error ile hata bildirimi.
- İstemci: akışlı sohbet arayüzü, durdurma düğmesi, kaynak bağlantıları, geçmiş sınırı.
- Yapılandırma: model adı, eşik, k, fiyatlar ve sınırlar tek bir dosyada; API anahtarı yalnız ortam değişkeninde.
Doğrulama: birim testinden değerlendirme kümesine
- Birim: maskeleme (sağlama haneleri dahil), parçalama, yeniden sıralama, şema doğrulama, SSE ayrıştırıcı (bu dersin testleri gibi).
- Değerlendirme: altın küme CI'da her çekme isteğinde koşar; G1–G3 eşik altında kalırsa birleştirme engellenir.
- Güvenlik: enjeksiyon ve kişisel veri kümesi her sürümde koşar (G4, G5).
- Uçtan uca: sahte model sağlayıcısıyla durdurma, zaman aşımı, 429 ve şema hatası onarım döngüsü.
- Performans: 50 eşzamanlı istekle ilk token süresi ve maliyet ölçümü (G6).
Hattı 3B izle
Kendi geri getirme adımını kurmadan önce dersinkini çalışırken izle: sorgu gömme uzayına düşer, en yakın parçalar bulunur, yeniden sıralanır, eşikle süzülür ve kaynaklı bir cevaba dönüşür. Senin sistemin de bu adımların her birini kayıtlarında açıklayabilmeli.
RAG'ı 3B izle: sorgu → komşular → yeniden sıralama → cevap
Mini RAG'ın parçaları 3B gömme uzayında. Oynat'a bas: sorgu merkezden yerine uçar, vektör arama 2k adayı bulur, en yakın k parça seçilir, hibrit yeniden sıralama sırayı değiştirir, eşiği geçen parçalar yeşile döner ve kaynaklı cevap kurulur. k, eşik ve yeniden sıralamayı değiştir; Adım düğmesiyle tek tek ilerle.
Başlangıç kodu
import pathlib, re
import numpy as np
import anthropic
from sentence_transformers import SentenceTransformer # yerel gömme modeli
client = anthropic.Anthropic() # anahtar ANTHROPIC_API_KEY ortam değişkeninden
embedder = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
def chunk(text, size=60, overlap=15):
words = text.split()
step = size - overlap
return [" ".join(words[i:i + size]) for i in range(0, max(1, len(words) - overlap), step)]
# 1) Dizinleme: her not parçalanır, her parça kaynağıyla birlikte gömülür
chunks = []
for path in sorted(pathlib.Path("notlar").glob("*.txt")):
for i, c in enumerate(chunk(path.read_text(encoding="utf-8"))):
chunks.append({"id": f"{path.stem}#{i}", "text": c})
E = embedder.encode([c["text"] for c in chunks], normalize_embeddings=True)
def words(s): return set(re.findall(r"\w+", s.lower()))
def retrieve(question, k=4, keep=2, threshold=0.35):
q = embedder.encode([question], normalize_embeddings=True)[0]
scores = E @ q
top = [(float(scores[i]), chunks[i]) for i in np.argsort(-scores)[:k]]
# 2) Yeniden sıralama: vektör puanı + anahtar kelime örtüşmesi
qw = words(question)
rer = sorted(top, key=lambda p: -(0.7 * p[0] + 0.3 * len(qw & words(p[1]["text"])) / max(1, len(qw))))
return [c for s, c in rer[:keep] if s >= threshold] # eşik altı: kanıt yok
def answer(question):
found = retrieve(question)
if not found:
return "Bu sorunun cevabı notlarda yok."
context = "\n".join(f'<belge id="{c["id"]}">{c["text"]}</belge>' for c in found)
response = client.messages.create(
model="claude-sonnet-5-5", max_tokens=600,
system="Yalnız <belge> içeriğine dayanarak cevap ver. Her cümlenin sonuna [id] ile kaynak yaz. Belgelerde yoksa bilmediğini söyle.",
messages=[{"role": "user", "content": f"{context}\n\n<soru>{question}</soru>"}],
)
return "".join(b.text for b in response.content if b.type == "text")
print(answer("Dönem projesi ne zaman teslim edilir?"))// server.mjs (npm install @anthropic-ai/sdk express)
import Anthropic from "@anthropic-ai/sdk";
import express from "express";
const client = new Anthropic(); // anahtar sunucuda, ANTHROPIC_API_KEY ortam değişkeninde; tarayıcıya asla gitmez
const app = express();
app.use(express.json());
app.post("/api/chat", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
const stream = client.messages.stream({
model: "claude-sonnet-5-5",
max_tokens: 4096,
system: "Yalnız ders notlarına dayanarak cevap ver.",
messages: req.body.messages, // istemcinin gönderdiği geçmiş: uzunluğunu sınırla
});
req.on("close", () => stream.abort()); // kullanıcı durdurdu ya da sekmeyi kapattı
stream.on("text", (delta) => {
res.write(`data: ${JSON.stringify({ type: "delta", text: delta })}\n\n`);
});
try {
const final = await stream.finalMessage();
res.write(`data: ${JSON.stringify({ type: "done", usage: final.usage })}\n\n`);
} catch (err) {
res.write(`data: ${JSON.stringify({ type: "error", message: "cevap alınamadı" })}\n\n`);
}
res.end();
});
app.listen(3000);Teslimler
- 3. üniteden sonraÖneri ve gereksinimler
Bir sayfa: kullanıcı, kullanım senaryoları, G1–G6'yı kendi cümlelerinle test edilebilir yaz; not kümesi seçildi; ilk sistem promptu ve 10 soruluk taslak değerlendirme kümesi depoda.
Prompt kalıpları: görevi açıkça tarif etmek - 5. üniteden sonraAra kontrol: RAG hattı
Parçalama, gömme ve arama çalışıyor; isabet@k altın kümede ölçüldü; şemalı cevap ve doğrulama; mimari belgesi (bileşenler, veri akışı).
RAG uçtan uca: belgeden kaynaklı cevap - 7. üniteden sonraDeğerlendirme raporu
25+ soruluk küme, ölçütler ve rubrik CI'da; iki prompt sürümünün karşılaştırması; hata analizi (hangi soru neden yanlış).
Değerlendirme: ölçüt, rubrik, hakem ve uydurma kontrolü - 8. üniteden sonraGüvenlik incelemesi
Tehdit modeli, maskeleme ve enjeksiyon kümeleri, veri envanteri (KVKK: amaç, süre, saklama yeri).
Güvenlik ve gizlilik: kişisel veri, prompt enjeksiyonu, KVKK - 10. üniteden sonraDemo, maliyet raporu ve sunum
Akışlı arayüzle canlı ya da kayıtlı demo; G1–G6 kanıt tablosu; aylık maliyet ve gecikme ölçümü; 10 dakikalık sunum, final raporu ve depo.
Akışlı web sohbet arayüzü (JavaScript)
