Кейсовое исследование продукта · 30 октября 2026
Gemini CLI Headless JSON: Валидируйте перед автоматизацией
Захватывайте вывод и ошибки Gemini CLI отдельно, проверяйте его JSON-оболочку и отклоняйте неправильный ответ перед передачей другому скрипту.

Рассматривайте статус процесса, формат ответа и правильность ответа как три отдельных проверки перед принятием автоматизированного результата.
Скрипт может получать валидный JSON от агента ИИ и при этом принимать неправильный ответ. Эта лабораторная работа предоставляет три контрольные точки: команда выполнена успешно, вернулся ожидаемый формат и соответствует ли ответ представленным фактам?
Вы будете подавать в Gemini CLI фиктивный журнал релизов, сохранять исходный ответ и проверять небольшой ответ перед тем, как его использует другая программа. Полезным артефактом является повторяемый критерий приёмки с доказательствами неудачи.
Источники проверены 30 октября 2026 года. Это упражнение с документационным обоснованием. Мы тестировали локальный валидатор с синтетическими ответами; мы не выполняли аутентифицированный запрос в Gemini. Описанный здесь инференс модели является целью приёмки, а не зафиксированным результатом.
Для финального шага модели вам нужны Bash, Python 3 и установленный аутентифицированный Gemini CLI. Вы можете выполнить упражнение валидатора без учетных данных. Используйте только приведённые ниже фиктивные данные.
Понимание двух JSON-слоёв
Google безголовый эталон documents --output-format json как один объект с response строкой, статистикой использования в stats, и дополнительные сведения об ошибке. Этот внешний объект является конвертом ответа CLI. Запрос модели ответить в формате JSON не превращает эту строку в гарантированную схему приложения.
Наш собственный контракт ответа — это объект с ровно двумя полями: failed_count, целое число, и failed_checks, массив имен. Валидатор будет разбирать оба уровня и сравнивать ответ с известным результатом этой лаборатории. Этот контракт создан для упражнения; это не функция API Gemini.
Настройте одно временное рабочее пространство
Держите этот терминал Bash открытым для упражнения:
lab_root=$(mktemp -d /tmp/gemini-json-lab.XXXXXX)
mkdir -p "$lab_root/project "$lab_root/evidence
cd "$lab_root/project
кот > checks.txt <<'LOG'
unit-tests: PASS
link-audit: FAIL
content-schema: PASS
image-audit: FAIL
LOG
printf 'Корень лаборатории: %s\n' "$lab_root"
Правильным результатом являются две неудачные проверки: link-audit и image-audit. Сохраняйте напечатанный путь. Если вы меняете терминалы, установите lab_root в этот путь и вернитесь в его project каталог.
Сохраните следующее как validate.py в этом каталоге:
импорт json
импорт система
из pathlib импорт Путь
определённо проверить(raw):
envelope = json.loads(raw)
если не Isinstance(envelope, дикт):
повышать ValueError("CLI envelope должен быть объектом")
если envelope.get(«ошибка») является не Нет:
повышать ValueError("CLI сообщила об ошибке")
ответ = envelope.get("response")
если не Isinstance(ответ, str) или не response.strip():
повышать ValueError("Отсутствует непустая строка ответа")
ответ = json.loads(response)
если не Isinstance(answer, дикт):
повышать ValueError("Ответ должен быть объектом")
если набор(answer) != {"failed_count", "failed_checks"}:
повышать ValueError("Неожиданные поля ответа")
подсчитать = ответ["failed_count"]
проверки = ответ["failed_checks"]
если тип(подсчет) является не int или подсчитать != 2:
повышать ValueError("Ожидаемое целое число failed_count равно 2")
если не Isinstance(проверки, список) или не все(
Isinstance(элемент, str) для элемент в проверки
):
повышать ValueError("failed_checks должен быть массивом строк")
если длина(проверки) != 2 или набор(проверки) != {"ссылка-аудит", "аудит-изображений"}:
повышать ValueError("Имена неудачных проверок не совпадают с фикстурой")
вернуть ответ
если __имя__ == "__main__":
попытка:
результат = validate(Path(sys.argv[1]).read_text(кодировка="utf-8"))
кроме (IndexError, OSError, ValueError) как исключение:
print(ф"ОТКАЗ: {искл}", файл=sys.stderr)
sys.exit(1)
print(json.dumps(result, sort_keys=Истина))
Это намеренно отклоняет ограждения Markdown и дополнительные комментарии внутри response. Не удаляйте тихо неожиданный вывод, чтобы проверка прошла. Запишите несовпадение, чтобы вы могли решить, нужно ли менять подсказку или потребителя. Валидатор допускает дополнительные поля внешнего конверта, потому что CLI может включать метаданные, не относящиеся к ответу.
Доказать пути отклонения без вызова модели
Запустите это из той же директории:
python3 - <<'PY'
import json
from validate import validate
expected = {"failed_count": 2, "failed_checks": ["link-audit", "image-audit"]}
good = json.dumps({"response": json.dumps(expected)})
assert validate(good) == expected
bad_cases = {
"сломанный конверт": "{",
"отсутствует ответ": "{}",
"ошибка CLI": json.dumps({"error": {"message": "синтетическая ошибка"}}),
"обычный текстовый ответ": json.dumps({"response": "Две проверки не прошли."}),
"неправильное количество": json.dumps({"response": json.dumps({**expected, "failed_count": 0})}),
"неправильные имена": json.dumps({"response": json.dumps({**expected, "failed_checks": ["unit-tests", "image-audit"]})}),
}
для name, raw в bad_cases.items():
попытка:
validate(raw)
кроме ValueError:
print("ПРОЙДЕНО: отклонено", name)
иначе:
raise SystemExit("НЕУДАЧА: принято " + name)
print("ПРОЙДЕНО: принято правильное синтетическое решение")
PY
Ожидайте шесть сообщений об отказе и одно сообщение о принятии. Это тесты вашего Python-шлюза, а не доказательство того, что Gemini генерирует какие-либо из этих ответов. Фиксированный ожидаемый ответ делает это небольшим упражнением по регрессии; настоящий процессор логов потребовал бы независимого способа получения ожидаемых фактов.
Зафиксируйте один безголовый запрос
Запишите установленный интерфейс перед выполнением запроса:
gemini --version > "$lab_root/evidence/cli-version.txt
gemini --помощь > "$lab_root/evidence/cli-help.txt
Проверьте, что список помощи --prompt и --output-formatGoogle’s ссылка на конфигурацию документирует эти параметры. Если в вашей установке они отсутствуют, сначала устраните это несоответствие.
С Руководство по автоматизации документирует подачу ввода через stdin вместе с подсказкой. Здесь мы используем перенаправление ввода и отдельные выходные файлы:
если gemini --подсказка 'Используйте только предоставленный фиктивный журнал. Не используйте инструменты. Возвращайте только объект JSON с failed_count (целое число) и failed_checks (массив точных названий, статус которых FAIL). Без Markdown или других полей.' \
--формат-вывода json \
< checks.txt \
> "$lab_root/evidence/raw.json" \
2> "$lab_root/evidence/stderr.txt"; тогда
cli_status=0
иначе
cli_status=$?
fi
printf '%s
' "$cli_status" > "$lab_root/evidence/exit-status.txt"
С if сохраняет код завершения неудачной команды даже в оболочке, используя set -e. Разделение stderr предотвращает смешивание диагностических данных с JSON-файлом. Избегайте прямой передачи в парсер при первом запуске: вам нужен оригинальный вывод и статус команды, если что-то не удается.
Сохраняйте ваши обычные настройки разрешений. Запрос без инструментов — это инструкция, а не ограничение доступа; безголовый режим сам по себе не изолирует вашу файловую систему. Если запрос требует неожиданного разрешения, исследуйте причину, вместо того чтобы включать автоматическое одобрение для обхода ошибки.
Принимайте ответ только после прохождения всех проверок
если [ "$cli_status" -eq 0 ]; тогда
если python3 validate.py "$lab_root/evidence/raw.json" \
> "$lab_root/evidence/accepted.json \
2> "$lab_root/evidence/validation-error.txt; тогда
printf 'PASS: accepted.json соответствует этому образцу.
'
иначе
printf 'REJECT: проверьте validation-error.txt и raw.json.
'
fi
иначе
printf 'REJECT: CLI завершился с кодом %s; проверьте stderr.txt.
' "$cli_status"
fi
Не следует считать наличие accepted.json как успех: перенаправление оболочки может создать пустой файл до того, как проверка не пройдёт. Только ветка успешной проверки разрешает выполнение следующего шага. Это упражнение останавливается на печати сообщения.
| Симптом | Что проверить далее |
|---|---|
| CLI завершился с ненулевым кодом | Прочтите сохранённый статус и stderr; устраните ошибки аутентификации, аргументов или сервиса перед очередной попыткой. |
| Некорректный внешний JSON | Проверьте исходные байты на наличие пустого или усечённого вывода, диагностических сообщений или неверного формата вывода. |
| Внешний JSON парсится, но ответ неверен | Проверить error, затем проверьте, является ли response обычным текстом, ограниченным JSON или имеет другую форму. |
| Допустимая форма, неверное количество или имена | Зафиксируйте фактический сбой. Не ослабляйте ожидаемый ответ, чтобы его принять. |
Сохраняйте каждую новую попытку в отдельной директории с доказательствами, чтобы сохранить первый сбой. Опорная документация Google по безголовому режиму различает json из stream-json, который генерирует отдельные события. Этот валидатор предназначен только для формата с одним объектом; смена формата требует другого потребителя.
Для вашей следующей задачи напишите ожидаемые факты и поведение при сбое перед тем, как попросить агента дать ответ. урок проверки принятия помогает вам выбрать, что тестировать. Сохраняйте необработанные доказательства и позволяйте проходить проверку только конкретное действие, которое она реально проверяет.
Источники проверены 30 октября 2026 г.
- Справка по безголовому режиму: конверт ответа, форматы вывода и поведение при завершении процесса.
- Автоматизируйте задачи с безголовым режимом: подсказки и stdin для скриптовых задач.
- Справка по конфигурации: ключи командной строки и сравнение установленного интерфейса.
Запустите это в работу
Постройте ворота принятия для небольшой задачи безголового агента.
Попробовать
Попросите Gemini сообщить о неудачных проверках в фиктивном журнале выпусков.
Докажите, что это сработало
Отклоняйте некорректный вывод и неправильное количество с использованием синтетических фикстур.
Держите в поле зрения
- Держите stdout, stderr и статус выхода команды как отдельные доказательства.
- Различайте оболочку CLI JSON и ответ модели внутри нее.
- Тестируйте пути отклонения локально перед запуском запроса к модели.