Агент-инженер по репозиторию · Модуль 1 · Урок 1.4

Чекпойнт v0: агент отвечает на вопрос о файле

Зачем (какую проблему чиним)

Собираем три предыдущих шага в первый рабочий запускаемый агент. Цель версии v0 достигнута: агент принимает вопрос, при необходимости читает файлы через read_file и отвечает по сути. Это база, на которую будут наслаиваться все следующие версии.

Решение и альтернативы

Решение: добавляем системный промпт (роль и границы агента), CLI-вход для вопроса и понятный вывод хода (какие инструменты вызваны). Системный промпт сразу фиксирует инвариант безопасности v0: только чтение, никаких изменений. Фиксируем версию тегом v0.

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

DIFF

Системный промпт, CLI и финальная сборка цикла.

⚠ Безопасность

Системный промпт — часть периметра: явно запрещаем агенту утверждать, что он что-то изменил, и любые действия записи (их ещё нет в коде, но модель не должна «фантазировать» о них). Тег v0 — первая точка отката: всё дальнейшее можно сравнить с этим заведомо безопасным состоянием.

Проверка

ANTHROPIC_API_KEY=... go run . "Опиши, что делает функция safeJoin" — агент находит файл, читает, отвечает. Затем git tag v0 — рабочий чекпойнт версии.

Глубже

Системные промпты и роль агента — курс «Разработка ИИ-агентов»; границы и least privilege как принцип — курс «Продакшн-разработка», Модуль 5 (безопасность, гл. 11).

main.go: системный промпт и CLI; финальная сборка версии v0
 func (a *Agent) callModel(ctx context.Context, msgs []anthropic.MessageParam) (*anthropic.Message, error) {
 	return a.client.Messages.New(ctx, anthropic.MessageNewParams{
 		Model:     anthropic.Model(a.cfg.Model),
 		MaxTokens: int64(a.cfg.MaxTokens),
 		Messages:  msgs,
+		System: []anthropic.TextBlockParam{{
+			Text: "Ты — агент-инженер по репозиторию. Версия v0: ты можешь только " +
+				"ЧИТАТЬ файлы инструментом read_file. Ты НЕ изменяешь файлы и не " +
+				"утверждаешь, что что-то изменил. Отвечай кратко и по делу.",
+		}},
 		Tools: []anthropic.ToolUnionParam{
 			{OfTool: &[]anthropic.ToolParam{readFileTool()}[0]},
 		},
 	})
 }

-func main() {
-	out, err := runAgent(context.Background(), "Что делает функция main в main.go?")
-	if err != nil {
-		fmt.Println("ошибка:", err)
-		return
-	}
-	fmt.Println(out)
-}
+func main() {
+	if len(os.Args) < 2 {
+		fmt.Println("использование: repo-agent \"вопрос о репозитории\"")
+		os.Exit(2)
+	}
+	cfg := loadConfig()
+	cfg.Root, _ = os.Getwd() // корень анализируемого репозитория
+	agent := newAgent(cfg)
+	out, err := agent.Run(context.Background(), os.Args[1])
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "ошибка:", err)
+		os.Exit(1)
+	}
+	fmt.Println(out)
+}

Anti-patterns

Грабли первого чекпойнта
ГрабляПочему плохоКак правильно
Тег v0 на нерабочем/несобирающемся состоянииТег обещает обратимость, но откат ведёт в сломанное состояниеТегировать только зелёный чекпойнт: собирается и выполняет цель версии
Системный промпт без границ ролиМодель может «решить», что умеет писать файлы, и галлюцинировать действияЯвно прописать инварианты версии (только чтение) и запрет утверждать о несделанных действиях

Практическое задание (RA-v0)

  • Добавить системный промпт с инвариантом v0 (только чтение) и CLI-вход для вопроса.
  • Прогнать агента на реальном вопросе о файле; убедиться, что ход с read_file виден и ответ корректен.
  • Зафиксировать версию: git commit -m "v0: working read-only repo agent" и git tag v0.

Проверка знаний

Почему тег версии v0 ставится только на собирающийся и работающий чекпойнт, а не на любой коммит?

  • A Так требует git
  • B Тег версии — точка обратимости; откат на сломанное состояние обесценивает саму идею чекпойнта
  • C Теги ускоряют сборку
  • D Чтобы было больше тегов

Зачем в системном промпте версии v0 явно запрещать агенту утверждать, что он изменил файлы?

  • A Это экономит токены
  • B Чтобы модель не галлюцинировала несуществующие действия записи, которых на версии v0 ещё нет в коде
  • C Чтобы ответы были длиннее
  • D Это требование SDK