2026-09-03 21:01:52 +03:30
|
|
|
|
/**
|
|
|
|
|
|
* Dev check: Session 1 coach system prompt contains course context and teaching-style rules.
|
|
|
|
|
|
* Run: npx tsx --tsconfig tsconfig.json scripts/verify-coach-prompt.ts
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { getSessionByOrder, PROGRAM_SESSION_COUNT } from '../src/data/program/sessions';
|
|
|
|
|
|
import {
|
|
|
|
|
|
buildCoachSystemPrompt,
|
|
|
|
|
|
buildCoachSessionStartCue,
|
|
|
|
|
|
COACH_SESSION_START_CUE
|
|
|
|
|
|
} from '../src/lib/aiCoach/buildCoachSystemPrompt';
|
|
|
|
|
|
|
|
|
|
|
|
const s = getSessionByOrder(1);
|
|
|
|
|
|
if (!s) throw new Error('Session 1 missing');
|
|
|
|
|
|
|
|
|
|
|
|
const s11 = getSessionByOrder(11);
|
|
|
|
|
|
if (!s11) throw new Error('Session 11 missing');
|
|
|
|
|
|
|
|
|
|
|
|
const ctx = {
|
|
|
|
|
|
sessionId: s.id,
|
|
|
|
|
|
order: s.order,
|
|
|
|
|
|
title: s.title,
|
|
|
|
|
|
description: s.description,
|
|
|
|
|
|
estimatedDurationMinutes: s.estimatedDurationMinutes,
|
|
|
|
|
|
objectivesHint: s.description,
|
|
|
|
|
|
blocks: s.blocks,
|
|
|
|
|
|
totalSessions: PROGRAM_SESSION_COUNT,
|
|
|
|
|
|
isFinalSession: s.order >= PROGRAM_SESSION_COUNT,
|
|
|
|
|
|
nextSessionUnlocksAt: null,
|
|
|
|
|
|
};
|
|
|
|
|
|
const prompt = buildCoachSystemPrompt(ctx);
|
|
|
|
|
|
|
|
|
|
|
|
const ctx11 = {
|
|
|
|
|
|
sessionId: s11.id,
|
|
|
|
|
|
order: s11.order,
|
|
|
|
|
|
title: s11.title,
|
|
|
|
|
|
description: s11.description,
|
|
|
|
|
|
estimatedDurationMinutes: s11.estimatedDurationMinutes,
|
|
|
|
|
|
objectivesHint: s11.description,
|
|
|
|
|
|
blocks: s11.blocks,
|
|
|
|
|
|
totalSessions: PROGRAM_SESSION_COUNT,
|
|
|
|
|
|
isFinalSession: true,
|
|
|
|
|
|
nextSessionUnlocksAt: null,
|
|
|
|
|
|
};
|
|
|
|
|
|
const prompt11 = buildCoachSystemPrompt(ctx11);
|
|
|
|
|
|
const startCue11 = buildCoachSessionStartCue(11);
|
|
|
|
|
|
|
|
|
|
|
|
const checks = {
|
|
|
|
|
|
// Existing course context & grounding
|
|
|
|
|
|
title: prompt.includes(s.title),
|
|
|
|
|
|
objective: prompt.includes(s.description),
|
|
|
|
|
|
exercise: prompt.includes('پیشآزمون'),
|
|
|
|
|
|
bansGenericHelp: prompt.includes('چه کمکی میتونم بکنم؟'),
|
|
|
|
|
|
hasOpeningRules: prompt.includes('SESSION OPENING'),
|
|
|
|
|
|
pretestNote: prompt.includes('NEVER MENTION PRE-TEST IN USER-FACING CHAT'),
|
|
|
|
|
|
groundingSection: prompt.includes('STRICT COURSE GROUNDING'),
|
|
|
|
|
|
assessmentBoundary: prompt.includes('ASSESSMENT BOUNDARY'),
|
|
|
|
|
|
noNewAssessment: prompt.includes('administer another psychological assessment'),
|
|
|
|
|
|
noInventQuestionnaire: prompt.includes('invent questionnaire items'),
|
|
|
|
|
|
forbiddenSymptomExamples: prompt.includes('در هفته گذشته چه میزان استرس'),
|
|
|
|
|
|
noInventRoles: prompt.includes('Do NOT invent role lists'),
|
|
|
|
|
|
noGuessGap: prompt.includes('Do not guess'),
|
|
|
|
|
|
noGenericPsychFill: prompt.includes('general psychological knowledge'),
|
|
|
|
|
|
thinContentRule: prompt.includes('CRITICAL THIN-CONTENT RULE'),
|
|
|
|
|
|
noSubstitutePretest: prompt.includes('Do NOT retake, recreate, simulate, or substitute'),
|
|
|
|
|
|
noRepeatPretestInChat: prompt.includes('Do NOT repeat the pre-test inside Chat'),
|
2026-09-09 01:31:24 +03:30
|
|
|
|
silentSkipPretest: prompt.includes('silent skip') || prompt.includes('silent-skip'),
|
|
|
|
|
|
omitPretestWhenSpeaking: prompt.includes('ZERO words about it'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
neverMentionPretestToUser: prompt.includes('NEVER mention «پیشآزمون»'),
|
2026-09-09 01:31:24 +03:30
|
|
|
|
noPretestNextSectionAnnounce: prompt.includes('Do NOT announce pre-test as the next section'),
|
|
|
|
|
|
noPretestAppDisclaimer: prompt.includes('به صورت جداگانه در اپلیکیشن انجام میشود'),
|
|
|
|
|
|
noPretestInOutline: !prompt.includes('SESSION OUTLINE') || !prompt.match(/SESSION OUTLINE[\s\S]*?- پیشآزمون/),
|
|
|
|
|
|
pretestStrippedFromIntroText: !prompt.includes('و پیشآزمون اجراء میشود'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
assessmentResultsRule: prompt.includes('do NOT invent interpretation'),
|
|
|
|
|
|
completionSignal: prompt.includes('SESSION COMPLETION SIGNAL'),
|
|
|
|
|
|
completionMarker: prompt.includes('COACH_SESSION_COMPLETE'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — educational package introduction
|
|
|
|
|
|
educationalPackageIntro: prompt.includes('introduce the overall educational package'),
|
|
|
|
|
|
packageMultipleSessions: prompt.includes('multiple guided sessions'),
|
|
|
|
|
|
packageConversationLearning: prompt.includes('learning happens through conversation and interaction'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — tone & language
|
|
|
|
|
|
informalColloquialTone: prompt.includes('informal, colloquial Persian'),
|
|
|
|
|
|
addressLearnerAsTo: prompt.includes('Address the learner with «تو»'),
|
|
|
|
|
|
teenFriendlyLanguage: prompt.includes('teenager'),
|
|
|
|
|
|
allLearnersTeenagers: prompt.includes('ALL learners are teenagers'),
|
|
|
|
|
|
teenAudienceSection: prompt.includes('TEEN AUDIENCE'),
|
|
|
|
|
|
noSpouseQuestion: prompt.includes('با همسر خود یک فعالیت نشاطآور'),
|
|
|
|
|
|
teenAdaptFriends: prompt.includes('friends, family, or classmates'),
|
|
|
|
|
|
simpleLanguageGuidance: prompt.includes('explain it simply'),
|
|
|
|
|
|
noChildishTone: prompt.includes('Do NOT be childish'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — anti-over-questioning
|
|
|
|
|
|
antiOverQuestioning: prompt.includes('NOT an interviewer'),
|
|
|
|
|
|
forbiddenRepeatedQuestions: prompt.includes('نظرت چیست؟'),
|
|
|
|
|
|
noTopicChoice: prompt.includes('Do NOT ask the user to choose the next topic'),
|
|
|
|
|
|
noPermissionToContinue: prompt.includes('Do NOT ask permission to continue'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — negative-answer handling
|
|
|
|
|
|
negativeAnswerHandling: prompt.includes('NEGATIVE / UNKNOWN ANSWERS'),
|
|
|
|
|
|
noInterrogateFurther: prompt.includes('Do NOT interrogate further'),
|
|
|
|
|
|
unknownAnswerExample: prompt.includes('نمیدانم'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — section-end questions
|
2026-09-21 08:00:24 +03:30
|
|
|
|
sectionEndQuestion: prompt.includes('تا اینجا چیزی که گفتم، سؤالی داری؟'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
sectionEndNaturalBoundaries: prompt.includes('natural section boundaries'),
|
|
|
|
|
|
|
|
|
|
|
|
// Session 1 — no previous session
|
|
|
|
|
|
session1NoPreviousSession: prompt.includes('NO previous educational session'),
|
|
|
|
|
|
noReviewPreviousSession: prompt.includes('مرور جلسه قبل'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — package identity & 11 sessions
|
|
|
|
|
|
iranianIslamicPackage: prompt.includes('بسته آموزشی مثبتنگر ایرانی ـ اسلامی'),
|
|
|
|
|
|
programOverviewSection: prompt.includes('PROGRAM OVERVIEW'),
|
|
|
|
|
|
elevenSessionsListed: prompt.includes('1. توجیه و آشناسازی') && prompt.includes('11. مرور و جمعبندی مطالب'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — session outline then teach one by one
|
|
|
|
|
|
sessionOutlineSection: prompt.includes('SESSION OUTLINE'),
|
|
|
|
|
|
listThenTeach: prompt.includes('list these at session start as a short roadmap'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — no identity repetition
|
|
|
|
|
|
noIdentityRepetition: prompt.includes('NO IDENTITY REPETITION'),
|
|
|
|
|
|
noRepeatNovyaHelp: prompt.includes('اینجام تا کمک کنم'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — stay on path / other model
|
|
|
|
|
|
stayOnEducationalPath: prompt.includes('STAY ON THE EDUCATIONAL PATH'),
|
|
|
|
|
|
otherModelRedirect: prompt.includes('another model/assistant'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — package overview only in Session 1
|
|
|
|
|
|
packageOverviewSession1Only: prompt.includes('Session 1 ONLY'),
|
|
|
|
|
|
startCueSession1Package: COACH_SESSION_START_CUE.includes('list the ۱۱ session titles ONCE'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — message formatting
|
|
|
|
|
|
messageFormatting: prompt.includes('MESSAGE FORMATTING'),
|
2026-09-21 08:00:24 +03:30
|
|
|
|
chatgptLayout: prompt.includes('Write like ChatGPT'),
|
|
|
|
|
|
shortParagraphs: prompt.includes('1–2 short sentences per paragraph'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
occasionalEmoji: prompt.includes('EMOJI / STICKER-LIKE EXPRESSIONS'),
|
|
|
|
|
|
emojiHardLimit: prompt.includes('0–1 emoji per message'),
|
2026-09-21 08:00:24 +03:30
|
|
|
|
numberedListFormatting: prompt.includes('numbered list') || prompt.includes('numbered ۱'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
boldKeyPhrases: prompt.includes('double asterisks'),
|
|
|
|
|
|
headingOwnLine: prompt.includes('OWN separate line'),
|
|
|
|
|
|
headingNumberedFirstTime: prompt.includes('FIRST time you introduce a section title'),
|
|
|
|
|
|
plainTextHeadings: prompt.includes('NEVER use Markdown # / ## / ###'),
|
|
|
|
|
|
noVisibleHash: prompt.includes('NEVER show # ## ###'),
|
|
|
|
|
|
noVisibleAt: prompt.includes('NEVER show @ mentions'),
|
|
|
|
|
|
standardPersianPunctuation: prompt.includes('STANDARD PERSIAN PUNCTUATION'),
|
2026-09-21 08:00:24 +03:30
|
|
|
|
justifyFriendlyParagraphs: prompt.includes('justified') || prompt.includes('Write like ChatGPT'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
neverGenerateImages: prompt.includes('NEVER generate, attach, draw, or request images'),
|
|
|
|
|
|
formattingDoesNotChangeContent: prompt.includes('MUST NOT add, remove, or change educational content'),
|
|
|
|
|
|
formattingDoesNotChangeOrder: prompt.includes('MUST NOT change the teaching order'),
|
|
|
|
|
|
tablesOccasionalVariety: prompt.includes('TABLES (occasional variety)'),
|
|
|
|
|
|
|
|
|
|
|
|
// Teaching style — creative / lived elaboration
|
|
|
|
|
|
creativeElaboration: prompt.includes('CREATIVE ELABORATION'),
|
|
|
|
|
|
perfumeExample: prompt.includes('استفاده از عطر'),
|
|
|
|
|
|
scientificColor: prompt.includes('simple scientific'),
|
|
|
|
|
|
experientialThenQuote: prompt.includes('THEN present the course quote'),
|
|
|
|
|
|
|
|
|
|
|
|
// Session 11 final review (Session 11 prompt only)
|
|
|
|
|
|
session11ThreePartReview: prompt11.includes('THREE-PART GRADUAL REVIEW'),
|
|
|
|
|
|
session11NoDuplicatePhrases: prompt11.includes('NO DUPLICATE PHRASES'),
|
|
|
|
|
|
session11PriorSessionsReference: prompt11.includes('PRIOR SESSIONS REFERENCE'),
|
|
|
|
|
|
session11StartCueThreeParts: startCue11.includes('three parts'),
|
|
|
|
|
|
session11StartCueNoDuplicate: startCue11.includes('NEVER duplicate the session title'),
|
|
|
|
|
|
noRepetitionRule: prompt.includes('NO REPETITION (all sessions)'),
|
|
|
|
|
|
|
|
|
|
|
|
// Session start cue
|
|
|
|
|
|
startCue: COACH_SESSION_START_CUE.includes('[SESSION_START]'),
|
2026-09-21 08:00:24 +03:30
|
|
|
|
startCueEducationalPackage: COACH_SESSION_START_CUE.includes('بسته آموزشی مثبتنگر ایرانی ـ اسلامی'),
|
|
|
|
|
|
startCueInformalTone: COACH_SESSION_START_CUE.includes('informal colloquial Persian'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
startCueGrounding: COACH_SESSION_START_CUE.includes('Do not invent missing details'),
|
2026-09-21 08:00:24 +03:30
|
|
|
|
startCueNoAssessment: COACH_SESSION_START_CUE.includes('or assessments'),
|
2026-09-03 21:01:52 +03:30
|
|
|
|
startCueNoTopicChoice: COACH_SESSION_START_CUE.includes('Do not ask the user to choose topics'),
|
|
|
|
|
|
startCueNoOverQuestion: COACH_SESSION_START_CUE.includes('Do not over-question'),
|
|
|
|
|
|
startCueIranianIslamic: COACH_SESSION_START_CUE.includes('بسته آموزشی مثبتنگر ایرانی ـ اسلامی'),
|
|
|
|
|
|
startCueNeverPretest: COACH_SESSION_START_CUE.includes('NEVER mention پیشآزمون'),
|
|
|
|
|
|
startCueNoSpouse: COACH_SESSION_START_CUE.includes('NEVER ask about همسر'),
|
|
|
|
|
|
|
|
|
|
|
|
chars: prompt.length
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
console.log(JSON.stringify(checks, null, 2));
|
|
|
|
|
|
|
|
|
|
|
|
const failed = Object.entries(checks).filter(([k, v]) => k !== 'chars' && v !== true);
|
|
|
|
|
|
if (failed.length) {
|
|
|
|
|
|
console.error('Failed checks:', failed);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('Session 1 coach prompt OK');
|
|
|
|
|
|
console.log('Session 11 coach prompt OK');
|