|
82 | 82 |
|
83 | 83 | _PROMPTS: dict[str, dict] = { |
84 | 84 |
|
85 | | -"zh": dict( |
86 | | -system="""\ |
87 | | -你是一名顶尖大学计算机科学课程的助教,负责根据讲义和课堂录音为学生撰写高质量的中文学习笔记。 |
88 | | -
|
89 | | -写作规范: |
90 | | -1. 使用中文写作,专业技术术语保留英文并在括号内标注(如:进程 (Process))。 |
91 | | -2. **严禁使用第三人称叙述视角。** 不得出现「老师说」「教授指出」「老师要我们」「本课教授强调」等以讲师为主语的句式。 |
92 | | - 笔记聚焦于**知识本身**,直接陈述概念、原理和结论,例如: |
93 | | - - ✗「老师把问题定得很清楚:…」 → ✓「本课聚焦于…」 |
94 | | - - ✗「教授用了一个例子…」 → ✓「以下例子说明…」 |
95 | | -3. 内容以「概念→原理→示例→考试重点」逻辑展开,写成流畅的说明性段落,不要逐条罗列幻灯片。 |
96 | | -4. 数学公式使用 LaTeX:行内 $...$,单独公式 $$...$$。 |
97 | | -5. 代码示例必须是**可编译/可运行**的完整片段(包含必要的 #include、函数签名、main 等),使用正确的语法高亮(```c, ```cpp, ```python 等)。 |
98 | | - 伪代码仅在真正没有对应真实代码时使用,并标注语言为 ```pseudo。 |
99 | | -6. 考试重点用: |
100 | | - > [!IMPORTANT] |
101 | | - > 内容 |
102 | | -7. 有趣类比或助记技巧用斜体。 |
103 | | -8. 图片插入规则(严格遵守): |
104 | | - - **插入且仅插入含视觉元素的图片**:图表、流程图、架构图、代码截图、数学推导、数据可视化等。如果是与课程无关的行政或其他元素(如课程信息、投票二维码、签到提示等),即使是图片也不要插入。 |
105 | | - - 纯文字要点、定义、标题幻灯片不需要插入——笔记本身已用文字表达更好。 |
106 | | - - **每张图片必须内联放置,紧跟解释该图片概念的段落之后。** 绝对不要将多张图片堆叠在一起。每张图片的上方和下方都应有解释性文字。如果一个片段有 5 张相关图片,它们应分散在文本的 5 个不同位置,各自紧邻相关解释。 |
107 | | - - 幻灯片格式:` *(一句话描述)*` |
108 | | - - 屏幕录制帧格式:` *(一句话描述)*` |
109 | | - (LXX 由调用方提供,禁止自行修改)。 |
110 | | -9. 绝对禁止捏造原始材料中不存在的技术细节。 |
111 | | -""", |
112 | | -chunk="""\ |
113 | | -请为以下课程片段({course_name} Lecture {lec_num}: {lec_title})撰写学习笔记。 |
114 | | -
|
115 | | -## 本片段幻灯片列表 |
116 | | -{slide_outline} |
117 | | -
|
118 | | -## 教授录音逐字稿(按幻灯片顺序) |
119 | | -{transcript_block} |
120 | | -
|
121 | | -## 可用图片(含图表/代码截图的幻灯片) |
122 | | -{image_hints} |
123 | | -
|
124 | | ---- |
125 | | -
|
126 | | -要求: |
127 | | -- 本片段对应笔记的二级标题为 `### {lec_num}.{chunk_idx} {chunk_title}`(**不要输出此行**,由调用方添加) |
128 | | -- 详细度:{detail}/10。{detail_instruction} |
129 | | -- 图片插入:**插入所有含视觉元素的图片**(图表、流程图、架构图、代码截图、数学推导、数据可视化等),跳过纯文字截图和与课程无关的行政元素。 |
130 | | - 路径必须完全照抄上方「可用图片」列表中给出的路径(含 images/L** 子目录),禁止自造路径。 |
131 | | - **关键:每张图片必须内联放在解释该概念的段落旁边,绝对不要将多张图片堆在一起。每张图片的上下都应有解释性文字。** |
132 | | - 格式:` *(说明)*` 或 ` *(说明)*` |
133 | | -- 代码示例写完整可编译片段(含必要 include/imports),用正确的语言标签(```c, ```cpp, ```python)。 |
134 | | -- 只写本片段内容,不要引入其他讲座的内容 |
135 | | -""", |
136 | | -slide_only="""\ |
137 | | -请根据以下幻灯片内容,撰写 {course_name} Lecture {lec_num}: {lec_title} 的学习笔记。 |
138 | | -(该讲座没有录音,请结合你的 CS 知识展开解释。) |
139 | | -
|
140 | | -## 幻灯片内容 |
141 | | -{slide_outline} |
142 | | -
|
143 | | -## 可用图片 |
144 | | -{image_hints} |
145 | | -
|
146 | | ---- |
147 | | -
|
148 | | -要求: |
149 | | -- 本片段对应笔记的二级标题为 `### {lec_num}.{chunk_idx} {chunk_title}`(**不要输出此行**) |
150 | | -- 详细度:{detail}/10。{detail_instruction} |
151 | | -- 图片插入:**插入所有含视觉元素的图片**(图表、流程图、架构图、代码截图、数学推导、数据可视化等),跳过纯文字截图和与课程无关的行政元素。 |
152 | | - 路径必须完全照抄上方「可用图片」列表中给出的路径(含 images/L** 子目录),禁止自造路径。 |
153 | | - **关键:每张图片必须内联放在解释该概念的段落旁边,绝对不要将多张图片堆在一起。每张图片的上下都应有解释性文字。** |
154 | | - 格式:` *(说明)*` 或 ` *(说明)*` |
155 | | -- 代码示例写完整可编译片段,用正确的语言标签(```c, ```cpp, ```python)。 |
156 | | -""", |
157 | | -verify="""\ |
158 | | -请检查以下笔记片段中的技术术语是否与幻灯片一致,以及是否存在明显的事实错误。 |
159 | | -
|
160 | | -**参考术语表(来自幻灯片):** |
161 | | -{term_list} |
162 | | -
|
163 | | -**笔记片段:** |
164 | | -{draft} |
165 | | -
|
166 | | -如果没有问题,直接回复 APPROVED(仅此一词)。 |
167 | | -如果有术语错误或事实错误,返回修正后的完整笔记片段(不加任何说明)。 |
168 | | -""", |
169 | | -exam="""\ |
170 | | -以下是 {course_name} 的全部讲座笔记摘要。请在最后汇总一个考试速记章节。 |
171 | | -
|
172 | | -格式要求: |
173 | | -- 标题:`## Exam Notes` |
174 | | -- 每条格式:`N. **考点名**:一句话说明` |
175 | | -- 不超过 30 条,覆盖各讲座核心考点、公式、算法步骤、常见混淆点 |
176 | | -
|
177 | | -笔记摘要: |
178 | | -{summary} |
179 | | -""", |
180 | | -no_transcript="(本片段无录音逐字稿)", |
181 | | -detail_instructions=[ |
182 | | - (range(0, 3), "极简要点:每个概念仅一行,不展开,每张幻灯片最多3条。"), |
183 | | - (range(3, 6), "有层次的要点结构:每个主要概念一条一级bullet(`-`)," |
184 | | - "其下最多2条二级bullet(` -`)补充关键细节。" |
185 | | - "每张幻灯片合计不超过5条,禁止写连续段落。"), |
186 | | - (range(6, 9), "详细段落:概念、原理、教授示例与类比全部包含。"), |
187 | | - (range(9, 11), "最高详细度:包含所有细节、边界情况、与其他章节的联系及考点标注。"), |
188 | | -], |
189 | | -), # end zh |
190 | | - |
191 | 85 | "en": dict( |
192 | 86 | system="""\ |
193 | 87 | You are a teaching assistant at a top university, writing high-quality study notes for computer science courses based on lecture slides and audio transcripts. |
|
207 | 101 | 8. Image insertion rules (strictly follow): |
208 | 102 | - **Insert all and only images that contain visual elements**: diagrams, flowcharts, architecture drawings, code screenshots, mathematical derivations, data visualizations, tables with meaningful structure, annotated figures, or any non-trivial visual illustration. Do NOT insert administrative or non-course elements (course info slides, polling QR codes, attendance prompts, etc.) even if they contain images. |
209 | 103 | - Pure text slides (bullet points, definitions, titles) do not need images — the notes express text better than a screenshot. |
210 | | - - **Each image MUST be placed inline, immediately after the paragraph that discusses the concept it illustrates.** NEVER group multiple images together in a cluster. Each image should have explanatory text both above and below it. If there are 5 relevant images in a segment, they should be spread across 5 different locations in the text, each adjacent to the related explanation. |
| 104 | + - **Each image MUST be placed inline, immediately after the paragraph that directly discusses the concept shown in that image.** Only insert an image when the paragraph above it is actually explaining the same concept the image depicts. If an image does not match any specific paragraph, skip it or move it to a more appropriate location. NEVER group multiple images together. |
211 | 105 | - Format for slide images: ` *(one-sentence description)*` |
212 | 106 | - Format for screen-capture frames: ` *(one-sentence description)*` |
213 | 107 | (LXX is provided by the caller — do not modify it; the caption must be in parentheses wrapped in asterisks exactly as shown). |
|
232 | 126 | - Detail level: {detail}/10. {detail_instruction} |
233 | 127 | - Images: **insert all images that contain visual elements** (diagrams, charts, graphs, code screenshots, architecture drawings, data visualizations, mathematical derivations, etc.). Skip images of pure text, bullet points, or administrative/non-course elements. |
234 | 128 | Copy the exact path from the "Available images" list above (including the images/L** subdirectory). Do not invent paths. |
235 | | - **CRITICAL: place each image inline, immediately after the paragraph that explains the concept it illustrates — NEVER cluster multiple images together. Each image must have explanatory text both above and below it.** |
| 129 | + **CRITICAL: only insert an image directly after a paragraph that discusses the SAME concept the image depicts. The caption must describe what the image shows and connect to the paragraph above. If an image doesn't match any paragraph, skip it. NEVER cluster multiple images together.** |
236 | 130 | Format: ` *(caption)*` or ` *(caption)*` |
237 | 131 | - Code examples must be complete and compilable (with necessary includes/imports), using the correct language tag (```c, ```cpp, ```python). |
238 | 132 | - Only cover the content in this segment; do not introduce material from other lectures. |
|
254 | 148 | - Detail level: {detail}/10. {detail_instruction} |
255 | 149 | - Images: **insert all images that contain visual elements** (diagrams, charts, graphs, code screenshots, architecture drawings, data visualizations, mathematical derivations, etc.). Skip images of pure text, bullet points, or administrative/non-course elements. |
256 | 150 | Copy the exact path from the "Available images" list above (including the images/L** subdirectory). Do not invent paths. |
257 | | - **CRITICAL: place each image inline, immediately after the paragraph that explains the concept it illustrates — NEVER cluster multiple images together. Each image must have explanatory text both above and below it.** |
| 151 | + **CRITICAL: only insert an image directly after a paragraph that discusses the SAME concept the image depicts. If an image doesn't match any paragraph, skip it. NEVER cluster multiple images together.** |
258 | 152 | Format: ` *(caption)*` or ` *(caption)*` |
259 | 153 | - Code examples must be complete and compilable, using the correct language tag (```c, ```cpp, ```python). |
260 | 154 | """, |
|
295 | 189 | } # end _PROMPTS |
296 | 190 |
|
297 | 191 |
|
| 192 | +# Language names for the translation instruction |
| 193 | +_LANG_NAMES = {"en": "English", "zh": "Chinese", "ja": "Japanese", "ko": "Korean"} |
| 194 | + |
| 195 | + |
298 | 196 | def _P(key: str) -> str: |
299 | | - """Return the prompt string for the current NOTE_LANGUAGE, falling back to English.""" |
300 | | - return _PROMPTS.get(NOTE_LANGUAGE, _PROMPTS["en"])[key] |
| 197 | + """Return the English prompt, with a translation instruction appended when |
| 198 | + NOTE_LANGUAGE is not English. All intermediate processing (system prompt, |
| 199 | + chunk prompt, verification) stays in English for best quality; only the |
| 200 | + final note output is translated word-by-word.""" |
| 201 | + return _PROMPTS["en"][key] |
301 | 202 |
|
302 | 203 |
|
303 | 204 | def _detail_instr(level: int) -> str: |
304 | | - instrs = _PROMPTS.get(NOTE_LANGUAGE, _PROMPTS["en"])["detail_instructions"] |
| 205 | + instrs = _PROMPTS["en"]["detail_instructions"] |
305 | 206 | for rng, txt in instrs: |
306 | 207 | if level in rng: |
307 | 208 | return txt |
@@ -580,6 +481,39 @@ def _get_client_for(model: str): |
580 | 481 | return _client_cache[p] |
581 | 482 |
|
582 | 483 |
|
| 484 | +def _translate(text: str, lang: str) -> str: |
| 485 | + """Translate note text to the target language, preserving all Markdown |
| 486 | + formatting, image references, LaTeX formulas, and code blocks verbatim. |
| 487 | + Only prose text is translated; technical terms keep English with |
| 488 | + translation in parentheses on first use.""" |
| 489 | + system = ( |
| 490 | + f"You are a professional translator. Translate English study notes " |
| 491 | + f"into {lang}. You MUST translate every English sentence into {lang}. " |
| 492 | + f"Do NOT leave any prose in English. The output must read naturally " |
| 493 | + f"as {lang} text, not as English with annotations." |
| 494 | + ) |
| 495 | + prompt = ( |
| 496 | + f"Translate the following study notes entirely into {lang}.\n\n" |
| 497 | + f"Rules:\n" |
| 498 | + f"1. Every English sentence must become a {lang} sentence. Do NOT " |
| 499 | + f"leave English prose — the reader should be able to read the entire " |
| 500 | + f"note in {lang} without knowing English.\n" |
| 501 | + f"2. Technical terms: write the {lang} term first, then the English " |
| 502 | + f"in parentheses on first use. Example for Chinese: 子网掩码 (subnet mask).\n" |
| 503 | + f"3. Keep EXACTLY as-is without any modification:\n" |
| 504 | + f" - Image lines:  *(caption)* — translate ONLY " |
| 505 | + f"the caption text inside *(...)*, keep the path unchanged\n" |
| 506 | + f" - LaTeX: $...$ and $$...$$\n" |
| 507 | + f" - Code blocks: ```...```\n" |
| 508 | + f" - Callout markers: > [!IMPORTANT]\n" |
| 509 | + f" - Markdown formatting: ###, **, *, ---, etc.\n" |
| 510 | + f"4. Do NOT shorten, summarize, or omit any content.\n" |
| 511 | + f"5. Output ONLY the translated text.\n\n" |
| 512 | + f"---\n\n{text}" |
| 513 | + ) |
| 514 | + return _call(NOTE_MODEL, system, prompt, len(text) * 3) |
| 515 | + |
| 516 | + |
583 | 517 | def _call(model: str, system: str, user: str, max_tokens: int) -> str: |
584 | 518 | """Call any supported LLM (OpenAI, Gemini, Anthropic) with a text prompt.""" |
585 | 519 | client = _get_client_for(model) |
@@ -895,6 +829,14 @@ def generate_section( |
895 | 829 | else: |
896 | 830 | tqdm.write(f" [warn] Verifier suspicious response, keeping draft") |
897 | 831 |
|
| 832 | + # Translate to target language if not English |
| 833 | + if NOTE_LANGUAGE != "en" and draft: |
| 834 | + lang = _LANG_NAMES.get(NOTE_LANGUAGE, NOTE_LANGUAGE) |
| 835 | + tqdm.write(f" translating to {lang}…") |
| 836 | + _tt = _time.monotonic() |
| 837 | + draft = _translate(draft, lang) |
| 838 | + tqdm.write(f" ✓ translated ({_time.monotonic()-_tt:.0f}s)") |
| 839 | + |
898 | 840 | heading = f"### {lec_num}.{ci} {chunk_title}" |
899 | 841 | content = f"{heading}\n\n{draft}" |
900 | 842 | sec_file.write_text(content, encoding="utf-8") |
|
0 commit comments