feat(llm,issues): Türkçe çıktı — prompt v2 + Gitea issue body Türkçe başlıklar

apps/worker/src/lib/prompts.ts:
- SASE_CONTEXT'e Türkçe çıktı kuralı eklendi (tüm doğal dil alanları Türkçe,
  enum/const/teknik tanımlayıcılar İngilizce kalır)
- 7 seed prompt v2'ye bump edildi (bug_triage, ux_friction, payment_issue,
  onboarding_stuck, upgrade_hesitation, pattern_bundle, provider_quality)
- name suffix "v2 (TR)"

apps/worker/src/lib/seed-runtime.ts:
- Yeni version insert ederken, aynı tag'in eski active versiyonlarını otomatik
  deactivate eder (publishPromptVersion ile aynı davranış)

apps/web/src/lib/gitea.ts (buildIssueBody):
- Tüm bölüm başlıkları Türkçe: Bağlam, Hipotez, Kullanıcı niyeti, Friction
  noktası, Tekrarlama adımları, Etkilenen alan, Hata imzası, Şüpheli provider,
  Hata modu, Hızlı çözüm, Uzun vadeli çözüm, Önerilen aksiyon, Araştırma
  önerisi, Tıkanma noktası, Anlaşılmayan kavram, Dokümantasyon eksiği,
  Önerilen in-app yardım, Tahmini efor, Kanıt, Definition of Done
- Önem/Tekrar/İlk/Son görüldü etiketleri

Sonraki analyze döngüsü itibarıyla yeni insight'ların metin alanları Türkçe.
Mevcut insight #cmp4o*, #cmp5e* (v1 ile üretilmiş) değişmez.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Semih
2026-05-16 10:45:43 +03:00
parent f93cabb932
commit 752e69cd01
4 changed files with 55 additions and 48 deletions

View File

