feat(lesson-ai): начало фразы отчёта зависит от номера темы
Отчёт о занятии теперь начинается по-разному в зависимости от
номера темы: «начали знакомство» для первого занятия модуля (1/N),
«продолжили изучение» для промежуточного (2…N-1 из N) и
«завершили изучение» для последнего (N/N). Раньше первое
предложение всегда звучало одинаково, из-за чего первое и последнее
занятие модуля читались в отчёте для родителя одинаково.
Позицию разбирает воркер, а не модель. На локальной модели
(qwen2.5-1.5b) проверка показала, что с одной только подсказкой в
промпте «2/2» уверенно читается как «продолжали» вместо
«завершили», а в отчёте без номера темы модель выдумывала начало и
подмешивала «Scratch Jr» из примера. Поэтому номер разбирается
детерминированно и передаётся готовой строкой.
Что добавлено:
- lessonTopicPosition в worker.js — разбор «N/M» в конце свободного
текста темы: { kind, n, total, label }, kind = first | middle | last.
N = M проверяется раньше N = 1, поэтому «1/1» — это last. Мусорные
значения (N > M, M = 0, пустая тема) дают null, и строка подсказки
не добавляется. Хелпер экспортирован ради юнит-проверок
- в контекст запроса к модели добавлена строка
«Позиция темы: последнее занятие модуля (2 из 2)»; модель только
выбирает формулировку по ней
Промпт (LESSON_AI_DEFAULT_PROMPT, 2348 → 4523 симв.): новый блок
«НАЧАЛО ФРАЗЫ» с обязательными глаголами для каждого случая,
приоритет системной подсказки над «N/M», нейтральное начало без
номера, пример с тремя вариантами и запрет переносить «модуль» и
цифры в текст отчёта. Четыре копии промпта (server.js, db/init.sql,
db/migration.sql INSERT и UPDATE) синхронизированы побайтово; в
value IN миграции добавлен прежний дефолт, иначе правка не доехала
бы до существующих БД.
Проверено на живой модели: 1/2 → «начали знакомство», 2/2 →
«завершили изучение», 3/5 → «продолжили», 1/7 → «начали знакомство»,
без номера — нейтрально и без утечки из примера; e2e через
POST /api/lesson-reports с темой «2/2» дал ai_status = done.
AGENTS.md §3d: описание lesson_ai_prompt всё ещё называло его
«шаблоном делового сообщения», а список контекста воркера не
упоминал тему — исправлено, добавлены два пункта про позицию темы.
This commit is contained in:
@@ -766,6 +766,22 @@ function createEntryAutoChecker({ pool, getSetting, logAudit, aiUrl, defaultProm
|
||||
return { start, notify, getStats, getInfo };
|
||||
}
|
||||
|
||||
const LESSON_TOPIC_POSITION_LABELS = {
|
||||
first: 'первое занятие модуля',
|
||||
middle: 'промежуточное занятие модуля',
|
||||
last: 'последнее занятие модуля',
|
||||
};
|
||||
|
||||
function lessonTopicPosition(topic) {
|
||||
const m = /(\d+)\s*\/\s*(\d+)\s*$/.exec(String(topic || '').trim());
|
||||
if (!m) return null;
|
||||
const n = parseInt(m[1], 10);
|
||||
const total = parseInt(m[2], 10);
|
||||
if (!Number.isInteger(n) || !Number.isInteger(total) || total < 1 || n < 1 || n > total) return null;
|
||||
const kind = n === total ? 'last' : n === 1 ? 'first' : 'middle';
|
||||
return { kind, n, total, label: `${LESSON_TOPIC_POSITION_LABELS[kind]} (${n} из ${total})` };
|
||||
}
|
||||
|
||||
function createLessonReportChecker({ pool, getSetting, logAudit, aiUrl, model, defaultPrompt, bus, onDone }) {
|
||||
const AI_URL = aiUrl || process.env.AI_URL || 'http://text-corrector:8080';
|
||||
const MODEL = model || process.env.AI_MODEL || 'qwen2.5-1.5b-instruct-q4_k_m.gguf';
|
||||
@@ -929,11 +945,13 @@ async function finish(row, status, text, error, aiText) {
|
||||
const time = row.lesson_time ? String(row.lesson_time).slice(0, 5) : '';
|
||||
const input = original.length > MAX_INPUT_CHARS ? original.slice(0, MAX_INPUT_CHARS) : original;
|
||||
const topic = String(row.topic || '').trim();
|
||||
const position = lessonTopicPosition(topic);
|
||||
const context = [
|
||||
`Группа: ${row.group_name || '—'}`,
|
||||
`Дата занятия: ${date}`,
|
||||
time ? `Время занятия: ${time}` : '',
|
||||
topic ? `Тема занятия: ${topic}` : '',
|
||||
position ? `Позиция темы: ${position.label}` : '',
|
||||
'',
|
||||
'Текст отчёта:',
|
||||
input,
|
||||
@@ -1033,4 +1051,4 @@ async function loop() {
|
||||
return { start, stop, notify, getInfo };
|
||||
}
|
||||
|
||||
module.exports = { createEntryAutoChecker, createPhotoEnhanceWorker, createLessonReportChecker };
|
||||
module.exports = { createEntryAutoChecker, createPhotoEnhanceWorker, createLessonReportChecker, lessonTopicPosition };
|
||||
|
||||
Reference in New Issue
Block a user