Уроборос

Записывает, как код на самом деле исполнялся: вызовы, доводы, результаты, исключения, длительности

View the Project on GitHub digitable-lol/ouroboros

Чтобы ИИ понимал, как код исполняется

Модель, читающая исходник, рассуждает о том, что должно происходить. Модель, читающая записи о вызовах, знает, что происходило.

Разница не в объёме сведений, а в том, какой вопрос вообще имеет ответ. По исходнику видно, что может случиться: ветви, которые там написаны, доводы, которые там объявлены. По записям видно другое — какие ветви исполнялись, какие доводы встретились на самом деле, какие вызовы зависли и какие бросили исключение. Второго в исходнике нет ни в каком виде, и вывести его чтением нельзя.

Обратное тоже верно, и это надо держать рядом: записи не скажут, что код неправильный. Они скажут, что он делает. Вывод «а должно быть иначе» остаётся человеку.

Что для этого сделать

  1. Поставить инструмент — Установка.
  2. Дописать в настройку клиента один блок:

    { "mcpServers": { "ouroboros": { "type": "stdio", "command": "ouroboros-mcp" } } }
    
  3. Проверить, что у агента появились инструменты с именами wrap_file, read_trace, trace_stats и остальные — всего семнадцать.
  4. Дать задание. Готовая формулировка — ниже.

Семнадцать инструментов, четыре группы

Сервер выкладывает операции напрямую, без промежуточного слоя выбора: агент видит все семнадцать имён и зовёт их по имени.

Дописать запись о вызовах

инструмент что делает
wrap_code_snippet обмазывает переданную строку кода, ничего не сохраняя
wrap_file обмазывает файл на месте
wrap_functions обмазывает только названные функции — для горячих и больших файлов

Прочитать записи

инструмент что делает
read_trace записи с отбором и чтением частями
trace_stats сводка: сколько раз звали, сколько бросили, сколько шло

Черновик

инструмент что делает
create_project заводит черновик с историей изменений
write_file обмазывает до сохранения; неразбираемое отвергает
read_file, list_files посмотреть, что в черновике
execute запускает команду, сам подставляя путь к debug.info
finish копирует черновик в чистовик; обмазку не снимает

C и C++ через clangd — выбрать, что обмазывать, в большом дереве

инструмент что делает
lint_file разбор clang-tidy
symbol_search найти имя по всему дереву
document_symbols что определено в одном файле
references кто зовёт
call_hierarchy кто зовёт кого, по цепочке
describe_symbol где определено и с какой подписью

У каждого инструмента проставлено, что он делает с файлами: читающие помечены как безопасные, wrap_file и finish — как переписывающие, execute — как запускающий произвольную команду. Клиент видит это до первого вызова (ouroboros/mcp/server.py:644).

Таблицы выше — краткий пересказ. Полный справочник снят с живого сервера: Справочник средств MCP — на каждое из семнадцати средств заголовок, описание, схема доводов, схема ответа и настоящий ответ на настоящий вызов. Он не написан руками, а напечатан из docs/mcp-tools.json, который снимается разговором с сервером командой scripts/probe/build-reference.sh. Если пересказ здесь и справочник разойдутся — верен справочник.

Как это выглядит на самом деле

Вызов wrap_file через сервер MCP:

{
  "ok": true,
  "path": "/srv/tmp/ouro-work/mcpdemo/stats.py",
  "language": "python",
  "functions_wrapped": 3,
  "runtime_header": "/srv/tmp/ouro-work/mcpdemo/ouroboros_runtime.py"
}

После прогона программы — trace_stats по тому же черновику:

{
  "ok": true,
  "calls_parsed": 5,
  "malformed": 0,
  "total_calls": 5,
  "by_function": [
    { "name": "parse_line", "count": 3, "result": 3, "raised": 0, "unknown": 0,
      "duration_seconds": { "min": 1e-06, "max": 2e-06, "mean": 2e-06, "total": 5e-06, "count": 3 } },
    { "name": "average", "count": 1, "result": 1, "raised": 0, "unknown": 0,
      "duration_seconds": { "min": 2e-06, "max": 2e-06, "mean": 2e-06, "total": 2e-06, "count": 1 } },
    { "name": "report", "count": 1, "result": 1, "raised": 0, "unknown": 0,
      "duration_seconds": { "min": 0.000351, "max": 0.000351, "mean": 0.000351, "total": 0.000351, "count": 1 } }
  ]
}

Это настоящий обмен с сервером, а не образец.

Порядок работы

create_project  →  write_file  →  execute  →  read_trace  →  finish
   черновик       обмазывает      запускает    читаем       переносим
                  при записи                   записи       наружу

Или короче, без черновика: wrap_file → агент сам запускает программу → read_trace.

Горячие файлы. wrap_file на файле с миллионом вызовов в секунду топит нужные записи в шуме. Агент, которому не сказали иного, скорее возьмёт wrap_file — поэтому на горячем файле имя wrap_functions называют в задании прямо.

Формулировка для задания агенту

Годится как есть:

Разберись, как на самом деле исполняется <файл>. Возьми уроборос: заведи черновик, обмажь <файл> (на горячем файле — wrap_functions, назови функции), прогони на настоящих данных, прочитай записи.

В отчёте назови: сколько вызовов записано, какие функции исполнялись и с какими доводами, что вернулось, что бросило исключение и что не вернулось (in_flight). Отдельно — сколько строк не разобралось (malformed).

Выводов о правильности программы не делай: записи говорят, что код сделал, а не что он должен был сделать. Если увидишь странность — назови её вопросом ко мне, а не утверждением.

Что агент обязан проверить, а не принять на веру

Что вообще что-то записалось. calls_parsed: 0 значит, что обмазанный код не исполнялся, а не что всё чисто. Это первое, на что стоит смотреть.

Испорченные строки. malformed больше нуля — часть записей не разобралась, и выводы сделаны по неполной картине.

Невернувшиеся вызовы. in_flight — это вход без выхода: зависание, падение или жёсткий выход. Пустой список хорошо, непустой требует объяснения.

Покрытие. Ветвь, в которую не зашли, записей не даст и не пожалуется. Утверждать по трассе что-либо о неисполнявшемся коде нельзя.

Что именно записано. Запись утверждает «на этом входе вышло вот это». Она не утверждает, что так правильно. Агент, подающий трассу как доказательство правильности, ошибается — и это самая частая ошибка при работе с инструментом.

Длительность — не цена. Поле d измеряет обмазанный прогон. Читать его как стоимость обычного прогона нельзя.

Навык

В хранилище лежит навык skill/SKILL.md — короткое описание инструмента для агента: что поставить, как подключить сервер, все семнадцать средств, порядок работы и границы с замеренными числами. Он описывает этот инструмент и ничего сверх него.