@@ -157,71 +157,71 @@ export function buildIssueBody(opts: {
}): string { }): string {
const b = opts.body; const b = opts.body;
const lines: string[] = [ const lines: string[] = [
`## Context (insight-driven)`, `## Bağlam (insight kaynaklı)`,
``, ``,
`- **Insight ID:** \`${opts.insightId}\``, `- **Insight ID:** \`${opts.insightId}\``,
`- **Type:** ${opts.type}`, `- **Tür:** ${opts.type}`,
`- **Severity:** ${opts.severity}`, `- **Önem:** ${opts.severity}`,
`- **Occurrences:** ${opts.occurrenceCount} session${opts.occurrenceCount === 1 ? "" : "s"}, ${opts.uniqueUserCount} user${opts.uniqueUserCount === 1 ? "" : "s"}`, `- **Tekrar:** ${opts.occurrenceCount} oturum, ${opts.uniqueUserCount} kullanıcı`,
`- **First seen:** ${opts.firstSeenAt.toISOString().slice(0, 10)}`, `- **İlk görüldü:** ${opts.firstSeenAt.toISOString().slice(0, 10)}`,
`- **Last seen:** ${opts.lastSeenAt.toISOString().slice(0, 10)}`, `- **Son görüldü:** ${opts.lastSeenAt.toISOString().slice(0, 10)}`,
`- **AI confidence:** ${opts.confidence?.toFixed(2) ?? "—"}`, `- **AI güven:** ${opts.confidence?.toFixed(2) ?? "—"}`,
`- **Model:** ${opts.model}`, `- **Model:** ${opts.model}`,
``, ``,
]; ];
if (typeof b.hypothesis === "string") lines.push(`## Hypothesis`, b.hypothesis as string, ""); if (typeof b.hypothesis === "string") lines.push(`## Hipotez`, b.hypothesis as string, "");
if (typeof b.intent_hypothesis === "string") lines.push(`## User intent`, b.intent_hypothesis as string, ""); if (typeof b.intent_hypothesis === "string") lines.push(`## Kullanıcı niyeti`, b.intent_hypothesis as string, "");
if (typeof b.friction_point === "string") lines.push(`## Friction point`, b.friction_point as string, ""); if (typeof b.friction_point === "string") lines.push(`## Friction noktası`, b.friction_point as string, "");
if (Array.isArray(b.reproduce_steps) && b.reproduce_steps.length) { if (Array.isArray(b.reproduce_steps) && b.reproduce_steps.length) {
lines.push(`## Reproduce steps`); lines.push(`## Tekrarlama adımları`);
(b.reproduce_steps as string[]).forEach((s, i) => lines.push(`${i + 1}. ${s}`)); (b.reproduce_steps as string[]).forEach((s, i) => lines.push(`${i + 1}. ${s}`));
lines.push(""); lines.push("");
} }
if (typeof b.affected_route === "string" || typeof b.affected_component_hypothesis === "string") { if (typeof b.affected_route === "string" || typeof b.affected_component_hypothesis === "string") {
lines.push(`## Affected`); lines.push(`## Etkilenen alan`);
if (b.affected_route) lines.push(`- Route: \`${b.affected_route}\``); if (b.affected_route) lines.push(`- Route: \`${b.affected_route}\``);
if (b.affected_component_hypothesis) lines.push(`- Component: ${b.affected_component_hypothesis}`); if (b.affected_component_hypothesis) lines.push(`- Component: ${b.affected_component_hypothesis}`);
lines.push(""); lines.push("");
} }
if (typeof b.error_signature === "string") { if (typeof b.error_signature === "string") {
lines.push(`## Error signature`, "```", b.error_signature as string, "```", ""); lines.push(`## Hata imzası`, "```", b.error_signature as string, "```", "");
} }
if (typeof b.affected_provider === "string" || typeof b.implicated_provider === "string") { if (typeof b.affected_provider === "string" || typeof b.implicated_provider === "string") {
lines.push(`## Implicated provider`, `${b.affected_provider ?? b.implicated_provider}`, ""); lines.push(`## Şüpheli provider`, `${b.affected_provider ?? b.implicated_provider}`, "");
} }
if (typeof b.failure_mode === "string") lines.push(`## Failure mode`, `${b.failure_mode}`, ""); if (typeof b.failure_mode === "string") lines.push(`## Hata modu`, `${b.failure_mode}`, "");
if (typeof b.quick_fix === "string") lines.push(`## Quick fix`, b.quick_fix as string, ""); if (typeof b.quick_fix === "string") lines.push(`## Hızlı çözüm`, b.quick_fix as string, "");
if (typeof b.long_term_fix === "string") lines.push(`## Long-term fix`, b.long_term_fix as string, ""); if (typeof b.long_term_fix === "string") lines.push(`## Uzun vadeli çözüm`, b.long_term_fix as string, "");
if (typeof b.suggested_action === "string") lines.push(`## Suggested action`, b.suggested_action as string, ""); if (typeof b.suggested_action === "string") lines.push(`## Önerilen aksiyon`, b.suggested_action as string, "");
if (Array.isArray(b.suggested_investigation) && b.suggested_investigation.length) { if (Array.isArray(b.suggested_investigation) && b.suggested_investigation.length) {
lines.push(`## Suggested investigation`); lines.push(`## Araştırma önerisi`);
(b.suggested_investigation as string[]).forEach((s) => lines.push(`- ${s}`)); (b.suggested_investigation as string[]).forEach((s) => lines.push(`- ${s}`));
lines.push(""); lines.push("");
} }
if (typeof b.block_point === "string") lines.push(`## Block point`, b.block_point as string, ""); if (typeof b.block_point === "string") lines.push(`## Tıkanma noktası`, b.block_point as string, "");
if (typeof b.unclear_concept === "string") lines.push(`## Unclear concept`, b.unclear_concept as string, ""); if (typeof b.unclear_concept === "string") lines.push(`## Anlaşılmayan kavram`, b.unclear_concept as string, "");
if (typeof b.documentation_gap === "string") lines.push(`## Documentation gap`, b.documentation_gap as string, ""); if (typeof b.documentation_gap === "string") lines.push(`## Dokümantasyon eksiği`, b.documentation_gap as string, "");
if (typeof b.suggested_in_app_help === "string") lines.push(`## Suggested in-app help`, b.suggested_in_app_help as string, ""); if (typeof b.suggested_in_app_help === "string") lines.push(`## Önerilen in-app yardım`, b.suggested_in_app_help as string, "");
if (typeof b.suggested_fix_effort === "string") { if (typeof b.suggested_fix_effort === "string") {
lines.push(`**Effort estimate:** ${b.suggested_fix_effort}`, ""); lines.push(`**Tahmini efor:** ${b.suggested_fix_effort}`, "");
} }
lines.push( lines.push(
`## Evidence`, `## Kanıt`,
``, ``,
`- Panel link: ${opts.panelUrl}/insights/i/${opts.insightId}`, `- Panel link: ${opts.panelUrl}/insights/i/${opts.insightId}`,
...opts.relatedSessionIds.slice(0, 5).map( ...opts.relatedSessionIds.slice(0, 5).map(
(sid) => `- Session: ${opts.panelUrl}/insights/sessions/${sid}`, (sid) => `- Oturum: ${opts.panelUrl}/insights/sessions/${sid}`,
), ),
``, ``,
`## Definition of Done`, `## Definition of Done`,
``, ``,
`- [ ] Fix deployed`, `- [ ] Fix deploy edildi`,
`- [ ] Validation period (14 days) clean (no regression)`, `- [ ] Validation periyodu (14 gün) regresyonsuz geçti`,
``, ``,
`---`, `---`,
`*Generated by Süper Panel insight pipeline. Insight #${opts.insightId.slice(0, 8)}.*`, `*Süper Panel insight pipeline tarafından üretildi. Insight #${opts.insightId.slice(0, 8)}.*`,
); );
return lines.join("\n"); return lines.join("\n");

File diff suppressed because one or more lines are too long

View File

@@ -21,12 +21,14 @@ const SASE_CONTEXT = `Sase.tr context:
- 4 upstream providers: PL24 (Partslink24), PCAT, RMEX, TecDoc. Each can timeout/fail independently. - 4 upstream providers: PL24 (Partslink24), PCAT, RMEX, TecDoc. Each can timeout/fail independently.
- Auth: JWT, sticky session routing. - Auth: JWT, sticky session routing.
- Subscription tiers: starter, brand_specific, full. Trial flow exists. - Subscription tiers: starter, brand_specific, full. Trial flow exists.
- Common URL paths: /vin-lookup, /dashboard/search, /dashboard/catalog/<brand>/<vehicleId>, /api-keys, /subscription, /upgrade, /pricing. - Common URL paths: /dashboard/search, /dashboard/vehicles/<id>, /dashboard/vehicles/<id>/categories/<id>, /dashboard/catalog, /dashboard/subscription, /dashboard/history, /dashboard/api-keys, /upgrade, /pricing.
Output rules: Output rules:
- Return ONLY valid JSON matching the schema. No markdown, no code fences, no preamble. - Return ONLY valid JSON matching the schema. No markdown, no code fences, no preamble.
- If you are uncertain, lower the confidence; do not invent specifics. - If you are uncertain, lower the confidence; do not invent specifics.
- All string fields are concise (titles ≤120 chars, hypothesis ≤500 chars).`; - All string fields are concise (titles ≤120 chars, hypothesis ≤500 chars).
- **Tüm metin alanlarını TÜRKÇE yaz** (title, hypothesis, reproduce_steps, quick_fix, long_term_fix, friction_point, intent_hypothesis, suggested_action, suggested_investigation, error_signature description, block_point, unclear_concept, documentation_gap, suggested_in_app_help, vb.). Sadece enum/const/teknik tanımlayıcı alanlar İngilizce kalır (örn. severity: "P0"|"P1"|..., type: "bug"|..., friction_type: "copy"|"layout"|..., failure_mode: "timeout"|..., implicated_provider: "PL24"|"PCAT"|...).
- Türkçe yazarken doğal, net, eyleme dönüştürülebilir cümleler kur. Teknik terimleri (route, component, endpoint, provider, fingerprint) Türkçe gloss'lamadan kullan: "/dashboard/search route'unda...", "VinLookupController component'inde...".`;
const BUG_TRIAGE_SCHEMA = { const BUG_TRIAGE_SCHEMA = {
type: "object", type: "object",
@@ -150,8 +152,8 @@ const PROVIDER_SCHEMA = {
export const SEED_PROMPTS: PromptTemplate[] = [ export const SEED_PROMPTS: PromptTemplate[] = [
{ {
tag: "bug_triage", tag: "bug_triage",
version: 1, version: 2,
name: "Bug Triage v1", name: "Bug Triage v2 (TR)",
systemPrompt: `You analyze bugs found in user sessions of a B2B SaaS. Extract reproducible steps, locate the failing component, and propose a hypothesis. systemPrompt: `You analyze bugs found in user sessions of a B2B SaaS. Extract reproducible steps, locate the failing component, and propose a hypothesis.
${SASE_CONTEXT} ${SASE_CONTEXT}
@@ -168,8 +170,8 @@ This session contains a bug. Return JSON per the schema.`,
}, },
{ {
tag: "ux_friction", tag: "ux_friction",
version: 1, version: 2,
name: "UX Friction v1", name: "UX Friction v2 (TR)",
systemPrompt: `You are a senior UX engineer. Given a session timeline, identify where the user got stuck and propose a quick fix and a long-term fix. systemPrompt: `You are a senior UX engineer. Given a session timeline, identify where the user got stuck and propose a quick fix and a long-term fix.
${SASE_CONTEXT} ${SASE_CONTEXT}
@@ -186,8 +188,8 @@ Return JSON per the schema.`,
}, },
{ {
tag: "payment_issue", tag: "payment_issue",
version: 1, version: 2,
name: "Payment Issue v1", name: "Payment Issue v2 (TR)",
systemPrompt: `You analyze payment/conversion failures in a B2B SaaS. Identify failure mode and likely cause. systemPrompt: `You analyze payment/conversion failures in a B2B SaaS. Identify failure mode and likely cause.
${SASE_CONTEXT} ${SASE_CONTEXT}
@@ -204,8 +206,8 @@ Return JSON per the schema.`,
}, },
{ {
tag: "onboarding_stuck", tag: "onboarding_stuck",
version: 1, version: 2,
name: "Onboarding Stuck v1", name: "Onboarding Stuck v2 (TR)",
systemPrompt: `You analyze new-user onboarding sessions where the user did not reach first value. Propose where they got stuck and what help is missing. systemPrompt: `You analyze new-user onboarding sessions where the user did not reach first value. Propose where they got stuck and what help is missing.
${SASE_CONTEXT} ${SASE_CONTEXT}
@@ -222,8 +224,8 @@ Return JSON per the schema.`,
}, },
{ {
tag: "upgrade_hesitation", tag: "upgrade_hesitation",
version: 1, version: 2,
name: "Upgrade Hesitation v1", name: "Upgrade Hesitation v2 (TR)",
systemPrompt: `You analyze sessions where a user reached the pricing/upgrade page but did not convert. Identify likely concern (price/feature/trust) and propose intervention. systemPrompt: `You analyze sessions where a user reached the pricing/upgrade page but did not convert. Identify likely concern (price/feature/trust) and propose intervention.
${SASE_CONTEXT} ${SASE_CONTEXT}
@@ -240,8 +242,8 @@ Return JSON per the schema.`,
}, },
{ {
tag: "pattern_bundle", tag: "pattern_bundle",
version: 1, version: 2,
name: "Pattern Bundle v1", name: "Pattern Bundle v2 (TR)",
systemPrompt: `You are given an aggregate timeline that bundles 3+ user sessions sharing the same fingerprint (similar tags, route, and error signature). Synthesize the cross-session pattern and recommend priority. systemPrompt: `You are given an aggregate timeline that bundles 3+ user sessions sharing the same fingerprint (similar tags, route, and error signature). Synthesize the cross-session pattern and recommend priority.
${SASE_CONTEXT} ${SASE_CONTEXT}
@@ -262,8 +264,8 @@ Return JSON per the schema. Use occurrence patterns from the bundle summary to e
}, },
{ {
tag: "provider_quality", tag: "provider_quality",
version: 1, version: 2,
name: "Provider Quality v1", name: "Provider Quality v2 (TR)",
systemPrompt: `You analyze upstream provider failures (PL24/PCAT/RMEX/TecDoc) impacting Sase.tr users. Identify which provider failed and propose action. systemPrompt: `You analyze upstream provider failures (PL24/PCAT/RMEX/TecDoc) impacting Sase.tr users. Identify which provider failed and propose action.
${SASE_CONTEXT} ${SASE_CONTEXT}

View File

@@ -12,12 +12,17 @@ const DEFAULT_BUDGETS: Array<{ key: string; value: unknown }> = [
]; ];
export async function upsertSeedData(): Promise<void> { export async function upsertSeedData(): Promise<void> {
// Prompt templates — only insert if (tag, version) doesn't exist. // Prompt templates — insert new versions if (tag, version) doesn't exist.
// When inserting a new version, deactivate older active versions of the same tag.
for (const p of SEED_PROMPTS) { for (const p of SEED_PROMPTS) {
const existing = await prisma.promptTemplate.findUnique({ const existing = await prisma.promptTemplate.findUnique({
where: { tag_version: { tag: p.tag, version: p.version } }, where: { tag_version: { tag: p.tag, version: p.version } },
}); });
if (!existing) { if (!existing) {
await prisma.promptTemplate.updateMany({
where: { tag: p.tag, active: true },
data: { active: false },
});
await prisma.promptTemplate.create({ await prisma.promptTemplate.create({
data: { data: {
tag: p.tag, tag: p.tag,
@@ -32,7 +37,7 @@ export async function upsertSeedData(): Promise<void> {
active: true, active: true,
}, },
}); });
console.log(`[seed] inserted prompt ${p.tag}@v${p.version}`); console.log(`[seed] inserted prompt ${p.tag}@v${p.version} (deactivated older versions)`);
} }
} }