Агент-инженер по репозиторию · Модуль 5 · Урок 5.4
Eval завершения задачи и детерминистские тесты с моком модели
Зачем (какую проблему чиним)
Два вопроса, без которых агента нельзя катить: (1) решена ли задача на самом деле (не «зелёные ли тесты», а закрыт ли issue по сути)? (2) как тестировать самого агента, если он недетерминирован из-за модели? Ответы — task-completion eval и детерминистские тесты с моком модели.
Решение и альтернативы
Решение для (1): eval завершения задачи проверяет объективные сигналы — зелёный verify и наличие нового теста, воспроизводящего исходную проблему (если задача — баг), и соответствие диффа плану. Где нужен смысловой вердикт — LLM-as-judge офлайн на отдельном наборе задач, не на проде.
Решение для (2): за anthropic-sdk-go ставим интерфейс модели Model; в тестах подменяем его скриптованным моком, который выдаёт заранее заданную последовательность ответов (tool_use → текст). Тогда петли агента (диспетчеризация, self-correction, гейт) тестируются детерминированно и без сети.
Альтернативы: считать задачу решённой по зелёным тестам — тесты могут не покрывать суть; тестить агента живой моделью — флаки, дорого, недетерминированно. Интерфейс + мок — стандарт индустрии.
DIFF
Вводим интерфейс Model (адаптер к anthropic-sdk-go + мок), и task-completion eval поверх объективных сигналов.
⚠ Безопасность
Eval — последний рубеж перед человеком: он ловит «зелёные тесты, но задача не решена» и регрессии. Детерминистские тесты с моком — это и безопасность процесса: критичные инварианты (никогда не main, нет auto-merge, гейт перед MR) покрываются юнит-тестами, которые ловят регрессию инвариантов до прода.
Проверка
go test ./... гоняет сценарии агента на моке: «баг → MR с новым тестом», «нельзя открыть MR на красном», «нет вызова merge», «запись на main отвергается». Все — детерминированы, без сети и без ключа.
Глубже
Eval-датасеты, LLM-as-judge, регрессии, детерминизм тестов агента — курс «Продакшн-разработка», Модуль 5 (гл. 9).
+package main
+
+import "context"
+
+// Model — узкий интерфейс над провайдером. Прод-адаптер оборачивает
+// anthropic-sdk-go; в тестах используется mockModel.
+type Model interface {
+ Next(ctx context.Context, msgs []Message) (Reply, error)
+}
+
+// mockModel выдаёт заранее заданную последовательность ответов — петли агента
+// (диспетчеризация, self-correction, гейт) тестируются детерминированно, без сети.
+type mockModel struct {
+ script []Reply
+ i int
+}
+
+func (m *mockModel) Next(ctx context.Context, _ []Message) (Reply, error) {
+ r := m.script[m.i]
+ m.i++
+ return r, nil
+}+func TestNoMROnRedVerify(t *testing.T) {
+ model := &mockModel{script: []Reply{
+ toolUse("edit_file", editBreakingTests()), // правка, ломающая тесты
+ text("готово"), // модель «считает» задачу готовой
+ }}
+ ag := newAgentWith(model, fakeForge)
+ _ = ag.RunTask(context.Background(), bugTask)
+ if fakeForge.created {
+ t.Fatal("MR не должен открываться при красном verify")
+ }
+}Anti-patterns
| Грабля | Почему плохо | Как правильно |
|---|---|---|
| Считать задачу решённой = зелёные тесты | Тесты могут не покрывать суть issue; «зелено, но не решено» | Eval по объективным сигналам: verify + новый тест на проблему + дифф по плану; LLM-judge офлайн |
| Тестировать агента живой моделью | Флаки, дорого, недетерминированно; CI нестабилен | Интерфейс Model + скриптованный мок: детерминизм без сети |
| Не покрывать инварианты безопасности тестами | Регрессия (запись в main, auto-merge) проскакивает в прод незаметно | Юнит-тесты на инварианты: не main, нет merge, гейт перед MR |
Практическое задание (RA-v4)
- Ввести интерфейс
Modelс прод-адаптером (anthropic-sdk-go) иmockModelдля тестов. - Написать task-completion eval (verify + новый тест на проблему + соответствие диффа плану) и юнит-тесты инвариантов.
- Закоммитить:
git commit -m "v4: model interface, mock, task-completion eval".
Проверка знаний
Как детерминированно протестировать петли агента, не завися от недетерминированной живой модели?
Верный ответ: B
B. Интерфейс модели + мок дают полную воспроизводимость: тест задаёт точную последовательность ответов (tool_use/текст) и проверяет поведение петель и инвариантов без сети и ключа. Повторные прогоны (A) и temperature=0 (C) не дают настоящего детерминизма и стоят денег.
Почему «зелёные тесты» — недостаточный критерий того, что задача (issue) решена?
Верный ответ: B
B. Зелёные тесты говорят лишь, что не сломано покрытое. Решена ли именно задача — отдельный вопрос: для бага нужен новый тест, падавший до правки и проходящий после, плюс соответствие изменения заявленному плану. Это и проверяет task-completion eval.