Keep chat pinned to the latest message while AI thinks or streams, and align buttons/inputs with the logo teal/blue focus treatment. Co-authored-by: Cursor <cursoragent@cursor.com>
192 lines
9.9 KiB
TypeScript
192 lines
9.9 KiB
TypeScript
/**
|
||
* 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'),
|
||
silentSkipPretest: prompt.includes('silent skip') || prompt.includes('silent-skip'),
|
||
omitPretestWhenSpeaking: prompt.includes('ZERO words about it'),
|
||
neverMentionPretestToUser: prompt.includes('NEVER mention «پیشآزمون»'),
|
||
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('و پیشآزمون اجراء میشود'),
|
||
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
|
||
sectionEndQuestion: prompt.includes('در مورد مطالبی که تا اینجا گفتیم، سؤالی دارید؟'),
|
||
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'),
|
||
occasionalEmoji: prompt.includes('EMOJI / STICKER-LIKE EXPRESSIONS'),
|
||
emojiHardLimit: prompt.includes('0–1 emoji per message'),
|
||
numberedListFormatting: prompt.includes('numbered list'),
|
||
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'),
|
||
justifyFriendlyParagraphs: prompt.includes('justified'),
|
||
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]'),
|
||
startCueEducationalPackage: COACH_SESSION_START_CUE.includes('introduce the overall educational package'),
|
||
startCueNoPreviousSession: COACH_SESSION_START_CUE.includes('Do NOT review a previous session'),
|
||
startCueGrounding: COACH_SESSION_START_CUE.includes('Do not invent missing details'),
|
||
startCueNoAssessment: COACH_SESSION_START_CUE.includes('Do NOT administer or invent any psychological assessment'),
|
||
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('بسته آموزشی مثبتنگر ایرانی ـ اسلامی'),
|
||
startCueLivedExamples: COACH_SESSION_START_CUE.includes('lived examples'),
|
||
startCueStayOnPath: COACH_SESSION_START_CUE.includes('Stay on the educational path'),
|
||
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');
|