Skip to main content
Glama

groundtruth-mcp

ci pypi python license

Ваш агент кодирования может прочитать каждый файл в вашем репозитории и всё равно лишь гадает. Это превращает собственные проверки, повторы, симуляции и запросы вашего проекта в инструменты MCP, поэтому он наблюдает последствия своего изменения, а не предсказывает их.

Китайская документация · Руководство по внедрению · Архитектура · Почему фиксированные сиды


Проблема

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

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

Решение — не улучшенный промпт. Решение — дать агенту возможность что-то наблюдать.

Что это делает

flowchart LR
    E[Agent edits a config] --> L[lint]
    L -->|DANGLING_TRANSITION at states 1.transitions 0.to| E
    E --> R[replay seed=7]
    R -->|the 5 steps that actually ran| E
    E --> S[simulate 2000 seeds]
    S -->|88.3% success · p95 2566ms · PASS| E
    S --> G["CI: groundtruth simulate --gate"]
    G -->|same config, same thresholds| S

Пять инструментов, построенных на четырёх небольших функциях, которые вы пишете:

Инструмент

Отвечает

Свойство, которое делает его полезным

lint

Является ли эта конфигурация самосогласованной?

Каждая проблема несёт точный путь для правки

replay

Что произойдёт, если я запущу именно эту?

Чистая функция от (config, seed) — воспроизводима где угодно

simulate

Стало ли моё изменение лучше или хуже в целом?

Сидированный батч, распределение, пороги, прохождение/непрохождение

query

Что на самом деле в данных?

Только чтение, обеспечиваемое базой данных, а не регулярным выражением

describe_data

Какие таблицы существуют?

Чтобы никому не пришлось угадывать схему

Те же возможности доступны через CLI, так что groundtruth simulate --gate — это шлюз слияния, читающий те же пороги, против которых агент оптимизирует. Они не могут разойтись, потому что существует только одна копия.

Шестьдесят секунд

pip install "groundtruth-mcp[mcp]"

git clone https://github.com/ZhenGtai123/groundtruth-mcp && cd groundtruth-mcp
groundtruth --config examples/checkout-flow/groundtruth.toml lint broken_checkout

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

broken_checkout: BLOCKED  errors=6 warnings=1 infos=0
source: flows\broken_checkout.json

-- ERRORS — these block (6) --
[DANGLING_TRANSITION] states[1].transitions[0].to  'payment_methd' does not name any states.id
    fix: point it at an existing state id, or delete the transition
[DEAD_END] states[6]  'review_hold' has no outgoing edge and is not marked terminal — a run that arrives here stops with no result
    fix: give it a transition, or mark it kind = "terminal" with an outcome
[DUPLICATE_STATE] states[2]  duplicate id='shipping' (first declared at states[1])
    fix: rename one of them; the engine silently uses the first and ignores the rest
[RATE_OUT_OF_RANGE] policy.gateway_failure_rate  1.4 is above the maximum 1.0
    fix: this is a probability, not a percentage — 0.18, not 18
[RETRY_BUDGET_TOO_THIN] policy.max_retries  140% gateway failure with 1 retries leaves 196.0% of checkouts failing on payment alone (budget: 2.0%)
    fix: raise max_retries, or lower gateway_failure_rate if the gateway improved
[UNKNOWN_STATE_KIND] states[3].kind  'stage' is not one of ['step', 'gateway', 'retry', 'terminal']
    fix: the engine only knows these four kinds; anything else is treated as a plain step

-- WARNINGS (1) --
[UNREACHABLE_STATE] states[4]  'gift_wrap' cannot be reached from 'cart_review'
    fix: no path from start reaches this state — delete it, or wire it in

Шесть из них берутся из файла правил. RETRY_BUDGET_TOO_THIN возникает из восьми строк Python, потому что «соответствует ли этот бюджет повторов целевому показателю отказов продукта» — это арифметика, а не схема.

Теперь посмотрите на один запуск:

groundtruth --config examples/checkout-flow/groundtruth.toml replay standard_checkout --seed 3
standard_checkout  seed=3  outcome=success  steps=7  fingerprint=52b66a2024a61b5d
metrics: latency_ms=2506  payment_attempts=2  steps=7

