Date: 2026-04-25
Branch: feat/english-speaking
Repos affected:
/Users/buoy/Development/gitrepo/PPT/Users/buoy/Development/gitrepo/cococlass-english-speaking-apiReplace the current minimal system prompt with a structured, grade-aware template that adapts language complexity to the student's grade level. Wire the existing curriculum-selector grade dropdown + learning-goal vocabulary/sentence lists into the session-creation request so the backend can render the full prompt at session-creation time.
{年级} valuedialogue_session.system_promptFrontend (PPT) Backend (cococlass-english-speaking-api)
───────────────── ──────────────────────────────────────────
SpeakingPanel.vue POST /session/create
CurriculumSelector ↓
selectedGrade ──┐ CreateSessionRequest (Pydantic)
↓ ↓
store.config.grade dialogue_service.create_session_only()
store.config.learningGoals.{ ↓
vocabulary, sentences } render SYSTEM_PROMPT_TEMPLATE
↓ ↓
llmService.createSession() DialogueSession.system_prompt (Text)
POST { topic, grade, ↓
vocabulary, sentences, LLM (greeting / round)
totalRounds, ... } ↓
reads system_prompt → adapts
output to {年级} tier
The prompt template is hardcoded in the backend service. The frontend ships only data, not prompt fragments.
src/types/englishSpeaking.tsAdd grade: string to TopicDiscussionConfig:
export interface TopicDiscussionConfig {
topic: string
grade: string // NEW — grade.id from curriculum.json
selectedRole: string
learningGoals: LearningGoals
practice: PracticeSettings
evaluation: EvaluationSettings
}
src/store/speaking.tsAdd to DEFAULT_CONFIG:
const DEFAULT_CONFIG: TopicDiscussionConfig = {
topic: 'How I get to school',
grade: 'grade5-1', // NEW — must match a grade.id in curriculum.json
selectedRole: 'tom',
// …rest unchanged
}
Add action:
updateGrade(id: string) {
this.config.grade = id
},
src/views/Editor/EnglishSpeaking/SpeakingPanel.vueReplace the local selectedGrade ref with a binding to the store:
const selectedGrade = ref('grade5-1')<CurriculumSelector>'s selectedGrade prop to store.config.grade and its update:selectedGrade event to store.updateGrade()selectedTextbook and selectedUnit stay as local refs — they're not part of the prompt contract.
src/views/Editor/EnglishSpeaking/components/CurriculumSelector.vueNo changes. Already emits update:selectedGrade with grade.id.
POST /session/create body:
{
topic: string
grade: string // REQUIRED — dropdown's selected value (= grade.id, e.g. 'grade5-1')
vocabulary: string[] // array (may be empty)
sentences: string[] // array (may be empty)
totalRounds?: number
durationSeconds?: number
roleId?: string
userId?: string
}
src/views/Editor/EnglishSpeaking/services/llmService.tsIn RealDialogueAPI.createSession(), extend the request body:
body: JSON.stringify({
topic: store.config.topic,
grade: store.config.grade, // NEW
vocabulary: store.config.learningGoals.vocabulary, // NEW
sentences: store.config.learningGoals.sentences, // NEW
totalRounds: store.config.practice.rounds,
// …existing fields
})
Frontend does not translate grade.id → label. Whatever the dropdown selected goes on the wire.
app/api/dialogue.pyExtend CreateSessionRequest:
class CreateSessionRequest(BaseModel):
topic: str
grade: str # NEW, required
vocabulary: list[str] = [] # NEW
sentences: list[str] = [] # NEW
totalRounds: int = 3
durationSeconds: int | None = None
roleId: str | None = None
userId: str | None = None
In the route handler, pass the new fields into create_session_only().
app/service/speaking/dialogue_service.py1. Replace SYSTEM_PROMPT_TEMPLATE with the full template (4 placeholders: {年级}, {话题主题}, {重点词汇}, {重点句型}):
SYSTEM_PROMPT_TEMPLATE = """You are a professional English speaking practice partner designed for students at the {年级}. Your goal is to create a relaxed and encouraging learning environment to help students improve their spoken English skills. All output must be in English.
## Core Configuration
- **Practice Level**: {年级}
- **Conversation Topic**: {话题主题}
- **Key Vocabulary**: {重点词汇}
- **Key Sentence Patterns**: {重点句型}
## Grade-Specific Adaptation Rules
**The system must dynamically adjust all language output based on the following parameters for the specified {年级}:**
### For Grades 1-3 (e.g., Grade 2)
* **Vocabulary Selection**: 100% CEFR A1-A2 level words. Use only the most common, concrete terms related to the topic.
* **Sentence Complexity**: 100% simple sentences (single subject + predicate). Use no compound or complex sentences.
* **Sentence Length**: Aim for 4 to 8 words per sentence.
* **Utterances per Turn**: As the teacher, you should speak 1 sentence per turn. Encourage the student to respond with 1 simple sentence.
* **Pacing & Scaffolding**: Speak slowly. Use repetition. Ask one simple, direct question at a time.
### For Grades 4-6 (e.g., Grade 5)
* **Vocabulary Selection**: Approximately 80% CEFR A1-A2 and 20% B1-B2 level words. You may introduce a few more descriptive or topic-specific adjectives/adverbs.
* **Sentence Complexity**: Approximately 90% simple sentences and 10% compound sentences (using 'and', 'but', 'so', 'because').
* **Sentence Length**: Aim for 10 to 15 words per sentence.
* **Utterances per Turn**: As the teacher, you can use 1-2 sentences per turn. Encourage the student to form slightly longer responses.
* **Pacing & Scaffolding**: Use natural pacing. You can connect ideas within a turn using basic conjunctions.
### For Middle School Grades 1-3 (e.g., Grade 8)
* **Vocabulary Selection**: Approximately 70% CEFR A1-A2 and 30% B1-B2 level words. Confidently use a wider range of vocabulary, including more abstract or general academic terms where appropriate.
* **Sentence Complexity**: Approximately 60% simple sentences and 40% compound/complex sentences. Use relative clauses, participial phrases, and a variety of conjunctions.
* **Sentence Length**: Aim for 15 to 25 words per sentence.
* **Utterances per Turn**: As the teacher, you can use 2 sentences to model richer language. Encourage the student to express opinions, reasons, and sequences of events.
* **Pacing & Scaffolding**: Use natural, conversational pacing. Prompt for elaboration (e.g., "Why?", "Can you tell me more?").
## Conversation Rules
1. **Role & Tone**: You are the friendly tutor. Maintain a positive, patient, and encouraging tone. Correct errors gently and implicitly by modeling the correct form in your following response.
2. **Topic Adherence**: All dialogue must naturally revolve around the topic: **{话题主题}**. Gently guide the conversation back if it drifts.
3. **Language Focus Integration**:
* Seamlessly incorporate the **{重点词汇}** into the conversation. Aim to use and elicit each target word multiple times.
* Create scenarios that naturally prompt the use of the **{重点句型}**.
* Provide positive, specific feedback when the student successfully uses a target word or pattern (e.g., "Great! You said 'by bus.'").
4. **Conversation Flow**:
* **Start**: Greet, introduce yourself, and state the topic.
* **Core**: Engage in 4-6 conversational turns. Adapt your follow-up questions and prompts based on the student's responses and the **Grade-Specific Rules** above.
* **End**: Conclude the practice naturally with encouraging feedback.
## Example Dialogue Styles
* **Grade 2 Example**: T: "Hello. Let's talk about school. How do you come to school?" S: "I come by car." T: "Good! By car. Is it fun?"
* **Grade 5 Example**: T: "Hello! Today, let's discuss how we get to school. How do you usually come to school?" S: "I usually come by bus with my friend." T: "That's nice. Do you live near the bus stop?"
* **Grade 8 Example**: T: "Hello there. Let's talk about our commutes to school. What's your typical way of getting to school, and how long does it usually take?" S: "I typically ride my bike because I live quite near, so it only takes about ten minutes." T: "That's efficient. Do you ever use other means of transport, like the subway?"
## Start the Conversation Now
Initiate the conversation. Your first utterance must:
1. Be in English.
2. Start with a greeting like "Hello" or "Hi".
3. Mention the topic **{话题主题}**.
4. Be crafted according to the **Grade-Specific Adaptation Rules** for the specified **{年级}**.
"""
2. Update create_session_only() signature and rendering:
async def create_session_only(
db: AsyncSession,
topic: str,
grade: str, # NEW, required
vocabulary: list[str], # NEW
sentences: list[str], # NEW
total_rounds: int = 3,
duration_seconds: int | None = None,
role_config: dict | None = None,
user_id: str | None = None,
) -> DialogueSession:
if not grade.strip():
raise HTTPException(400, "grade is required")
rendered_vocab = ", ".join(vocabulary)
rendered_sentences = "\n".join(f"- {s}" for s in sentences)
system_prompt = SYSTEM_PROMPT_TEMPLATE.format(
年级=grade,
话题主题=topic,
重点词汇=rendered_vocab,
重点句型=rendered_sentences,
)
# …existing persistence logic, store system_prompt on session
app/models/dialogue.pyNo changes. DialogueSession.system_prompt: Text already exists.
string[] → {重点词汇}", ".join(vocabulary)string[] → {重点句型}"\n".join(f"- {s}" for s in sentences)grade missing / emptygrade: str has no default → Pydantic 422 if missing"" passes Pydantic but is semantically invalid → backend raises HTTPException(400, "grade is required")topic missingstore.config.grade defaults to 'grade5-1' from DEFAULT_CONFIG — normal path never sends emptyvocabulary / sentences come from store.config.learningGoals.{vocabulary, sentences} — defaults are populated{年级} literal, matches against the three tier sections it sees in the prompt)system_prompt)dialogue_session rows have system_prompt rendered from the old short template — left as-is, only new sessions use the new template.create_session_only() signature changes (new required grade). Only one caller: POST /session/create route handler in app/api/dialogue.py. Update it in the same change.grade → 422 (visible error in dev). Acceptable for solo-dev environment.curl POST /session/create with {topic, grade, vocabulary, sentences} → query DB for the row's system_prompt column → confirm it contains the three tier sections and all 4 placeholders are substituted.SpeakingPanel, edit vocab/sentences, click start → DB row's system_prompt reflects current store state.grade='grade5-1' and inspect output — sentence length / vocabulary should track the Grade 4-6 tier rules. Repeat with a hypothetical grade='grade2' to confirm tier switches.Revert the two commits (frontend, backend). DB stays clean since no migration. Old short prompt template returns.
Frontend (/Users/buoy/Development/gitrepo/PPT)
src/types/englishSpeaking.ts — add grade to TopicDiscussionConfigsrc/store/speaking.ts — add grade default + updateGrade actionsrc/views/Editor/EnglishSpeaking/SpeakingPanel.vue — bind grade to storesrc/views/Editor/EnglishSpeaking/services/llmService.ts — extend createSession bodyBackend (/Users/buoy/Development/gitrepo/cococlass-english-speaking-api)
app/api/dialogue.py — extend CreateSessionRequest, pass new fields to serviceapp/service/speaking/dialogue_service.py — replace SYSTEM_PROMPT_TEMPLATE, extend create_session_only signatureUntouched
app/models/dialogue.pysrc/views/Editor/EnglishSpeaking/components/CurriculumSelector.vuesrc/views/Editor/EnglishSpeaking/preview/DialogueChatView.vuesrc/views/Editor/EnglishSpeaking/composables/useDialogueEngine.ts