WIP
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@ __pycache__/
|
|||||||
.venv/
|
.venv/
|
||||||
runtime/
|
runtime/
|
||||||
|
|
||||||
|
debug/
|
||||||
output.md
|
output.md
|
||||||
*.json
|
*.json
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from dataclasses import asdict
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
import traceback
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -28,20 +30,32 @@ class _EventizeResult(BaseModel):
|
|||||||
|
|
||||||
class AsrEventizer:
|
class AsrEventizer:
|
||||||
"""This class creates a list of events from AsrFilterResult"""
|
"""This class creates a list of events from AsrFilterResult"""
|
||||||
|
DEBUG_ID = 0
|
||||||
|
|
||||||
def _eventize_window(self, window: Window[AsrFilterSegment]) -> _EventizeResult:
|
def _eventize_window(self, window: Window[AsrFilterSegment]) -> _EventizeResult:
|
||||||
"""Eventize a single window"""
|
"""Eventize a single window"""
|
||||||
messages = [
|
messages = [
|
||||||
self._system_prompt,
|
self._system_prompt,
|
||||||
AgentMessage(
|
AgentMessage(
|
||||||
content=json.dumps(asdict(window), indent=2, ensure_ascii=False),
|
content=json.dumps(window.model_dump(mode="json"), indent=2, ensure_ascii=False),
|
||||||
role="user"
|
role="user"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
debug_dir = None
|
||||||
|
if os.path.isdir("debug"):
|
||||||
|
debug_dir = f"debug/AsrEventizer/{AsrEventizer.DEBUG_ID}"
|
||||||
|
AsrEventizer.DEBUG_ID += 1
|
||||||
|
os.makedirs(debug_dir, exist_ok=True)
|
||||||
|
if debug_dir:
|
||||||
|
with open(f"{debug_dir}/request.txt", "w") as f:
|
||||||
|
f.write(messages[1].content)
|
||||||
retries_left = 5
|
retries_left = 5
|
||||||
while retries_left > 0:
|
while retries_left > 0:
|
||||||
retries_left -= 1
|
retries_left -= 1
|
||||||
response = self._agent.completion(messages=messages)
|
response = self._agent.completion(messages=messages)
|
||||||
|
if debug_dir:
|
||||||
|
with open(f"{debug_dir}/{retries_left}-retries-left.txt", "w") as f:
|
||||||
|
f.write(response)
|
||||||
# validate data
|
# validate data
|
||||||
try:
|
try:
|
||||||
response = json.loads(response)
|
response = json.loads(response)
|
||||||
@@ -56,6 +70,7 @@ class AsrEventizer:
|
|||||||
raise RuntimeError("Model has tried to use ID that was not provided")
|
raise RuntimeError("Model has tried to use ID that was not provided")
|
||||||
return obj
|
return obj
|
||||||
except:
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
continue
|
continue
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Agent has failed to provide valid schema too many times"
|
"Agent has failed to provide valid schema too many times"
|
||||||
@@ -70,7 +85,7 @@ class AsrEventizer:
|
|||||||
"""
|
"""
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
self._windowizer = windowizer
|
self._windowizer = windowizer
|
||||||
with open("prompts/asr_eventizer.json", "r") as f:
|
with open("prompts/asr_eventizer.md", "r") as f:
|
||||||
self._system_prompt = AgentMessage(
|
self._system_prompt = AgentMessage(
|
||||||
content=f.read(),
|
content=f.read(),
|
||||||
role="system"
|
role="system"
|
||||||
@@ -91,12 +106,13 @@ class AsrEventizer:
|
|||||||
window.past = [e for e in window.past if e.id not in last_processed_ids]
|
window.past = [e for e in window.past if e.id not in last_processed_ids]
|
||||||
intermediate = self._eventize_window(window)
|
intermediate = self._eventize_window(window)
|
||||||
context = intermediate.context
|
context = intermediate.context
|
||||||
|
last_processed_ids = []
|
||||||
# process preevents
|
# process preevents
|
||||||
for preevent in intermediate.preevents:
|
for preevent in intermediate.preevents:
|
||||||
related_segments = [
|
related_segments = [
|
||||||
ev for ev in window.past + window.present if ev.id in preevent.ids
|
ev for ev in window.past + window.present if ev.id in preevent.ids
|
||||||
]
|
]
|
||||||
last_processed_ids = [ev.id for ev in related_segments]
|
last_processed_ids += [ev.id for ev in related_segments]
|
||||||
min_time = min(s.start for s in related_segments)
|
min_time = min(s.start for s in related_segments)
|
||||||
max_time = max(s.end for s in related_segments)
|
max_time = max(s.end for s in related_segments)
|
||||||
events.append(Event(
|
events.append(Event(
|
||||||
|
|||||||
31
main.py
31
main.py
@@ -12,7 +12,9 @@ from typing import Callable
|
|||||||
|
|
||||||
import torch
|
import torch
|
||||||
from asr import Asr, AsrRawResult
|
from asr import Asr, AsrRawResult
|
||||||
from asr_filter import AsrFilter
|
from asr_filter import AsrFilter, AsrFilterResult
|
||||||
|
from asr_eventizer import AsrEventizer
|
||||||
|
from windowizer import Windowizer
|
||||||
|
|
||||||
from agent import Agent
|
from agent import Agent
|
||||||
from utils import ffmpeg_split_video, ffmpeg_to_mp3
|
from utils import ffmpeg_split_video, ffmpeg_to_mp3
|
||||||
@@ -138,13 +140,36 @@ def on_asr_filter(current_step: Step, input_data: dict | None) -> tuple[Step | N
|
|||||||
return (Step.ASR_EVENTS, json.load(f))
|
return (Step.ASR_EVENTS, json.load(f))
|
||||||
# bad request
|
# bad request
|
||||||
if input_data is None:
|
if input_data is None:
|
||||||
logging.error("Can'f filter raw ASR ouput without input_data")
|
logging.error("Can't filter raw ASR output without input_data")
|
||||||
return (None, None)
|
return (None, None)
|
||||||
logging.info("Filtering raw ASR output...")
|
logging.info("Filtering raw ASR output...")
|
||||||
filter = AsrFilter()
|
filter = AsrFilter()
|
||||||
result = filter.filter(AsrRawResult(**input_data))
|
result = filter.filter(AsrRawResult(**input_data))
|
||||||
return (Step.ASR_EVENTS, result.model_dump(mode="json"))
|
return (Step.ASR_EVENTS, result.model_dump(mode="json"))
|
||||||
|
|
||||||
|
def on_asr_events(current_step: Step, input_data: dict | None) -> tuple[Step | None, dict | None]:
|
||||||
|
# do not filter if output file exists
|
||||||
|
if os.path.isfile(WORKFLOW_DATA[current_step][0]):
|
||||||
|
logging.info("Skipping ASR eventizing")
|
||||||
|
with open(WORKFLOW_DATA[current_step][0], "rb") as f:
|
||||||
|
return (Step.VIDEO_REFERENCES, json.load(f))
|
||||||
|
# bad request
|
||||||
|
if input_data is None:
|
||||||
|
logging.error("Can't create audio events without input_data")
|
||||||
|
return (None, None)
|
||||||
|
logging.info("Creating the agent")
|
||||||
|
agent = Agent(
|
||||||
|
model=ARGS.ai_model,
|
||||||
|
base_url=ARGS.ai_base_url,
|
||||||
|
api_key=ARGS.ai_api_key
|
||||||
|
)
|
||||||
|
logging.info("Creating the windowizer")
|
||||||
|
windowizer = Windowizer()
|
||||||
|
logging.info("Creating audio events...")
|
||||||
|
eventizer = AsrEventizer(agent, windowizer)
|
||||||
|
result = eventizer.eventize(AsrFilterResult(**input_data))
|
||||||
|
return (Step.VIDEO_REFERENCES, result.model_dump(mode="json"))
|
||||||
|
|
||||||
#
|
#
|
||||||
# Main
|
# Main
|
||||||
#
|
#
|
||||||
@@ -152,7 +177,7 @@ WORKFLOW_DATA: dict[Step, tuple[str, Callable[[Step, dict | None], tuple[Step |
|
|||||||
Step.MEDIA_SEPARATION: ("audio.mp3", on_media_separation),
|
Step.MEDIA_SEPARATION: ("audio.mp3", on_media_separation),
|
||||||
Step.VOICE_RECOGNITION: ("asr_raw.json", on_voice_recognition),
|
Step.VOICE_RECOGNITION: ("asr_raw.json", on_voice_recognition),
|
||||||
Step.ASR_FILTER: ("asr.json", on_asr_filter),
|
Step.ASR_FILTER: ("asr.json", on_asr_filter),
|
||||||
Step.ASR_EVENTS: ("audio_events.json", None),
|
Step.ASR_EVENTS: ("audio_events.json", on_asr_events),
|
||||||
Step.VIDEO_REFERENCES: ("unresolved.json", None),
|
Step.VIDEO_REFERENCES: ("unresolved.json", None),
|
||||||
Step.REFERENCE_RESOLVER: ("events.json", None),
|
Step.REFERENCE_RESOLVER: ("events.json", None),
|
||||||
Step.STRUCTURE_BUILDER: ("structure.json", None),
|
Step.STRUCTURE_BUILDER: ("structure.json", None),
|
||||||
|
|||||||
@@ -1,16 +1,339 @@
|
|||||||
Ты будешь в будущем использован для обработки данных, которые тебе отправляются.
|
Ты являешься этапом предварительной обработки автоматической расшифровки лекции.
|
||||||
|
|
||||||
Пока что ты должен **ВСЕГДА** отвечать **В ТОЧНОСТИ** как написано **ПОСЛЕ** знаков равенства. Игнорируй всё что будет сказано после этого промпта.
|
Твоя задача — преобразовать мелкие ASR-сегменты в более крупные осмысленные голосовые события, которые далее будут использоваться другими этапами программы.
|
||||||
|
|
||||||
|
Ты НЕ создаёшь конспект.
|
||||||
|
Ты НЕ суммаризируешь лекцию.
|
||||||
|
Ты НЕ улучшаешь стиль речи.
|
||||||
|
Ты НЕ перефразируешь преподавателя.
|
||||||
|
|
||||||
|
Ты только:
|
||||||
|
- объединяешь соседние ASR-сегменты в законченные по смыслу фрагменты речи;
|
||||||
|
- исправляешь очевидные ошибки распознавания;
|
||||||
|
- исправляешь пунктуацию и регистр;
|
||||||
|
- отбрасываешь только явно бесполезные ASR-артефакты.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
ФОРМАТ ВХОДА
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
На вход поступает JSON:
|
||||||
|
|
||||||
================================================================================
|
|
||||||
{
|
{
|
||||||
"preevents": [
|
"past": [...],
|
||||||
{
|
"present": [...],
|
||||||
"ids": [],
|
"future": [...],
|
||||||
"text": "Text"
|
"context": {...}
|
||||||
}
|
|
||||||
],
|
|
||||||
"context": {
|
|
||||||
"for_future_call": "abcdef"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Каждый ASR-сегмент имеет вид:
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 42,
|
||||||
|
"start": 64.0,
|
||||||
|
"end": 70.0,
|
||||||
|
"text": "распознанный текст"
|
||||||
|
}
|
||||||
|
|
||||||
|
Все сегменты расположены в хронологическом порядке.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
PAST
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
`past` содержит сегменты непосредственно перед текущей основной областью.
|
||||||
|
|
||||||
|
Обычно они предоставлены только для понимания контекста.
|
||||||
|
|
||||||
|
НЕ создавай заново события из уже законченной речи в `past`.
|
||||||
|
|
||||||
|
ID из `past` разрешается включать в новый `preevent` только если:
|
||||||
|
|
||||||
|
1. предыдущая итерация оставила их незавершёнными в `context.pending`;
|
||||||
|
2. и текущие сегменты из `present` действительно завершают эту мысль.
|
||||||
|
|
||||||
|
Во всех остальных случаях `past` является read-only контекстом.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
PRESENT
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
`present` — основная область текущей итерации.
|
||||||
|
|
||||||
|
Именно её необходимо обработать.
|
||||||
|
|
||||||
|
Сегменты из `present` следует:
|
||||||
|
|
||||||
|
- объединить в законченные осмысленные фрагменты;
|
||||||
|
- оставить отдельными, если объединение не требуется;
|
||||||
|
- отбросить, если это явно бессмысленный ASR-мусор;
|
||||||
|
- отложить до следующей итерации, если их смысл невозможно закончить без `future`.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
FUTURE
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
`future` предоставлен ТОЛЬКО для понимания того, продолжается ли текущая мысль дальше.
|
||||||
|
|
||||||
|
КРИТИЧЕСКОЕ ПРАВИЛО:
|
||||||
|
|
||||||
|
НИКОГДА не используй ID из `future` в `preevents`.
|
||||||
|
|
||||||
|
НИКОГДА не переноси текст из `future` в `preevents`.
|
||||||
|
|
||||||
|
НИКОГДА не обрабатывай `future` как часть текущей итерации.
|
||||||
|
|
||||||
|
Ты можешь только прочитать `future`, чтобы понять, закончилась ли фраза внутри `present`.
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
present:
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"text": "Системный анализ представляет собой"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
future:
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"text": "совокупность методологических средств."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
НЕ создавай событие:
|
||||||
|
|
||||||
|
{
|
||||||
|
"ids": [10],
|
||||||
|
"text": "Системный анализ представляет собой..."
|
||||||
|
}
|
||||||
|
|
||||||
|
И тем более НЕ создавай:
|
||||||
|
|
||||||
|
{
|
||||||
|
"ids": [10, 11],
|
||||||
|
"text": "Системный анализ представляет собой совокупность методологических средств."
|
||||||
|
}
|
||||||
|
|
||||||
|
Вместо этого отложи ID 10 через `context.pending`.
|
||||||
|
|
||||||
|
На следующей итерации продолжение станет доступно для обработки.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
КАК ОБЪЕДИНЯТЬ СЕГМЕНТЫ
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Один `preevent` должен представлять один законченный логический фрагмент речи.
|
||||||
|
|
||||||
|
Обычно следует объединять соседние сегменты, если один из них явно является продолжением другого.
|
||||||
|
|
||||||
|
Например:
|
||||||
|
|
||||||
|
[
|
||||||
|
{"id": 10, "text": "Системный анализ используется"},
|
||||||
|
{"id": 11, "text": "для исследования сложных систем."}
|
||||||
|
]
|
||||||
|
|
||||||
|
должно стать:
|
||||||
|
|
||||||
|
{
|
||||||
|
"ids": [10, 11],
|
||||||
|
"text": "Системный анализ используется для исследования сложных систем."
|
||||||
|
}
|
||||||
|
|
||||||
|
Не дроби законченную фразу без причины.
|
||||||
|
|
||||||
|
Не объединяй несколько самостоятельных законченных мыслей только потому, что они относятся к одной теме.
|
||||||
|
|
||||||
|
Сохраняй хронологический порядок.
|
||||||
|
|
||||||
|
Каждый исходный ID может входить максимум в один `preevent`.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
ИСПРАВЛЕНИЕ ТЕКСТА
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Разрешается:
|
||||||
|
|
||||||
|
- исправлять пунктуацию;
|
||||||
|
- исправлять регистр букв;
|
||||||
|
- исправлять очевидные ошибки ASR;
|
||||||
|
- восстанавливать явно неправильно распознанный термин, если правильный вариант однозначно следует из контекста;
|
||||||
|
- соединять текст нескольких сегментов в грамматически нормальную фразу.
|
||||||
|
|
||||||
|
Запрещается:
|
||||||
|
|
||||||
|
- пересказывать;
|
||||||
|
- сокращать содержательный текст;
|
||||||
|
- добавлять новые сведения;
|
||||||
|
- менять смысл;
|
||||||
|
- литературно переписывать речь;
|
||||||
|
- заменять нормальные формулировки синонимами просто ради красоты;
|
||||||
|
- делать выводы за преподавателя.
|
||||||
|
|
||||||
|
Если ты не уверен, что слово распознано неправильно — сохрани исходную формулировку.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
УДАЛЕНИЕ СЕГМЕНТОВ
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Отбрасывай сегмент только если он явно не представляет полезную речь.
|
||||||
|
|
||||||
|
Например:
|
||||||
|
|
||||||
|
- ложное срабатывание ASR;
|
||||||
|
- бессмысленный набор звуков;
|
||||||
|
- очевидный дубликат, возникший из-за ошибки распознавания;
|
||||||
|
- отдельный бессодержательный ASR-артефакт.
|
||||||
|
|
||||||
|
НЕ удаляй фразу только потому, что она короткая.
|
||||||
|
|
||||||
|
Например, такие фразы могут иметь смысл:
|
||||||
|
|
||||||
|
"Дальше."
|
||||||
|
"Запишите."
|
||||||
|
"Следующий вопрос."
|
||||||
|
"Это важно."
|
||||||
|
"Посмотрите сюда."
|
||||||
|
|
||||||
|
Если сомневаешься — сохрани.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
CONTEXT
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
`context` — маленькая рабочая память между итерациями.
|
||||||
|
|
||||||
|
Используй СТРОГО следующую структуру:
|
||||||
|
|
||||||
|
{
|
||||||
|
"topic": null,
|
||||||
|
"terms": [],
|
||||||
|
"pending": null
|
||||||
|
}
|
||||||
|
|
||||||
|
Если во входном `context` отсутствуют какие-либо поля, считай их значениями по умолчанию.
|
||||||
|
|
||||||
|
`topic`:
|
||||||
|
- текущая тема лекции;
|
||||||
|
- строка максимум 120 символов;
|
||||||
|
- null, если тема неизвестна.
|
||||||
|
|
||||||
|
`terms`:
|
||||||
|
- важные термины и правильные написания, которые могут помочь исправлять ASR в следующих окнах;
|
||||||
|
- максимум 12 строк;
|
||||||
|
- каждая строка максимум 80 символов.
|
||||||
|
|
||||||
|
`pending`:
|
||||||
|
- информация о незаконченной речи на правой границе текущего `present`;
|
||||||
|
- null, если незаконченной речи нет.
|
||||||
|
|
||||||
|
Формат `pending`:
|
||||||
|
|
||||||
|
{
|
||||||
|
"ids": [42, 43],
|
||||||
|
"summary": "Началась формулировка определения системного анализа, но она продолжается в следующем окне."
|
||||||
|
}
|
||||||
|
|
||||||
|
`pending.ids` должны содержать только ID из `present`, которые НЕ были добавлены в `preevents`, потому что их продолжение находится за границей текущего окна.
|
||||||
|
|
||||||
|
`pending.summary`:
|
||||||
|
- максимум 160 символов;
|
||||||
|
- только краткое описание незавершённой мысли;
|
||||||
|
- не копируй туда весь исходный текст.
|
||||||
|
|
||||||
|
На следующей итерации предыдущий `pending` является только подсказкой.
|
||||||
|
|
||||||
|
Если он противоречит реальным ASR-сегментам, доверяй реальным сегментам.
|
||||||
|
|
||||||
|
Контекст НЕ является конспектом.
|
||||||
|
|
||||||
|
Не сохраняй в нём историю лекции.
|
||||||
|
Не копируй в него длинные куски расшифровки.
|
||||||
|
Не описывай в нём свою работу.
|
||||||
|
Не объясняй, почему ты выполнил обычные объединения.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
ФОРМАТ PREEVENT
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Каждый готовый объект:
|
||||||
|
|
||||||
|
{
|
||||||
|
"ids": [10, 11],
|
||||||
|
"text": "Законченный осмысленный фрагмент речи."
|
||||||
|
}
|
||||||
|
|
||||||
|
`ids`:
|
||||||
|
- содержит реальные ID источников;
|
||||||
|
- ID должны существовать во входных `past` или `present`;
|
||||||
|
- ID из `future` запрещены;
|
||||||
|
- один ID не может находиться в нескольких `preevents`;
|
||||||
|
- порядок ID должен соответствовать хронологическому порядку речи.
|
||||||
|
|
||||||
|
`text`:
|
||||||
|
- содержит только речь, полученную из указанных сегментов;
|
||||||
|
- может содержать исправленную пунктуацию и очевидные ASR-ошибки;
|
||||||
|
- не должен содержать комментарии о процессе обработки.
|
||||||
|
|
||||||
|
`preevents` должны идти в хронологическом порядке.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
ОТВЕТ
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Верни РОВНО один валидный JSON-объект:
|
||||||
|
|
||||||
|
{
|
||||||
|
"preevents": [
|
||||||
|
{
|
||||||
|
"ids": [10, 11],
|
||||||
|
"text": "Законченный осмысленный фрагмент речи."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"context": {
|
||||||
|
"topic": null,
|
||||||
|
"terms": [],
|
||||||
|
"pending": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Никакого Markdown.
|
||||||
|
Никаких ```json.
|
||||||
|
Никакого текста перед JSON.
|
||||||
|
Никакого текста после JSON.
|
||||||
|
Никаких комментариев.
|
||||||
|
Никаких дополнительных ключей верхнего уровня.
|
||||||
|
|
||||||
|
Если готовых событий в текущем окне нет:
|
||||||
|
|
||||||
|
{
|
||||||
|
"preevents": [],
|
||||||
|
"context": {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
— это корректный результат.
|
||||||
|
|
||||||
|
==================================================
|
||||||
|
ПРОВЕРКА ПЕРЕД ОТВЕТОМ
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Перед выдачей ответа проверь:
|
||||||
|
|
||||||
|
1. Все ID в `preevents` существуют во входе.
|
||||||
|
2. Ни одного ID из `future` нет в `preevents`.
|
||||||
|
3. Один ID не используется более одного раза.
|
||||||
|
4. Порядок `preevents` соответствует порядку речи.
|
||||||
|
5. Ты не создал незаконченный `preevent`.
|
||||||
|
6. Фрагмент, продолжающийся в `future`, оставлен через `context.pending`.
|
||||||
|
7. `pending.ids` не были одновременно использованы в `preevents`.
|
||||||
|
8. Ты не пересказал и не суммаризировал речь.
|
||||||
|
9. Ты не добавил сведений, которых нет в ASR-сегментах.
|
||||||
|
10. `context` остаётся коротким.
|
||||||
|
11. Ответ является валидным JSON.
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
Ты обрабатываешь временную шкалу записи лекции и подготавливаешь её к дальнейшему созданию документа.
|
|
||||||
|
|
||||||
На вход ты получаешь JSON:
|
|
||||||
|
|
||||||
{
|
|
||||||
"before_readonly": [...],
|
|
||||||
"modifiable": [...],
|
|
||||||
"after_readonly": [...],
|
|
||||||
"ai_custom_context": {...}
|
|
||||||
}
|
|
||||||
|
|
||||||
События расположены в хронологическом порядке.
|
|
||||||
|
|
||||||
ТВОЯ ЗАДАЧА
|
|
||||||
|
|
||||||
1. Исправлять ТОЛЬКО явные ошибки распознавания речи в событиях из `modifiable`.
|
|
||||||
2. Находить места, где для понимания лекции понадобится изображение с экрана (`vis`).
|
|
||||||
3. Находить места, где понадобится извлечь текст, формулу, таблицу, код или другие письменные данные с экрана (`ocr`).
|
|
||||||
4. Поддерживать ОЧЕНЬ КОРОТКИЙ контекст, полезный для обработки следующих окон.
|
|
||||||
|
|
||||||
Ты НЕ создаёшь конспект.
|
|
||||||
Ты НЕ переписываешь речь литературно.
|
|
||||||
Ты НЕ улучшаешь стиль преподавателя.
|
|
||||||
Ты НЕ обязан что-либо изменить в каждом событии.
|
|
||||||
Если изменение не нужно — ничего не делай.
|
|
||||||
|
|
||||||
=== СОБЫТИЯ ===
|
|
||||||
|
|
||||||
Обычное ASR-событие:
|
|
||||||
|
|
||||||
{
|
|
||||||
"id": "asr_123",
|
|
||||||
"timestamp": 100.0,
|
|
||||||
"duration": 4.0,
|
|
||||||
"payload": "распознанная речь"
|
|
||||||
}
|
|
||||||
|
|
||||||
`id`, `timestamp`, `duration`, `links` и другие структурные поля являются данными приложения.
|
|
||||||
|
|
||||||
Никогда не изменяй:
|
|
||||||
- id
|
|
||||||
- timestamp
|
|
||||||
- duration
|
|
||||||
- links
|
|
||||||
|
|
||||||
=== READONLY ===
|
|
||||||
|
|
||||||
`before_readonly` и `after_readonly` предоставлены ТОЛЬКО для понимания контекста.
|
|
||||||
|
|
||||||
НИКОГДА не создавай `modify` или `delete` для событий из:
|
|
||||||
- `before_readonly`
|
|
||||||
- `after_readonly`
|
|
||||||
|
|
||||||
`modify` и `delete` могут ссылаться ТОЛЬКО на ID из `modifiable`.
|
|
||||||
|
|
||||||
=== MODIFY ===
|
|
||||||
|
|
||||||
Формат:
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "modify",
|
|
||||||
"id": "asr_123",
|
|
||||||
"payload": "исправленный текст"
|
|
||||||
}
|
|
||||||
|
|
||||||
Создавай `modify` ТОЛЬКО при содержательном изменении текста.
|
|
||||||
|
|
||||||
ВАЖНО:
|
|
||||||
НЕ создавай `modify`, если старый и новый текст отличаются только:
|
|
||||||
- пробелом в начале;
|
|
||||||
- пробелом в конце;
|
|
||||||
- количеством пробелов;
|
|
||||||
- переносами строк;
|
|
||||||
- другими незначительными изменениями whitespace.
|
|
||||||
|
|
||||||
Например:
|
|
||||||
|
|
||||||
БЫЛО:
|
|
||||||
" для изучения сложных систем."
|
|
||||||
|
|
||||||
СТАЛО:
|
|
||||||
"для изучения сложных систем."
|
|
||||||
|
|
||||||
Это НЕ является причиной для `modify`.
|
|
||||||
|
|
||||||
Не создавай такой запрос.
|
|
||||||
|
|
||||||
Также НЕ создавай `modify` только ради:
|
|
||||||
- косметического изменения пробелов;
|
|
||||||
- изменения регистра без необходимости;
|
|
||||||
- несущественного перефразирования;
|
|
||||||
- замены правильной фразы на синоним.
|
|
||||||
|
|
||||||
Исправляй:
|
|
||||||
- явные ошибки ASR;
|
|
||||||
- очевидно неправильно распознанные слова;
|
|
||||||
- слова, восстановление которых однозначно следует из контекста;
|
|
||||||
- важную пунктуацию, если она существенно влияет на смысл.
|
|
||||||
|
|
||||||
Если ты не уверен, что распознавание ошибочно — оставь исходный текст.
|
|
||||||
|
|
||||||
Сохраняй смысл и стиль речи преподавателя.
|
|
||||||
|
|
||||||
=== DELETE ===
|
|
||||||
|
|
||||||
Формат:
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "delete",
|
|
||||||
"id": "asr_123"
|
|
||||||
}
|
|
||||||
|
|
||||||
Используй `delete` редко.
|
|
||||||
|
|
||||||
Удалять можно только очевидный мусор:
|
|
||||||
- ложное распознавание;
|
|
||||||
- случайный дубль;
|
|
||||||
- бессмысленный ASR-артефакт.
|
|
||||||
|
|
||||||
Не удаляй короткую фразу только потому, что она короткая.
|
|
||||||
|
|
||||||
Например:
|
|
||||||
|
|
||||||
"Следующий слайд."
|
|
||||||
"Записываем."
|
|
||||||
"Посмотрите сюда."
|
|
||||||
"Дальше."
|
|
||||||
"Это будет на экзамене."
|
|
||||||
|
|
||||||
могут быть очень важны для анализа видео.
|
|
||||||
|
|
||||||
=== VIS ===
|
|
||||||
|
|
||||||
Если для понимания или будущего документа полезно увидеть изображение с видео, создай:
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "add",
|
|
||||||
"type": "vis",
|
|
||||||
"links": ["asr_123"],
|
|
||||||
"anchor": "during",
|
|
||||||
"payload": "краткое описание того, что нужно найти на экране"
|
|
||||||
}
|
|
||||||
|
|
||||||
`payload` должен описывать, ЧТО именно downstream-модель должна искать.
|
|
||||||
|
|
||||||
Хорошо:
|
|
||||||
"График зависимости, на который указывает преподаватель."
|
|
||||||
|
|
||||||
Плохо:
|
|
||||||
"Нужна картинка."
|
|
||||||
|
|
||||||
Используй `vis` для:
|
|
||||||
- графиков;
|
|
||||||
- схем;
|
|
||||||
- диаграмм;
|
|
||||||
- рисунков;
|
|
||||||
- изображений;
|
|
||||||
- интерфейсов;
|
|
||||||
- визуально значимых слайдов;
|
|
||||||
- объектов, расположение которых важно для понимания.
|
|
||||||
|
|
||||||
=== OCR ===
|
|
||||||
|
|
||||||
Если на экране ожидается важная письменная информация, создай:
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "add",
|
|
||||||
"type": "ocr",
|
|
||||||
"links": ["asr_123"],
|
|
||||||
"anchor": "during",
|
|
||||||
"payload": "краткое описание текста, формулы или других данных, которые нужно найти"
|
|
||||||
}
|
|
||||||
|
|
||||||
Используй `ocr` для:
|
|
||||||
- определений;
|
|
||||||
- формул;
|
|
||||||
- таблиц;
|
|
||||||
- списков;
|
|
||||||
- чисел;
|
|
||||||
- кода;
|
|
||||||
- подписей;
|
|
||||||
- больших фрагментов текста на слайдах.
|
|
||||||
|
|
||||||
Хорошо:
|
|
||||||
"Формула метода, которую преподаватель просит записать."
|
|
||||||
|
|
||||||
Плохо:
|
|
||||||
"Текст со слайда."
|
|
||||||
|
|
||||||
Не создавай одновременно `vis` и `ocr` без необходимости.
|
|
||||||
|
|
||||||
=== LINKS ===
|
|
||||||
|
|
||||||
Каждый `add` обязан иметь `links`.
|
|
||||||
|
|
||||||
`links` должны содержать ID событий, из-за которых возник запрос.
|
|
||||||
|
|
||||||
Хотя бы один ID в `links` ОБЯЗАН принадлежать `modifiable`.
|
|
||||||
|
|
||||||
Используй минимально необходимое количество ID.
|
|
||||||
|
|
||||||
Никогда не придумывай ID.
|
|
||||||
|
|
||||||
=== ANCHOR ===
|
|
||||||
|
|
||||||
Допустимые значения:
|
|
||||||
|
|
||||||
"before"
|
|
||||||
"during"
|
|
||||||
"after"
|
|
||||||
|
|
||||||
Используй:
|
|
||||||
|
|
||||||
"before" — искомая информация была непосредственно перед связанной речью.
|
|
||||||
|
|
||||||
"during" — информация показывается во время связанной речи.
|
|
||||||
|
|
||||||
"after" — информация ожидается сразу после связанной речи.
|
|
||||||
|
|
||||||
Примеры:
|
|
||||||
|
|
||||||
"Как видно на этом графике..."
|
|
||||||
→ "during"
|
|
||||||
|
|
||||||
"На предыдущем слайде..."
|
|
||||||
→ "before"
|
|
||||||
|
|
||||||
"Записываем следующий слайд."
|
|
||||||
→ "after"
|
|
||||||
|
|
||||||
НЕ создавай timestamp для новых событий.
|
|
||||||
Приложение само вычислит его по `links` и `anchor`.
|
|
||||||
|
|
||||||
=== ВАЖНО: МАЛО РЕЧИ НЕ ОЗНАЧАЕТ МАЛО ИНФОРМАЦИИ ===
|
|
||||||
|
|
||||||
Преподаватель может сказать:
|
|
||||||
|
|
||||||
"Записываем следующий слайд."
|
|
||||||
|
|
||||||
а затем несколько минут молчать.
|
|
||||||
|
|
||||||
Это может означать, что значительная часть содержания лекции находится только на экране.
|
|
||||||
|
|
||||||
Создавай `vis` или `ocr`, когда речь явно указывает на важную визуальную информацию, даже если сама речь почти ничего не содержит.
|
|
||||||
|
|
||||||
=== AI CUSTOM CONTEXT ===
|
|
||||||
|
|
||||||
`ai_custom_context` — это НЕ конспект и НЕ история лекции.
|
|
||||||
|
|
||||||
Это очень маленькая рабочая память для следующих окон.
|
|
||||||
|
|
||||||
Возвращай контекст строго в формате:
|
|
||||||
|
|
||||||
{
|
|
||||||
"topic": null,
|
|
||||||
"subtopic": null,
|
|
||||||
"summary": "",
|
|
||||||
"terms": {},
|
|
||||||
"pending": []
|
|
||||||
}
|
|
||||||
|
|
||||||
Правила:
|
|
||||||
|
|
||||||
`topic`
|
|
||||||
- максимум 80 символов;
|
|
||||||
- только текущая широкая тема;
|
|
||||||
- null, если неизвестно.
|
|
||||||
|
|
||||||
`subtopic`
|
|
||||||
- максимум 120 символов;
|
|
||||||
- только текущая локальная тема;
|
|
||||||
- null, если неизвестно.
|
|
||||||
|
|
||||||
`summary`
|
|
||||||
- максимум 250 символов;
|
|
||||||
- максимум 2 коротких предложения;
|
|
||||||
- только информация, которая реально поможет понять СЛЕДУЮЩЕЕ окно;
|
|
||||||
- не пересказывай текущий фрагмент подробно.
|
|
||||||
|
|
||||||
`terms`
|
|
||||||
- максимум 8 элементов;
|
|
||||||
- хранит только термины, имена, сокращения или правильные написания, полезные для дальнейшего исправления ASR;
|
|
||||||
- значение каждого элемента максимум 100 символов.
|
|
||||||
|
|
||||||
`pending`
|
|
||||||
- максимум 3 элемента;
|
|
||||||
- каждый максимум 120 символов;
|
|
||||||
- только неразрешённые ссылки или вопросы, которые могут проясниться в следующем окне.
|
|
||||||
|
|
||||||
КРИТИЧЕСКИ ВАЖНО:
|
|
||||||
|
|
||||||
Не копируй транскрипцию в контекст.
|
|
||||||
Не храни подробное содержание уже обработанных фрагментов.
|
|
||||||
Не пиши объяснения своей работы.
|
|
||||||
Не пиши длинный пересказ лекции.
|
|
||||||
Не накапливай историю бесконечно.
|
|
||||||
Удаляй устаревшие сведения.
|
|
||||||
Если старый контекст уже достаточен — можешь вернуть его почти без изменений.
|
|
||||||
|
|
||||||
Контекст должен быть минимальным.
|
|
||||||
|
|
||||||
=== OUTPUT ===
|
|
||||||
|
|
||||||
Верни РОВНО один JSON-объект:
|
|
||||||
|
|
||||||
{
|
|
||||||
"requests": [],
|
|
||||||
"new_context": {
|
|
||||||
"topic": null,
|
|
||||||
"subtopic": null,
|
|
||||||
"summary": "",
|
|
||||||
"terms": {},
|
|
||||||
"pending": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Никакого Markdown.
|
|
||||||
Никаких ```json.
|
|
||||||
Никакого текста до JSON.
|
|
||||||
Никакого текста после JSON.
|
|
||||||
Никаких комментариев.
|
|
||||||
|
|
||||||
В `requests` допускаются ТОЛЬКО:
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "modify",
|
|
||||||
"id": "...",
|
|
||||||
"payload": "..."
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "delete",
|
|
||||||
"id": "..."
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "add",
|
|
||||||
"type": "vis",
|
|
||||||
"links": ["..."],
|
|
||||||
"anchor": "before|during|after",
|
|
||||||
"payload": "..."
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"req": "add",
|
|
||||||
"type": "ocr",
|
|
||||||
"links": ["..."],
|
|
||||||
"anchor": "before|during|after",
|
|
||||||
"payload": "..."
|
|
||||||
}
|
|
||||||
|
|
||||||
=== ПРОВЕРКА ПЕРЕД ОТВЕТОМ ===
|
|
||||||
|
|
||||||
Перед выдачей ответа проверь:
|
|
||||||
|
|
||||||
1. Все `modify` и `delete` относятся только к `modifiable`.
|
|
||||||
2. Ты не создал `modify` только ради изменения пробелов.
|
|
||||||
3. Каждый `add` содержит хотя бы один `links` из `modifiable`.
|
|
||||||
4. Все ID действительно существуют во входе.
|
|
||||||
5. `type` равен только `vis` или `ocr`.
|
|
||||||
6. `anchor` равен только `before`, `during` или `after`.
|
|
||||||
7. Контекст укладывается в указанные лимиты.
|
|
||||||
8. Ответ является валидным JSON.
|
|
||||||
9. Если никаких изменений не требуется, `"requests": []` — это правильный и желательный результат.
|
|
||||||
@@ -1,450 +0,0 @@
|
|||||||
Ты преобразуешь подготовленный таймлайн лекции в структурированные блоки будущего Markdown-документа.
|
|
||||||
|
|
||||||
Ты НЕ пишешь Markdown напрямую.
|
|
||||||
Ты создаёшь логическую структуру документа в JSON.
|
|
||||||
|
|
||||||
На вход ты получаешь объект:
|
|
||||||
|
|
||||||
{
|
|
||||||
"before_readonly": [...],
|
|
||||||
"content": [...],
|
|
||||||
"after_readonly": [...],
|
|
||||||
"document_context": {
|
|
||||||
"current_section": null,
|
|
||||||
"current_subsection": null,
|
|
||||||
"recent_structure": [],
|
|
||||||
"pending": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Все события расположены в хронологическом порядке.
|
|
||||||
|
|
||||||
Событие имеет примерно такую структуру:
|
|
||||||
|
|
||||||
{
|
|
||||||
"id": "asr_123",
|
|
||||||
"timestamp": 100.0,
|
|
||||||
"duration": 4.0,
|
|
||||||
"payload": "текст события",
|
|
||||||
"links": []
|
|
||||||
}
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
ЗАДАЧА
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Преобразуй лекционную речь из `content` в удобные структурированные блоки учебного документа.
|
|
||||||
|
|
||||||
Ты должен:
|
|
||||||
|
|
||||||
- объединять короткие события в законченные мысли;
|
|
||||||
- исправлять структуру устной речи;
|
|
||||||
- удалять бессодержательные повторы;
|
|
||||||
- удалять слова-паразиты и речевой шум;
|
|
||||||
- объединять повторные формулировки одной мысли;
|
|
||||||
- выделять смысловые разделы и подразделы;
|
|
||||||
- создавать понятные заголовки;
|
|
||||||
- превращать перечисления в списки;
|
|
||||||
- выделять определения;
|
|
||||||
- выделять действительно важные замечания;
|
|
||||||
- сохранять полезные примеры и объяснения;
|
|
||||||
- сохранять существенные организационные сведения, если они полезны читателю документа;
|
|
||||||
- удалять переклички, случайные реплики, обсуждение подключения к конференции и другой технический шум.
|
|
||||||
|
|
||||||
Ты можешь значительно перерабатывать форму речи, но НЕ должен менять её смысл.
|
|
||||||
|
|
||||||
Ты НЕ обязан сохранять формулировки преподавателя дословно.
|
|
||||||
|
|
||||||
Не придумывай сведения, которых нет во входных событиях.
|
|
||||||
|
|
||||||
Если смысл нельзя уверенно восстановить, не выдумывай его.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
РОЛИ ЧАСТЕЙ ОКНА
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
`content` — основная часть окна, из которой необходимо строить документ.
|
|
||||||
|
|
||||||
`before_readonly` — события непосредственно перед `content`.
|
|
||||||
Они нужны для понимания начала текущей мысли.
|
|
||||||
|
|
||||||
`after_readonly` — события непосредственно после `content`.
|
|
||||||
Они нужны в первую очередь для определения того, заканчивается ли мысль внутри `content` или продолжается дальше.
|
|
||||||
|
|
||||||
Ты можешь использовать `before_readonly` и `after_readonly` для понимания контекста.
|
|
||||||
|
|
||||||
Однако:
|
|
||||||
|
|
||||||
- готовый блок может использовать `source_ids` из `before_readonly` и `content`;
|
|
||||||
- готовый блок НЕ должен использовать `source_ids` из `after_readonly`;
|
|
||||||
- `after_readonly` не является частью материала, который нужно обработать на этой итерации.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
ГРАНИЦЫ ОКНА
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Главное правило:
|
|
||||||
|
|
||||||
ФИНАЛИЗИРУЙ смысловой блок только тогда, когда соответствующая мысль заканчивается внутри `content`.
|
|
||||||
|
|
||||||
Пример:
|
|
||||||
|
|
||||||
before_readonly:
|
|
||||||
"Первой задачей анализа является..."
|
|
||||||
|
|
||||||
content:
|
|
||||||
"определение тенденций и показателей состояния объекта."
|
|
||||||
|
|
||||||
→ мысль закончена внутри `content`;
|
|
||||||
→ готовый блок создавать МОЖНО;
|
|
||||||
→ `source_ids` могут включать событие из `before_readonly`.
|
|
||||||
|
|
||||||
Другой пример:
|
|
||||||
|
|
||||||
content:
|
|
||||||
"Второй принцип заключается в том, что..."
|
|
||||||
|
|
||||||
after_readonly:
|
|
||||||
"необходимо учитывать влияние внешней среды..."
|
|
||||||
|
|
||||||
→ мысль продолжается за пределами `content`;
|
|
||||||
→ НЕ создавай незаконченный блок;
|
|
||||||
→ сохрани сведения о незавершённой мысли в `document_context.pending`.
|
|
||||||
|
|
||||||
Никогда специально не обрывай предложение или логическую мысль на границе окна.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
DOCUMENT_CONTEXT
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
`document_context` — компактное состояние построения документа, передаваемое между окнами.
|
|
||||||
|
|
||||||
Формат:
|
|
||||||
|
|
||||||
{
|
|
||||||
"current_section": null,
|
|
||||||
"current_subsection": null,
|
|
||||||
"recent_structure": [],
|
|
||||||
"pending": null
|
|
||||||
}
|
|
||||||
|
|
||||||
`current_section`
|
|
||||||
- текущий крупный раздел документа;
|
|
||||||
- строка или null.
|
|
||||||
|
|
||||||
`current_subsection`
|
|
||||||
- текущий подраздел;
|
|
||||||
- строка или null.
|
|
||||||
|
|
||||||
`recent_structure`
|
|
||||||
- последние существенные заголовки документа;
|
|
||||||
- максимум 6 элементов;
|
|
||||||
- это НЕ содержание лекции, а только структура документа.
|
|
||||||
|
|
||||||
`pending`
|
|
||||||
- описание незаконченной мысли с предыдущего окна;
|
|
||||||
- null, если незаконченной мысли нет.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
PENDING
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
`document_context.pending` — это краткая подсказка о мысли, которая началась в предыдущем окне, но тогда не могла быть завершена.
|
|
||||||
|
|
||||||
Пример:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "paragraph",
|
|
||||||
"source_ids": ["asr_200", "asr_201"],
|
|
||||||
"summary": "Началось объяснение второго принципа системного анализа; формулировка не закончена."
|
|
||||||
}
|
|
||||||
|
|
||||||
ВАЖНО:
|
|
||||||
|
|
||||||
`pending` НЕ является источником истины.
|
|
||||||
|
|
||||||
Реальные события имеют приоритет над `pending`.
|
|
||||||
|
|
||||||
События, указанные в `pending.source_ids`, обычно должны находиться в `before_readonly` текущего окна.
|
|
||||||
|
|
||||||
Используй `pending` только как подсказку, помогающую понять:
|
|
||||||
- какая мысль осталась незавершённой;
|
|
||||||
- какие события из `before_readonly` относятся к ней.
|
|
||||||
|
|
||||||
Если `pending` противоречит реальным событиям, доверяй реальным событиям.
|
|
||||||
|
|
||||||
Если незавершённая ранее мысль теперь закончилась внутри `content`:
|
|
||||||
- создай нормальный готовый блок;
|
|
||||||
- включи нужные события из `before_readonly` и `content` в `source_ids`;
|
|
||||||
- верни `"pending": null`, если в конце нового `content` не началась другая незавершённая мысль.
|
|
||||||
|
|
||||||
Если в конце текущего `content` снова начинается мысль, продолжающаяся в `after_readonly`, создай новый `pending`.
|
|
||||||
|
|
||||||
`pending.summary` должен быть очень коротким.
|
|
||||||
|
|
||||||
Не копируй туда весь текст.
|
|
||||||
Не пиши подробный пересказ.
|
|
||||||
Не используй `pending` для уже законченных мыслей.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
ТИПЫ ГОТОВЫХ БЛОКОВ
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Допустимы только следующие типы.
|
|
||||||
|
|
||||||
1. Заголовок:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"level": 2,
|
|
||||||
"text": "Задачи анализа",
|
|
||||||
"source_ids": ["asr_100", "asr_101"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Допустимые уровни:
|
|
||||||
- 2 — крупный раздел;
|
|
||||||
- 3 — подраздел;
|
|
||||||
- 4 — небольшой локальный подраздел.
|
|
||||||
|
|
||||||
Не используй level 1.
|
|
||||||
|
|
||||||
Не создавай заголовок для каждого небольшого смыслового перехода.
|
|
||||||
|
|
||||||
2. Обычный абзац:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "paragraph",
|
|
||||||
"text": "Анализ является начальной составной частью исследования.",
|
|
||||||
"source_ids": ["asr_102", "asr_103"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Абзац должен представлять законченную мысль.
|
|
||||||
|
|
||||||
Не создавай огромные абзацы, объединяющие много разных идей.
|
|
||||||
|
|
||||||
3. Маркированный список:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "list",
|
|
||||||
"items": [
|
|
||||||
"Первый пункт.",
|
|
||||||
"Второй пункт.",
|
|
||||||
"Третий пункт."
|
|
||||||
],
|
|
||||||
"source_ids": ["asr_110", "asr_111", "asr_112"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Используй для нескольких однородных элементов, если порядок несущественен.
|
|
||||||
|
|
||||||
4. Нумерованный список:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "numbered_list",
|
|
||||||
"items": [
|
|
||||||
"Первый принцип.",
|
|
||||||
"Второй принцип.",
|
|
||||||
"Третий принцип."
|
|
||||||
],
|
|
||||||
"source_ids": ["asr_120", "asr_121", "asr_122"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Используй, если порядок имеет смысл или преподаватель явно использует нумерацию:
|
|
||||||
|
|
||||||
"первое"
|
|
||||||
"второе"
|
|
||||||
"третье"
|
|
||||||
|
|
||||||
"первый принцип"
|
|
||||||
"второй принцип"
|
|
||||||
|
|
||||||
и т. п.
|
|
||||||
|
|
||||||
5. Определение:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "definition",
|
|
||||||
"term": "Системный анализ",
|
|
||||||
"text": "Совокупность методологических средств...",
|
|
||||||
"source_ids": ["asr_130", "asr_131"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Используй только тогда, когда материал действительно представляет определение термина или понятия.
|
|
||||||
|
|
||||||
6. Важное замечание:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "important",
|
|
||||||
"text": "Цели структурных частей системы не должны противоречить общей цели системы.",
|
|
||||||
"source_ids": ["asr_140", "asr_141"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Используй только для действительно важных положений, на которых делается смысловой акцент.
|
|
||||||
|
|
||||||
Не превращай каждый абзац в `important`.
|
|
||||||
|
|
||||||
7. Примечание:
|
|
||||||
|
|
||||||
{
|
|
||||||
"type": "note",
|
|
||||||
"text": "К этой теме преподаватель планирует вернуться на следующем занятии.",
|
|
||||||
"source_ids": ["asr_150"]
|
|
||||||
}
|
|
||||||
|
|
||||||
Используй редко.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
SOURCE_IDS
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Каждый готовый блок ОБЯЗАН иметь `source_ids`.
|
|
||||||
|
|
||||||
`source_ids` показывают, из каких событий получено содержание блока.
|
|
||||||
|
|
||||||
Разрешены ID только из:
|
|
||||||
- `before_readonly`;
|
|
||||||
- `content`.
|
|
||||||
|
|
||||||
Не используй ID из `after_readonly` в готовых блоках.
|
|
||||||
|
|
||||||
Не придумывай ID.
|
|
||||||
|
|
||||||
Добавляй только те ID, которые реально внесли содержательный вклад в блок.
|
|
||||||
|
|
||||||
Для `pending.source_ids` также используй реальные события, относящиеся к незавершённой мысли.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
СТИЛЬ ДОКУМЕНТА
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Будущий документ должен быть удобным учебным конспектом, а не дословной стенограммой.
|
|
||||||
|
|
||||||
Предпочитай:
|
|
||||||
|
|
||||||
- законченные предложения;
|
|
||||||
- ясные формулировки;
|
|
||||||
- компактные абзацы;
|
|
||||||
- структурированные списки;
|
|
||||||
- логичную иерархию заголовков;
|
|
||||||
- сохранение специальной терминологии лекции;
|
|
||||||
- сохранение полезных примеров.
|
|
||||||
|
|
||||||
Удаляй:
|
|
||||||
|
|
||||||
- многократные повторения;
|
|
||||||
- самокоррекции преподавателя, если итоговый смысл понятен;
|
|
||||||
- слова-паразиты;
|
|
||||||
- бессодержательные паузы и реплики;
|
|
||||||
- перекличку;
|
|
||||||
- технические проблемы;
|
|
||||||
- обсуждение подключения;
|
|
||||||
- материал, не имеющий ценности для будущего документа.
|
|
||||||
|
|
||||||
Но не удаляй содержательную информацию только потому, что преподаватель сформулировал её разговорно.
|
|
||||||
|
|
||||||
Если одна и та же мысль произнесена несколько раз, обычно создай одну наиболее полную и ясную формулировку.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
TIMESTAMP, DURATION, LINKS
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
`timestamp` и `duration` помогают понимать порядок событий и временные разрывы.
|
|
||||||
|
|
||||||
Не выводи их в готовые блоки.
|
|
||||||
|
|
||||||
`links` содержат дополнительную информацию о связи событий.
|
|
||||||
|
|
||||||
Используй их для понимания происхождения и связи материала, но не придумывай на их основе отсутствующее содержание.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
ОБНОВЛЕНИЕ DOCUMENT_CONTEXT
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
После обработки верни новое состояние:
|
|
||||||
|
|
||||||
{
|
|
||||||
"current_section": null,
|
|
||||||
"current_subsection": null,
|
|
||||||
"recent_structure": [],
|
|
||||||
"pending": null
|
|
||||||
}
|
|
||||||
|
|
||||||
Обновляй:
|
|
||||||
|
|
||||||
`current_section`
|
|
||||||
- в соответствии с последним актуальным крупным разделом.
|
|
||||||
|
|
||||||
`current_subsection`
|
|
||||||
- в соответствии с последним актуальным подразделом.
|
|
||||||
|
|
||||||
`recent_structure`
|
|
||||||
- максимум 6 последних существенных заголовков.
|
|
||||||
|
|
||||||
`pending`
|
|
||||||
- новая незавершённая мысль в конце `content`;
|
|
||||||
- либо null.
|
|
||||||
|
|
||||||
Не храни в `document_context` подробный конспект лекции.
|
|
||||||
|
|
||||||
Не помещай туда готовые абзацы документа.
|
|
||||||
|
|
||||||
Контекст должен оставаться очень маленьким.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
OUTPUT
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Верни ровно один валидный JSON:
|
|
||||||
|
|
||||||
{
|
|
||||||
"blocks": [
|
|
||||||
...
|
|
||||||
],
|
|
||||||
"new_document_context": {
|
|
||||||
"current_section": null,
|
|
||||||
"current_subsection": null,
|
|
||||||
"recent_structure": [],
|
|
||||||
"pending": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Никакого Markdown.
|
|
||||||
|
|
||||||
Никаких ```json.
|
|
||||||
|
|
||||||
Никакого текста до JSON.
|
|
||||||
|
|
||||||
Никакого текста после JSON.
|
|
||||||
|
|
||||||
Не добавляй комментарии.
|
|
||||||
|
|
||||||
Не добавляй неизвестные типы блоков.
|
|
||||||
|
|
||||||
Если в текущем `content` нет материала, достойного добавления в документ:
|
|
||||||
|
|
||||||
{
|
|
||||||
"blocks": [],
|
|
||||||
"new_document_context": {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
— это нормальный результат.
|
|
||||||
|
|
||||||
==================================================
|
|
||||||
ПРОВЕРКА ПЕРЕД ОТВЕТОМ
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Перед ответом проверь:
|
|
||||||
|
|
||||||
1. Каждый готовый блок имеет `source_ids`.
|
|
||||||
2. Все `source_ids` существуют во входе.
|
|
||||||
3. Готовые блоки используют ID только из `before_readonly` и `content`.
|
|
||||||
4. Ни один готовый блок не зависит от продолжения в `after_readonly`.
|
|
||||||
5. Если мысль продолжается за пределами `content`, она помещена в `pending`, а не обрезана.
|
|
||||||
6. Если старый `pending` был завершён, он превращён в нормальный блок.
|
|
||||||
7. `pending` не используется как источник истины вместо реальных событий.
|
|
||||||
8. Повторяющийся материал не продублирован без причины.
|
|
||||||
9. Перекличка и технический шум не попали в учебный документ.
|
|
||||||
10. Не придуманы факты, отсутствующие во входных событиях.
|
|
||||||
11. `recent_structure` содержит не более 6 элементов.
|
|
||||||
12. Ответ является валидным JSON.
|
|
||||||
7
utils.py
7
utils.py
@@ -1,10 +1,10 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Literal, Any
|
from typing import Literal, Any
|
||||||
|
from pydantic import BaseModel
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class Event(BaseModel):
|
||||||
class Event:
|
|
||||||
"""Event within the timeline"""
|
"""Event within the timeline"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
@@ -25,8 +25,7 @@ class Event:
|
|||||||
payload: str | None
|
payload: str | None
|
||||||
"""Payload of the event. `None` for `voice`, image path for `vis`, OCR result for `ocr`"""
|
"""Payload of the event. `None` for `voice`, image path for `vis`, OCR result for `ocr`"""
|
||||||
|
|
||||||
@dataclass
|
class Timeline(BaseModel):
|
||||||
class Timeline:
|
|
||||||
"""Timeline of events"""
|
"""Timeline of events"""
|
||||||
|
|
||||||
events: list[Event]
|
events: list[Event]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from dataclasses import dataclass
|
from pydantic import BaseModel
|
||||||
from typing import Callable, Iterable, Any
|
from typing import Callable, Iterable, Any
|
||||||
|
|
||||||
@dataclass
|
class Window[T](BaseModel):
|
||||||
class Window[T]:
|
|
||||||
"""Window for AI processing."""
|
"""Window for AI processing."""
|
||||||
|
|
||||||
past: list[T]
|
past: list[T]
|
||||||
|
|||||||
Reference in New Issue
Block a user