-- TRACE --
  0. cart_review --always-->
  1. shipping --always-->
  2. payment_method --always-->
  3. authorize --failure-->  # attempt 1 declined
  4. retry_decision --retries_left-->  # 0 retry(s) used of 2
  5. authorize --success-->  # attempt 2 authorized
  6. confirmed  # terminal: success

Сид 3 всегда порождает эти семь шагов — на вашей машине, в CI, в следующем году. Именно поэтому его стоит читать.

И две тысячи таких:

groundtruth --config examples/checkout-flow/groundtruth.toml \
  simulate standard_checkout --runs 2000 --seed 0 --gate --check-determinism
standard_checkout: PASS  runs=2000  base_seed=0  fingerprint=449e16b50c8184c0

-- OUTCOMES --
  success: 1767 (88.3%)
  abandoned: 227 (11.3%)
  payment_failed: 6 (0.3%)

-- METRICS (mean / p50 / p95 / max) --
  latency_ms: 1587.75 / 1553 / 2566 / 3626
  payment_attempts: 1.06 / 1 / 2 / 3
  steps: 5.12 / 5 / 7 / 10

-- THRESHOLDS --
  PASS  rate:success = 0.8835  expected >= 0.8  (below this, the flow is losing customers faster than the business case allows)
  PASS  rate:stuck = 0  expected <= 0  (a run with nowhere to go is always a config bug, never bad luck)
  PASS  p95:latency_ms = 2566  expected <= 4000  (95th-percentile checkout wall time, retries included)
  PASS  mean:payment_attempts = 1.0585  expected <= 1.6  (rising attempts mean the gateway is degrading or the retry policy is too eager)

note: determinism: 20 seeds re-ran identically

Часть, которая окупает себя

Поднимите одно число — shipping.abandon_chance с 0.05 до 0.28, правку такого рода, которая выглядит как продуктовое изменение и проходит ревью:

$ groundtruth lint standard_checkout
standard_checkout: OK  errors=0 warnings=0 infos=0     # exit 0

$ groundtruth simulate standard_checkout --runs 2000 --seed 0 --gate
standard_checkout: FAIL  runs=2000  base_seed=0  fingerprint=5a7c0d9feed5adca

-- OUTCOMES --
  success: 1336 (66.8%)
  abandoned: 660 (33.0%)

-- THRESHOLDS --
  FAIL  rate:success = 0.668  expected >= 0.8
  PASS  rate:stuck = 0  expected <= 0
  PASS  p95:latency_ms = 2549  expected <= 4000
  PASS  mean:payment_attempts = 0.795  expected <= 1.6
                                                       # exit 1

Структурно безупречно. Двадцать один пункт конверсии потерян. Ни схема, ни система типов, ни ревью кода этого не ловят; сидированный батч с объявленным диапазоном ловит это за четыре секунды, в пулл-реквесте, до того как человек прочитает дифф.

Это работает и в обратную сторону. express_checkout показывает более высокий процент успеха, чем стандартный поток — 91.0% — и при этом является худшим конфигом: его платёжные сбои составляют 3.9% против 0.3%, скрываясь внутри заголовочного числа, которое выглядит нормально. Агрегат это упускает; написанный вручную валидатор говорит об этом прямо:

[RETRY_BUDGET_TOO_THIN] policy.max_retries  18% gateway failure with 1 retries
leaves 3.2% of checkouts failing on payment alone (budget: 2.0%)

Ни один слой не поглощает другой. Вот почему их два.

Внедрение

Один модуль, один файл конфигурации. examples/checkout-flow/groundtruth_app.py — это весь шаблон, около сотни строк, включая комментарии.

from groundtruth_mcp import Context, Issue, Loaded, Toolkit, Trace

kit = Toolkit(name="my-project", subject_noun="pipeline")

@kit.loader
def load(name: str):
    path = CONFIG_DIR / f"{name}.yaml"
    if not path.is_file():
        return None                        # → "no pipeline named X; available: ..."
    return Loaded(subject=parse(path), source=str(path))

@kit.validator
def check(pipeline, ctx: Context) -> list[Issue]:
    ...                                    # the checks a rule file can't express

