Поток отслеживания линии от исходного ввода до AST и Retriever
Дата написания: 27 июня 2026 г.
Цель
Этот документ — переработанная версия документа docs/roadmaps/2026-06-26-source-to-ast-input-flow.md, в которой уточнены части, ставшие неясными к 27‑му дню.
Суть не в переопределении нового проектного блока. Вся архитектура и направление проектирования БД уже описаны отдельно. Здесь мы будем полностью отслеживать, какой файл и какая строка попадают в парсер AST, какой фрагмент используется в поиске Retriever и какие кандидатные основания возвращаются для оценки LLM.
Файловый путь добавляется для отслеживания местоположения.
В Retriever попадают не файловые пути, а фрагменты кода/текста.
Файлы *.gd* парсером AST разбиваются по функциям и объявлениям.
Все остальные необходимые текстовые файлы разрезаются напрямую по диапазонам строк.
Каждый фрагмент последовательно проходит через Retriever и оценку LLM.(No content to translate.)
При клонировании или загрузке проекта сначала раскрывайте файлы с относительным путём.
Форма выглядит следующим образом.
# <относительный путь>
Содержимое файла
# <относительный путь>
<содержимое файла>Это обозначение предназначено для отладки человеком. Заголовок вроде # player.gd служит лишь указателем «из какого файла взят последующий текст».
В поиск Retriever не попадает # player.gd. В AST Parser также не попадает # player.gd. Попадает реальное содержимое файла под заголовком.
Пример ввода
Ниже показано развернутое небольшое проект Godot. Номера вроде E001, E002 — это номера строк для пояснений и они не включаются в фактический ввод.
E001 # project.godot
E002 config_version=5
E003
E004 [application]
E005 config/name="Mini Dodge"
E006 run/main_scene="res://player.tscn"
E007
E008 # player.gd
E009 extends Area2D
E010
E011 signal hit
E012
E013 @export var speed = 400
E014 var screen_size
E015
E016 func _ready():
E017 screen_size = get_viewport_rect().size
E018 hide()
E019
E020 func _process(delta):
E021 var velocity = Vector2.ZERO
E022 if Input.is_action_pressed(&"move_right"):
E023 velocity.x += 1
E024 if Input.is_action_pressed(&"move_left"):
E025 velocity.x -= 1
E026
E027 if velocity.length() > 0:
E028 velocity = velocity.normalized() * speed
E029 $AnimatedSprite2D.play()
E030 else:
E031 $AnimatedSprite2D.stop()
E032
E033 position += velocity * delta
E034 position = position.clamp(Vector2.ZERO, screen_size)
E035
E036 func start(pos):
E037 position = pos
E038 show()
E039 $CollisionShape2D.disabled = false
E040
E041 # player.tscn
E042 [gd_scene load_steps=3 format=3 uid="uid://mini"]
E043
E044 [ext_resource type="Script" path="res://player.gd" id="1"]
E045
E046 [node name="Player" type="Area2D"]
E047 script = ExtResource("1")1 этап: разделение границ файлов
Сначала разделите файлы, используя заголовок # <относительный_путь> в качестве ориентира.
project.godot начинается с E001. Основное содержание находится с E002 по E006. E001 служит лишь маркером границы файла и не является частью анализируемого текста.
player.gd начинается с E008. Основное содержание находится с E009 по E039. E008 — это маркер границы файла, поэтому он не попадает в AST‑парсер.
player.tscn начинается с E041. Основное содержание находится с E042 по E047. E041 — это маркер границы файла, поэтому он не включается в поиск Retriever.
На этом этапе пользовательский интерфейс может показывать, например, «в данный момент обрабатываются E009‑E039 из player.gd». Однако поисковый запрос пока не формируется.
2 этап: Определение направления обработки в зависимости от типа файла
player.gd — это файл GDScript. Поэтому весь диапазон E009‑E039 попадает в AST Parser.
project.godot — это файл настроек. Он не отправляется в AST Parser, а разбивается вручную по необходимым секциям.
player.tscn — это файл сцены. Он не отправляется в AST Parser, а разбивается вручную по блокам node/resource.
README или документ с описанием Markdown по умолчанию исключаются в режиме анализа исходного кода. Исключённые файлы также должны отображаться в пользовательском интерфейсе. Причина в том, что человек должен проверять, «что было упущено».
Если, например, существует README.md, то он должен быть исключён из анализа, а причина исключения должна отображаться как «В режиме анализа исходного кода файлы документации исключаются по умолчанию». При этом это решение об исключении является лишь значением по умолчанию для режима анализа; если позже понадобится режим, в котором читаются и документы, его следует настроить отдельно.
Шаг 3: Передать файл .gd в AST Parser
В player.gd ввод, поступающий в AST Parser, — E009‑E039.
Фактически текст, получаемый AST Parser, выглядит следующим образом.
extends Area2D
signal hit
@export var speed = 400
var screen_size
func _ready():
screen_size = get_viewport_rect().size
hide()
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_action_pressed(&"move_right"):
velocity.x += 1
if Input.is_action_pressed(&"move_left"):
velocity.x -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play()
else:
$AnimatedSprite2D.stop()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)
func start(pos):
position = pos
show()
$CollisionShape2D.disabled = falseЗдесь нет # player.gd.
AST Parser читает этот текст сверху вниз и разбивает его на объявления и функции.
Первый фрагмент — одна строка E009.
extends Area2DВторой фрагмент — это одна строка E011.
signal hitТретий кусок — E013‑E014.
@export var speed = 400
var screen_sizeЧетвёртый кусок — E016‑E018.
func _ready():
screen_size = get_viewport_rect().size
hide()Пятый фрагмент — E020‑E034.
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_action_pressed(&"move_right"):
velocity.x += 1
if Input.is_action_pressed(&"move_left"):
velocity.x -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play()
else:
$AnimatedSprite2D.stop()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)Шестой фрагмент — E036‑E039.
func start(pos):
position = pos
show()
$CollisionShape2D.disabled = falseВажно, что после создания фрагмента он не теряет исходный диапазон строк. Например, фрагмент _process продолжает отслеживаться как «фрагмент из player.gd в диапазоне E020‑E034».
Шаг 4: Прямая обработка фрагментов
Если нужны текстовые файлы, не являющиеся объектом AST‑парсера, их также разбивают на фрагменты.
В project.godot фрагментами настроек приложения становятся E004‑E006.
[application]
config/name="Mini Dodge"
run/main_scene="res://player.tscn"Этот фрагмент можно использовать для поиска начальной сцены проекта благодаря run/main_scene="res://player.tscn". При выполнении поиска Retriever в тексте в качестве поискового материала будут использоваться такие элементы, как run/main_scene, res://player.tscn, application. Сам путь к файлу project.godot не становится поисковым запросом.
В player.tscn одна строка E044 служит фрагментом ссылки на скрипт.
[ext_resource type="Script" path="res://player.gd" id="1"]E046‑E047 становится фрагментом узла.
[node name="Player" type="Area2D"]
script = ExtResource("1")Этот прямой фрагмент не является AST‑чанком, но последующий процесс тот же. Частью текста ищут в Retriever, а найденные кандидаты проверяются LLM.
Шаг 5: Что действительно попадает в Retriever
В Retriever попадает не «путь к файлу», а «текст текущего фрагмента и извлечённые из него поисковые подсказки».
Например, при обработке фрагмента _process в качестве основы для поиска используется текст E020‑E034.
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_action_pressed(&"move_right"):
velocity.x += 1
if Input.is_action_pressed(&"move_left"):
velocity.x -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play()
else:
$AnimatedSprite2D.stop()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)Подсказки для поиска в этом тексте представлены следующим образом.
Input.is_action_pressed
Vector2.ZERO
velocity.normalized
position.clamp
AnimatedSprite2D
_process
Godot 3
Godot 4
migrationВ приведённых подсказках Godot 3, Godot 4, migration добавляются, когда пользовательский запрос звучит как «проверьте, является ли этот проект Godot 3 или Godot 4, и при необходимости найдите основания для преобразования».
Поиск Retriever использует этот фрагмент текста и подсказки.
Векторный поиск включает полное содержимое функции _process и основные символы.
Ключевой поиск включает такие имена символов, как Input.is_action_pressed, Vector2.ZERO, position.clamp.
Путь к файлу player.gd не используется как поисковый запрос. Однако позже он применяется для добавления в журнал и пользовательский интерфейс фразы «Этот поиск произошёл в player.gd в диапазоне E020‑E034».
Шаг 6: Что ищет Retriever
Retriever ищет в тех же фрагментах docs_chunks, api_mapping, label_prototypes одинаковым способом. Ни одну из трёх таблиц он не обрабатывает особым образом.
В docs_chunks могут появиться кандидаты из официальной документации, такие как Input.is_action_pressed, Vector2.ZERO, position, Area2D.
В api_mapping могут появиться кандидаты на имена функций, классов и свойств, изменённые при переходе от Godot 3 к Godot 4. Например, если в кодовом фрагменте есть подсказки KinematicBody2D, yield, Color8, могут быть предложены соответствующие варианты миграции.
В label_prototypes могут появиться кандидаты, представляющие «паттерн, при котором полностью меняется способ использования функции» или «пример, где меняется состав аргументов и форма вызова». Это имеет смысл, когда необходимо объяснить не просто изменение имени, а изменение самого способа использования.
Например, в фрагменте _process могут вернуться следующие варианты.
Кандидат docs_chunks:
Описание официальной документации Input.is_action_pressed
Vector2.ZERO официальное описание в документации
Node2D.position — описание в официальной документации
api_mapping кандидат:
В этом фрагменте может не быть особых кандидатов на переименование из Godot 3 → 4.
label_prototypes кандидаты:
Пример кода перемещения в Godot 4 с использованием `Input.is_action_pressed` и `Vector2.ZERO`Эти кандидаты не являются правильными ответами. Это лишь «основания, которые могут быть связаны с этим фрагментом кода».
7 шаг: Передача результатов Retriever в LLM
В LLM попадают пользовательский запрос, текущий фрагмент кода и кандидаты Retriever.
Например, в фрагменте _process это выглядит следующим образом.
**Запрос пользователя:**
Проверьте, относится ли этот проект к Godot 3 или Godot 4, и при необходимости найдите основания для преобразования.
**Текущий фрагмент кода:**
`player.gd` — строки E020‑E034
func _process(delta):
var velocity = Vector2.ZERO
if Input.is_action_pressed(&"move_right"):
velocity.x += 1
if Input.is_action_pressed(&"move_left"):
velocity.x -= 1
if velocity.length() > 0:
velocity = velocity.normalized() * speed
$AnimatedSprite2D.play()
else:
$AnimatedSprite2D.stop()
position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)
**Кандидаты‑источники:**
- `docs_chunks` — возможный официальный документ по `Input.is_action_pressed`
- `docs_chunks` — возможный официальный документ по `Vector2.ZERO`
- `docs_chunks` — возможный официальный документ по `Node2D.position`
- `label_prototypes` — пример обработки ввода в Godot 4LLM здесь делает два вывода.
Во‑первых, проверяет, действительно ли найденный кандидат относится к текущему фрагменту кода.
Во‑вторых, определяет, относится ли текущий фрагмент к сигналам Godot 3, к сигналам Godot 4 или требует миграции.
В этом примере такие элементы, как Input.is_action_pressed, Vector2.ZERO, position.clamp, могут быть интерпретированы как основание того, что код будет работать и в Godot 4. Поэтому такой фрагмент, скорее всего, будет отнесён к категории «можно рассматривать как код Godot 4, миграция не требуется».
Шаг 8: Поток примера фрагмента Godot 3
Предположим, что в другом файле есть следующий фрагмент.
G101 # enemy.gd
G102 extends KinematicBody2D
G103
G104 var velocity = Vector2.ZERO
G105
G106 func _physics_process(delta):
G107 velocity = move_and_slide(velocity)enemy.gd является файлом .gd, поэтому в AST Parser попадает тело G102‑G107.
Первый фрагмент — G102.
extends KinematicBody2DВторой кусок — G106‑G107.
func _physics_process(delta):
velocity = move_and_slide(velocity)Если передать фрагмент extends KinematicBody2D в Retriever, поисковый запрос будет выглядеть следующим образом.
KinematicBody2D
Godot 3
Godot 4
migrationВ этом случае в api_mapping может появиться кандидат, в котором KinematicBody2D мигрирует в семейство CharacterBody2D в Godot 4.
В docs_chunks могут появиться кандидаты официальной документации, связанные с CharacterBody2D или физическими телами.
В label_prototypes может появиться пример, где код перемещения на основе KinematicBody2D заменяется на CharacterBody2D с использованием velocity и метода move_and_slide().
В LLM это будет выглядеть примерно так:
Запрос пользователя:
Проверьте, использует ли этот проект Godot 3 или Godot 4, и при необходимости найдите основания для миграции.
Текущий фрагмент кода:
G102 из enemy.gd
extends KinematicBody2D
Retriever кандидат:
Кандидат на миграцию KinematicBody2D → CharacterBody2D в api_mapping.
Кандидат официальной документации CharacterBody2D из docs_chunks
Пример кандидата преобразования кода перемещения KinematicBody2D в label_prototypesLLM может оценить этот фрагмент как «Godot 3 сигнал сильный и требуется миграция».
Затем фрагменты G106‑G107 также обрабатываются отдельно.
func _physics_process(delta):
velocity = move_and_slide(velocity)В этом фрагменте способ вызова move_and_slide(velocity) служит подсказкой для поиска. Рассматривается не только название функции, но и форма вызова с аргументами.
Если меняется только название функции, то важным кандидатом является api_mapping, а если меняется вся форма вызова, то также важным становится кандидат label_prototypes.
Шаг 9: Накопление результатов фрагментов
Каждый фрагмент оценивается по отдельности.
Элементы E020‑E034 в player.gd могут быть накоплены как сигналы Godot 4.
Элемент G102 в enemy.gd может быть накоплен как сигнал Godot 3.
Элементы G106‑G107 в enemy.gd также могут быть накоплены как сигналы миграции к Godot 3.
Оценка всего проекта не делается по имени файла. Путь к файлу используется только для отслеживания. Оценка проекта в целом делается на основе накопленных результатов оценки каждого кода‑фрагмента.
Например, если в проекте большинство сигналов относится к Godot 4, но встречаются отдельные сигналы миграции к Godot 3, то оценка может быть ближе к mixed. Если же большинство сигналов относится к миграции Godot 3, проект может быть классифицирован как Godot 3.
Приостановка и возобновление
RunPod или локальный веб‑сервер могут остановиться посередине. Поэтому обработка должна продолжаться не по файлам, а по фрагментам.
Например, если обработаны E009, E011, E013‑E014 в player.gd и процесс остановился на E016‑E018, после перезапуска следует продолжить с E016‑E018.
Уже завершённые E009, E011, E013‑E014 повторно обрабатывать нельзя.
В веб‑интерфейсе должно отображаться следующее.
player.gd E009 обработка завершена
player.gd E011 обработка завершена
player.gd E013-E014 обработка завершена
player.gd E016-E018 обработка прервана
player.gd E020-E034 в ожидании
player.gd E036-E039 в ожиданииТак должно отображаться, чтобы можно было проверить, продолжается ли предыдущая обработка или начинается заново.
Что должно отображаться на экране отладки
Экран отладки необходим для отслеживания.
По крайней мере, должно отображаться следующее.
Текущий файл: player.gd
Текущий диапазон строк: E020-E034
Текущее содержимое фрагмента:
func _process(delta):
...
Ключи поиска, введённые в Retriever:
- Input.is_action_pressed
- Vector2.ZERO
- position.clamp
Найденные кандидаты:
- docs_chunks: Input.is_action_pressed
- docs_chunks: Vector2.ZERO
- label_prototypes: Godot 4 input polling example
Вывод LLM:
- Сигналы Godot 4
- Миграция не требуетсяЗдесь важно то, что «что попало в поиск», и «что просто прикреплено для отслеживания», отображаются раздельно.
Путь player.gd может отображаться в текущем файле, но не должен входить в список подсказок поиска Retriever.
Итоги
Поток, фиксируемый в документе на 27‑й день, выглядит следующим образом.
# <относительный путь>
<тело>
-> Делим файл только заголовками.
-> Текст .gd‑файла попадает в AST‑парсер.
-> AST‑парсер последовательно вырезает функции и объявления.
-> Реальный текст каждого фрагмента и подсказки символов отправляются в поиск Retriever.
-> docs_chunks, api_mapping, label_prototypes ищутся тем же способом.
-> Кандидаты поиска и текущий фрагмент передаются в LLM вместе.
-> Решения LLM сохраняются по фрагментам и накапливаются по проекту.Цель этого документа не в том, чтобы установить новую структуру, а в том, чтобы можно было увидеть, на какой строке происходит разрез входных данных и какой текст переходит на следующий этап. При реализации необходимо также отображать в веб‑интерфейсе текущий файл, диапазон текущих строк, фактический передаваемый текст, подсказку для поиска Retriever, варианты поиска и решение LLM согласно этим критериям.