@kit.runner
def run_once(pipeline, seed: int, ctx: Context) -> Trace:
    ...                                    # one run, pure in (pipeline, seed)

Один только @kit.runner даёт вам и replay, и simulate — библиотека запускает его по одному разу на каждый сид и сохраняет результат. Всё остальное (пакетирование сидов, агрегация, процентили, проверка порогов, бюджетирование вывода, формулировка ошибок, MCP-поверхность) поставляется пакетом.

# groundtruth.toml
[project]
toolkit = "groundtruth_app:kit"

[lint]
rules = "rules.toml"

[[thresholds]]
metric = "rate:success"
min = 0.80
note = "why this number, for whoever has to change it"

Затем groundtruth doctor сообщает, что подключено, groundtruth serve передаёт инструменты агенту, а groundtruth simulate --gate блокирует слияние. Полное пошаговое руководство с примерами по каждой предметной области: docs/ADOPTION.md.

Правила, которые вы получаете бесплатно

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

Тип

Ловит

Ключевые поля

required_fields

недописанные записи

select, fields

unique_key

дублирующиеся идентификаторы, которые движок молча затеняет

select, key

enum

значение, которое ваш движок не обрабатывает

select, values

type

строка там, где должно быть число

select, expect

range

1.4 в поле, которое является вероятностью

select, min, max

pattern

идентификаторы, нарушающие контракт именования

select, regex

not_empty

пустой список там, где требуется одна запись

select

ref_exists

ссылка на то, что было переименовано

select, collection, key

reachable

узел, до которого не ведёт ни один путь от начала

collection, key, edges, start

no_dead_end

нетерминальный узел без выхода

collection, key, edges, terminal_field

no_self_loop

узел, который переходит сам в себя

collection, key, edges

no_cycle

кольцо без выхода (со списком allow для намеренных)

collection, key, edges

Селекторы — это намеренно небольшой язык путей — states[].transitions[].to — и каждое совпадение сообщает конкретный путь, по которому оно найдено, что делает возможным states[3].transitions[1].to вместо «переход недействителен».

Каждое правило принимает необязательные code, severity и hint. Подсказка (hint) — это предложение, на которое действует агент, поэтому пишите его в повелительном наклонении.

Только чтение означает только чтение

query выполняет один SELECT. Это обеспечивают два уровня, и они не равны.

Сканирование ключевых слов — это пользовательский опыт: оно отклоняет DELETE FROM … предложением с объяснением, вместо ошибки базы данных, которую модели приходится расшифровывать. Это не граница — чёрный список по тексту всегда в одном случае от ошибки, и каноническая демонстрация — SELECT * INTO audit_copy FROM users, который начинается с SELECT, не содержит запрещённых глаголов и создаёт таблицу.

Граница — это хранилище данных: mode=ro плюс PRAGMA query_only в SQLite, транзакция READ ONLY в PostgreSQL, таймаут запроса в обоих. Тесты полностью обходят защиту и подтверждают, что соединение по-прежнему отказывает.

Маскирование столбцов — это единственный контроль на текстовом уровне, который является принудительным: значения в deny_columns отбрасываются после выборки и до того, как создаётся результирующая строка, поэтому SELECT * не может их утечь. Всё возвращаемое оборачивается в теги <untrusted>, потому что столбец notes, содержащий нечто похожее на инструкцию, — это данные, и он должен прийти помеченным как данные.

CLI

groundtruth [--config PATH] <command>

  doctor                     what is wired up, what is missing
  targets                    the configs this project exposes
  lint TARGET                exit 1 on errors
  replay TARGET --seed N     one deterministic run, full trace
  simulate TARGET            --runs N --seed N --gate --check-determinism
  query "SELECT ..."         one read-only statement
  schema                     readable tables and columns
  serve                      the MCP server, over stdio

Коды выхода: 0 — чисто, 1 — найдены проблемы (ошибки линтера, порог вне своего диапазона, недетерминизм), 2 — не удалось запустить (плохой конфиг, отсутствующая возможность, отклонённый запрос). Добавьте --json к lint, replay и simulate для машиночитаемого вывода.

Установка

pip install groundtruth-mcp          # core: rules, simulation, gating, CLI
pip install "groundtruth-mcp[mcp]"   # + the MCP server
pip install "groundtruth-mcp[postgres]"  # + the PostgreSQL data source

Python 3.11+. Ядро не имеет сторонних зависимостей — это сделано намеренно, чтобы CI-шлюз не зависел от стека агента. Голый раннер может обеспечивать соблюдение ваших порогов без установки SDK.

Проверка подключения

python scripts/mcp_smoke.py [path/to/groundtruth.toml]

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

Что CI проверяет в каждом пулл-реквесте

Не значок, означающий «тесты прошли», — шесть вещей, каждая из которых однажды что-то заблокировала:

Проверка

Почему это шлюз, а не предложение

ruff check + ruff format --check

Включая BLE, поэтому каждый широкий except содержит письменное обоснование

mypy

Пакет поставляется с py.typed; неверная аннотация — это неверный API

pytest на 3.11 / 3.12 / 3.13

69 тестов, нижняя граница покрытия 75% (сейчас 78% с покрытием ветвлений)

scripts/mcp_smoke.py

Настоящий подпроцесс, настоящий stdio, настоящие tools/list и tools/call

simulate --gate --check-determinism

Собственный аргумент проекта, применённый к самому себе

lint broken_checkout должен завершиться с кодом 1

Линтер, который не может упасть, декоративен

Ограничения, изложенные прямо

  • Белый список таблиц SQL является текстовым. Он сканирует идентификаторы после FROM и JOIN. Настоящее обеспечение по таблицам — это грант базы данных; это защитное ограждение с хорошим сообщением об ошибке, а удерживает на самом деле транзакция только для чтения.

  • Чёрный список ключевых слов срабатывает внутри строковых литералов. Запрос, фильтрующий по значению, содержащему grant, будет отклонён. Исправление требует настоящего SQL-парсера, который не стоит строить, когда парсер не является границей.

  • Авто-LIMIT — это эвристика. LIMIT внутри подзапроса подавляет добавление на верхнем уровне. max_rows по-прежнему ограничивает то, что выводится.

  • Селекторы не фильтруют. states[].transitions[] обходит всё; нет states[kind=terminal]. Язык предикатов был бы третьей функцией, которую никто не просил. Вместо этого напишите @kit.validator.

  • Пороги действуют на весь проект, а не на каждую цель. Каждая цель в проекте оценивается по одним и тем же диапазонам. Проекты, чьи конфиги требуют действительно разных диапазонов, должны использовать отдельные файлы groundtruth.toml.

  • Источник PostgreSQL реализован, но слабо протестирован — набор тестов проверяет границу на SQLite, где он может работать везде без служебного контейнера.

Откуда это взялось

Извлечено из приватной кодовой базы, где этот паттерн заслужил своё место: конвейер создания контента, участники которого постоянно поставляли конфигурации, проходившие валидацию схемы и ломавшиеся во время выполнения. Домен-специфичные части остались в стороне. Обобщению подверглась форма — проверка, воспроизведение, симуляция, запрос — а также набор решений, которые оказались важнее, чем список функций:

  • Единый список пороговых значений, читаемый и агентом, и CI, потому что две копии рассинхронизировались, и инструмент какое-то время выдавал PASS для чисел, которые CI отверг бы.

  • Ошибки, которые прямо называют допустимые альтернативы, потому что агент, которому нужно сделать второй вызов, чтобы узнать, что ему можно передать, вместо этого будет угадывать.

  • Описания инструментов, формируемые из текущей конфигурации, потому что устаревшее описание — это инструмент, которым агент пользуется уверенно, но неправильно.

  • Ограничение объёма вывода на каждом пути, потому что один слишком активный запрос может вытеснить остальную часть разговора.

docs/ARCHITECTURE.md содержит карту модулей и полное обоснование.

Лицензия

MIT.

-
license - not tested
-
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • A paid remote MCP for OpenAI Codex agent coordination MCP, built to return verdicts, receipts, usage

  • Free public MCP for AI agents — 193 tools, 44 workflows. No API key.

  • Agent Replay Debugger MCP — record every agent step + deterministic replay. Step-debugger for

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ZhenGtai123/groundtruth-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server