# Plata — конкретные темы сверх FAQ (расширенно)

Версия для чтения: короткие абзацы, каждая метрика = формула + смысл + когда врёт. Связано с `AI-SYSTEM-DESIGN-WEEK.md` (план недели) и `AI-SD-CARD.md` (одна страница на доску).

---

## 1. RAG: разворачивание, чанки, метрики, практики

### Что такое RAG на самом деле

RAG = Retrieval-Augmented Generation.

Это не библиотека и не «векторная база».

Это дизайн-паттерн: прежде чем LLM ответит, система находит релевантные куски внешних данных и подкладывает их в промпт.[39]

Так LLM не выдумывает ответ из памяти, а отвечает по тому, что реально нашлось.

Термин придуман в 2020 году в статье Lewis et al., и с тех пор это дефолтный способ дать модели доступ к знаниям компании.[39]

### Из чего состоит RAG-система

Любая RAG-система — это два независимых пайплайна, которые не стоит смешивать в один «ящик». Ошибки в них чинятся по-разному.[39][40]

Первый пайплайн — **ingestion** (оффлайн). Он работает один раз или по расписанию.

Второй пайплайн — **query** (онлайн). Он работает на каждый запрос пользователя.

Если у тебя на доске нарисован один прямоугольник «vector DB» — интервьюер поймёт, что продакшена ты не видел.

### Пайплайн ingestion по шагам

Идём по шагам, каждый шаг — отдельный блок на доске:

1. **Source of truth** — где живут оригинальные документы (политики, тарифы, FAQ). Это единственный источник, которому можно верить.
2. **ACL / tenant** — права доступа: какой пользователь вообще имеет право видеть этот документ. В банке это критично: политика для премиум-клиентов ≠ политика для всех.
3. **Чистка / parse** — извлечь текст из PDF/Office/HTML. Тут на помощь приходит Apache Tika (см. раздел 5).
4. **Chunking** — разрезать текст на куски (см. ниже, как именно).
5. **Embed + BM25** — каждый кусок превращается в вектор (для смыслового поиска) И добавляется в лексический индекс (для поиска по точным словам). Оба индекса живут рядом, это и есть «hybrid».
6. **Версия и doc_id** — каждому документу присваивается версия. Сменили политику «28 дней» на «30 дней» — старые куски нельзя отдавать.
7. **Index** — всё это складывается в хранилище (vector DB + inverted index).

Смысл шага 6 (версии) важнее, чем кажется: без версии ты не сможешь откатить ответы после плохого обновления политики.[40]

### Пайплайн query по шагам (пример — банк, каре-бот)

Идём по шагам, каждый шаг — потенциальное место поломки. Сквозной пример: клиент спрашивает «какая комиссия за перевод в другой банк?».

1. **Input guards** — проверить входящий запрос: PII (номер карты маскировать до провайдера и лога), prompt injection («игнорируй инструкции»), язык, длина.
2. **Rewrite / HyDE (опционально)** — переписать запрос для лучшего поиска: «а какая там комиссия, если перевести в другой банк?» → «comisión transferencia interbancaria SPEI». Только если eval доказал выигрыш, иначе лишняя латентность.
3. **Hybrid retrieve** — одновременно векторный поиск (смысл: «какой тариф на перевод» найдет «comisión por transferencia interbancaria») и BM25 (точные строки: «SPEI», «TS-999», номер договора, «1.5%»). Результаты объединяются.
4. **Metadata filter** — отфильтровать по правам (клиент не премиум → выкинуть премиум-куски), языку, продукту, версии документа. Это безопасность, не фича.
5. **Rerank** — маленькая модель (Cohere, Voyage) пересортировывает топ-20, оставляет топ-5. Модель обрабатывает меньше токенов — быстрее и дешевле.
6. **Prompt + cite** — собрать промпт: вопрос + куски с метками [1], [2] + требование цитировать. Цитата — механизм контроля: по ней проверяют citation accuracy.
7. **Generate T=0** — температура 0 на фактах: детерминированный ответ, без галлюцинаций. Креатив — не для комиссий.
8. **Output guards** — проверить ответ: цифра в ответе сверена с цифрой в контексте («2%» есть в куске [1]?), нет данных чужой карты, формат валиден (JSON).
9. **Лог chunk_ids** — записать request_id, chunk_ids, prompt_version, модель, токены, латентность.

Почему лог критичен — разделение вины: жалоба «бот ответил ерунду» → смотришь лог. Нужного chunk_id нет → виноват retrieval. Чанк был, ответ врёт → виновата генерация. Без лога chunk_ids чинишь вслепую.[40][39]

### Как делить на чанки

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

Главное правило: **граница смысла важнее числа токенов**.

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

Практические стратегии:

| Стратегия | Как работает | Когда брать |
|---|---|---|
| По заголовкам (structure-aware) | Режем по `##`, секциям, пунктам | FAQ банка, политики — лучший дефолт для Plata |
| Recursive split | Сначала по абзацам, потом по предложениям, с overlap | Проза без структуры |
| Parent–child | Маленький чанк ищется, большой — отдаётся модели | Когда ответу нужен широкий контекст |
| Semantic split | Режем там, где падает cosine между соседями | Смешанные топики в одном документе |
| Contextual retrieval | К чанку приписывается контекст документа | Чанк без «чьи это цифры» |

Что такое **overlap** в таблице: это перекрытие соседних чанков на N токенов, чтобы предложение на стыке не потерялось. Например, чанки по 400 токенов с overlap 50.

### Contextual Retrieval — почему все на него ссылаются

Проблема обычного RAG: чанк вырван из документа и сам по себе невнятный.

Пример из Anthropic: в чанке написано «The company's revenue grew by 3% over the previous quarter» — а какая это компания и какой квартал, неизвестно. По такому куску поиск не найдёт ответ про «ACME Q2 2023», даже если кусок правильный.[26]

Решение Anthropic (сентябрь 2024): перед эмбеддингом к каждому чанку LLM дописывает 50–100 токенов объяснения, откуда этот кусок.[26]

```
original: "The company's revenue grew by 3% over the previous quarter."
context:  "This chunk is from an SEC filing on ACME corp's performance in Q2 2023;
           the previous quarter's revenue was $314 million."
```

Тот же текст с контекстом кладётся и в эмбеддинг, и в BM25-индекс — оба индекса становятся умнее.

Цифры из их бенчмарка (метрика = доля неудачных retrieval, `1 − recall@20`):

- Baseline (embedding + BM25): 5.7% неудач.
- + Contextual embeddings и BM25: 2.9% (−49%).
- + Reranker (Cohere): 1.9% (−67%).

Важная деталь: они проверяли и другие подходы (добавление суммаризации документа к чанкам, HyDE, summary index) — и все дали слабый выигрыш по сравнению с contextual retrieval.[26]

Зачем это знать на собеседовании: когда интервьюер спросит «как улучшить retrieval» — это самый свежий, проверенный, с цифрами ответ.

### Зачем BM25, если есть эмбеддинги

Эмбеддинги понимают смысл, но не точные строки.

BM25 — это «старая» лексическая модель из поиска, наследник TF-IDF. Он ищет точное совпадение слов, с поправкой на длину документа и «насыщение» частоты: если слово встречается 10 раз, это не в 10 раз важнее, чем 1 раз.

Классический пример: запрос «Error code TS-999» в базе поддержки. Эмбеддинг найдёт материалы «про ошибки вообще», а BM25 найдёт именно строку «TS-999».[26]

Поэтому продакшен-дефолт — **hybrid**: BM25 ловит идентификаторы и имена полей, dense ловит перефразы. В банке это особенно важно: номера договоров, «NIP», лимиты — это точные строки.

### Когда RAG вообще не нужен

Если корпус меньше ~200 000 токенов (примерно 500 страниц) — можно вообще обойтись без RAG: положить весь корпус в промпт и пользоваться prompt caching. Anthropic прямо это пишут.[26]

Также RAG не нужен на простых задачах, где нет «знаний»: классификация интента, извлечение 4 полей, перефраз. Ретривер там добавляет только латентность.

И никогда не начинай с GraphRAG. Пока простой hybrid + rerank не доказал потолок на задачах с несколькими прыжками (multi-hop), граф — это оверинжиниринг. На Middle+ собеседовании заявить «у меня GraphRAG» без обоснования — минус.

### Метрики retrieval — что каждая значит и когда врёт

Retrieval оценивается только относительно **gold** — заранее размеченного «правильного» набора чанков для каждого вопроса. Без gold никакая retrieval-метрика не существует.[39]

Gold может быть двух видов:

- **chunk_id / doc_id** — «правильный ответ лежит в этом куске». Это дёшево: вручную отметить 50–100 пар вопрос → кусок.
- **reference text** — «правильный ответ написан так». Это дороже, и из него Ragas выводит relevance косвенно.

**Recall@k** — «из всех нужных кусков сколько попало в top-k».

Формула: `|relevant ∩ top-k| / |relevant|`.

Если нужный кусок вообще не нашёлся — recall 0, что бы ни говорила генерация. На собеседовании: recall отвечает на вопрос «система нашла материал?». Низкий recall = чини поиск, а не промпт.

**Precision@k** — «сколько из top-k кусков реально нужны».

Формула: `|relevant ∩ top-k| / k`.

Высокий recall при низком precision означает: нужное нашлось, но вокруг тонны мусора. Мусор в контексте — это дорого (токены) и опасно (модель может ответить по мусорному куску).

**Hit rate** — «хотя бы один нужный кусок попал в top-k?».

Формула: 1 если `|relevant ∩ top-k| ≥ 1`, иначе 0. Дешёвый бинарный сигнал «нашлись или нет». Хорош как первый фильтр в CI.

**MRR (Mean Reciprocal Rank)** — «насколько высоко стоит первый нужный кусок».

Формула: среднее по вопросам от `1 / rank_первого_релевантного`.

Если нужный кусок на позиции 1 — вклад 1.0. На позиции 3 — вклад 0.33. Наказывает за то, что нужное «внизу списка».

**nDCG@k (normalized Discounted Cumulative Gain)** — «насколько хорошо упорядочены все нужные куски, а не только первый».

Сначала DCG: сумма `rel_i / log2(i+1)` по позициям top-k, где rel_i — релевантность куска на позиции i (часто 1/0).

Потом nDCG = DCG / идеальный DCG (как если бы всё релевантное стояло первым). Нормализация нужна, потому что разные запросы имеют разное число релевантных кусков, и сырой DCG несопоставим.

MRR и nDCG — «ранжирование в целом». Если интервьюер спросит, чем они отличаются: MRR смотрит только на первый релевантный, nDCG — на весь порядок.

**ID-based recall** — частный случай recall@k, где релевантность определяется по идентификаторам кусков, а не по тексту.

Формула: `|gold_ids ∩ retrieved_ids| / |gold_ids|`.

Почему это лучший дешёвый тест для банка: размечать «правильный doc_id» быстро, не нужно писать референс-ответы, и метрика не зависит от LLM-судьи.

**Context Recall (Ragas)** — LLM-вариант recall без размеченных id.

Как считается: берётся reference (эталонный ответ), разбивается на утверждения (claims), и для каждого утверждения LLM отвечает «можно ли это вывести из найденного контекста». Считается доля утвердительных ответов.[37]

Формула: `число утверждений reference, выводимых из контекста / всего утверждений reference`.

Чем отличается от ID-recall: не требует разметки id, но зависит от LLM-судьи и потому шумнее и дороже.

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

### Метрики generation — что каждая значит

**Faithfulness (Ragas)** — «все ли утверждения ответа подтверждаются найденным контекстом».

Как считается: ответ разбивается на утверждения; каждое проверяется на выводимость из retrieved context; доля подтверждённых.[36]

Формула: `supported_claims / all_claims`.

Это не «ответ правильный» — это «ответ не придуман поверх контекста». Ответ «я не знаю» при пустом контексте может быть идеально faithful.

Когда врёт: если контекст сам по себе мусорный, faithfulness может быть высоким при полной бесполезности. Поэтому его всегда читают вместе с recall.

**Answer relevancy (Ragas)** — «ответ вообще про заданный вопрос?».

Как считается: LLM генерирует из ответа N обратных вопросов (по умолчанию 3), потом считается средний cosine между эмбеддингом исходного вопроса и эмбеддингами сгенерированных.[38]

Идея: если ответ реально отвечает на вопрос, то из ответа можно реконструировать вопрос.

Важно: relevancy не проверяет правду. «Париж — столица России» релевантно вопросу про столицу, но ложно. Поэтому relevancy читают вместе с faithfulness.

Когда врёт: cosine-эмбеддинги ловят лексику, а не факты. Ответ с другой терминологией может получить низкий балл, хотя отвечает по делу.

**Citation accuracy** — «ссылка ведёт на тот кусок, из которого реально взят факт».

Считается кодом + человеком: собрать пары (утверждение → chunk_id) и проверить, что chunk_id действительно поддерживает утверждение.

Это отдельная метрика, потому что «показали кусок» ≠ «процитировали кусок». Jason Liu прямо советует логировать и то и другое: shown (что показали модели) и cited (что модель процитировала). Расхождение — дешёвый детектор проблем.[40]

**Task success** — «задача бизнеса выполнена».

SQL исполнился и дал эталонный ответ, поле извлечено верно, тикет создан. Это не RAGAS-метрика, но именно она решает.

### Квадрант «где дыра» — обязателен на доске

Две метрики, четыре клетки:

| | recall низкий | recall высокий |
|---|---|---|
| **faith низкий** | мусор в индексе + модель додумывает | чанк был найден, но модель соврала → чини промпт / добавь отказ |
| **faith высокий** | модель честно молчит, знаний не хватает → чини retrieval | всё ок, смотри продукт и тон |

Фраза для интервью: «Я не смотрю на средний балл. Я смотрю, в каком квадранте дыра, и чиню один рычаг».[39]

### Best practices RAG, которые можно сказать на доске

1. Hybrid + rerank — раньше, чем смена эмбеддингов. Rerank дешевле переобучения индекса.[26]
2. Логируй: chunk_id, rewrite, shown vs cited, mean cosine, reranker score.[40]
3. Rewrite и HyDE — только после A/B на golden set. Иначе латентность в никуда.
4. Semantic cache в банке опасен: «дней отпуска» ≈ «дней больничного» по эмбеддингу, но это разные ответы.
5. ACL — на уровне чанка, а не после генерации.
6. Версия документа в метаданных: сменили «28 дней» на «30» — старый ответ нельзя отдавать.

**Читать:** Contextual Retrieval[26]; Evidently RAG eval[39]; Jason Liu Levels of RAG[40]; Ragas metrics[35].

---

## 2. Оркестрация / субагенты

### Словарь, который надо выучить

**Workflow** — система, где LLM и тулы соединены заранее написанными код-путями. Модель не решает, куда идти; код решает. Примеры: routing, chaining, parallel, vote.[29]

**Agent** — система, где модель сама решает, какие шаги делать и какие тулы звать. Код задаёт рамку, но не путь.[29]

**Субагент** — отдельный агент со своим контекстом, своими тулами и своим циклом «подумай → вызови тул → посмотри результат». Lead-агент спавнит субагентов и собирает их результаты.

**Orchestrator-workers** — паттерн: центральный агент (orchestrator) сам разбивает задачу на подзадачи и раздаёт их worker-ам. Ключевое отличие от обычной параллелизации — подзадачи не известны заранее, их определяет оркестратор на лету.[29]

**Handoff** — передача задачи от одного агента другому (в терминах OpenAI Agents SDK).[34]

### Когда НЕ делить на субагентов

Есть два принципа от Cognition (Devin), и они стоят того, чтобы их процитировать дословно по смыслу:[28]

**Принцип 1: «Делись контекстом, и делись полными трейсами, а не отдельными сообщениями».**

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

**Принцип 2: «Действия несут неявные решения, и конфликтующие решения дают плохой результат».**

Их пример: задача «собрать Flappy Bird» делится на «фон с трубами» и «птицу». Субагент 1 делает фон в стиле Super Mario. Субагент 2 делает птицу, которая не похожа на игру. Главный агент получает два несостыкованных куска и не может их склеить.[28]

Почему просто «скопировать задачу в контекст субагента» не решает: в продакшене контекст — это сотни ходов диалога и десятки тул-коллов; невозможно пересказать все неявные решения в брифе.

Вывод Cognition: по умолчанию исключай архитектуры, нарушающие эти принципы. Один линейный агент с непрерывным контекстом — самый надёжный дефолт.[28]

Пример-подтверждение: Claude Code на июнь 2025 спавнит субагентов, но они не работают параллельно с основным агентом и обычно только отвечают на вопрос (не пишут код). Причина — субагент не имеет контекста главного агента.[28]

Не делить, если:

- шаг B зависит от решения шага A (общий артефакт, общий стиль, общая схема);
- все пишут в одно состояние (тикет, карта, один файл);
- задача — каре-бот банка: 80% — FAQ и статус карты, это **router + RAG**, не рой агентов.[29][33]

### Когда делить ИМЕЕТ смысл

Anthropic Research — успешный кейс: lead-агент планирует, спавнит параллельных субагентов-исследователей, те жмут поиск и возвращают сжатый результат.[27]

Условия, при которых у них это сработало:

1. Задача **breadth-first**: много независимых направлений, которые можно искать одновременно. Пример: «найди всех членов советов IT-компаний из S&P 500» — 10 поисков параллельно, они не зависят друг от друга.
2. Работа не влезает в одно контекстное окно.
3. Субагент-исследователь — «интеллектуальный фильтр»: он сам ищет, сам решает, что важно, и возвращает только сжатый итог.
4. Результат субагента — артефакт (файл, запись в БД), а lead получает ссылку на него, а не простыню токенов через «испорченный телефон».

Что такое «game of telephone» в их тексте: когда субагент возвращает большой результат главному агенту, а тот пересказывает его дальше — информация искажается при каждой передаче. Решение — субагенты пишут результат в filesystem, а не пересказывают через контекст.[27]

Их внутренний результат: Opus 4 (lead) + Sonnet 4 (workers) на внутреннем research-eval **на 90.2% лучше**, чем один Opus 4.[27]

И важнейшая оговорка: **токены**. Агент жжёт ~4× токенов чата, multi-agent — ~15×. В их анализе BrowseComp token usage объяснял ~80% дисперсии качества, ещё тул-коллы и модель — вместе 95%. Субагенты работают, потому что «достаточно тратят токенов на решение».[27]

Вывод: multi-agent экономически оправдан только там, где ценность задачи платит 15× токенов, и где параллелизм реальный, а не кажущийся.

И ещё одно их признание: coding с общим состоянием — плохой фит для multi-agent сегодня, LLM пока плохо делегируют друг другу в реальном времени.[27]

### Как понять, что разделение дало смысл (протокол)

A/B на **одном и том же** golden set, два варианта: один агент vs lead+workers.

| Метрика | Ожидание при успехе сплита |
|---|---|
| Task success / pass^k | выросла на независимо-параллельных задачах |
| Coverage независимых осей | выросло (вот ради этого всё затевалось) |
| Конфликты артефактов | не выросли (если выросли — архитектура врёт) |
| Токены / $ / p95 latency | ожидаемо хуже, это плата |

Правило: если success не вырос, а $ вырос — субагентов выкидываем. Это не религия, это метрика.

### Метрики оркестрации по слоям

Оркестрацию нельзя оценить одной цифрой. Режь по компонентам:[32][27]

| Слой | Метрика | Как считается |
|---|---|---|
| Router | accuracy, confusion matrix | размеченный gold «какой скилл правильный» vs выбранный |
| План lead-агента | «план покрыл все оси запроса» | бинарный судья или чеклист по gold |
| Worker | task success по СВОЕМУ брифу | code / outcome |
| Интеграция | финальный outcome + отсутствие противоречий между результатами workers | SQL state / файл / цитаты |
| Процесс | turns, tool calls, tokens, spawn count | код. У Anthropic ранний агент спавнил 50 субагентов на простой запрос — ловится лимитом в промпте и этим логом[27] |
| Стабильность | pass^k — успех во всех k попытках | банку важнее, чем pass@k[32][42] |

Ключевое: **transcript ≠ outcome**. «Сказал, что тикет создан» ≠ «строка появилась в БД». Для агентов, меняющих состояние, суди по конечному состоянию, а не по словам.[32]

Начинай eval с ~20 реальных запросов, не жди сотни. Ранние правки промпта дают прыжки 30% → 80%, и их видно на 20 примерах.[27]

**Читать:** Anthropic multi-agent research[27]; Cognition Don’t Build Multi-Agents[28]; Building Effective Agents[29]; OpenAI Practical Guide[33].

---

## 3. Тулы: метрики полезности и осмысленности

### Два разных вопроса

Первый вопрос: **тул полезен системе?** Это про инструмент сам по себе.

Второй вопрос: **агент осмысленно его использует?** Это про ACI — качество интерфейса между моделью и инструментом.

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

### Метрики полезности тула (вопрос A)

**Ablation** — главный тест: выключи тул из набора, прогони golden set. Если success не упал — тул не нужен. Упал — вот его ценность. Это как удалить функцию и посмотреть, кто сломается.

**Unique contribution** — сколько раз тул был выбран И реально изменил outcome. Тул, который вызывается, но результат не влияет на ответ, — мёртвый груз.

**Error rate** — доля вызовов, вернувших ошибку (5xx, validation, неверные аргументы). Высокий error rate — либо плохой тул, либо модель не понимает его контракт. Различать — по ошибкам: 5xx = тул, wrong args = описание.

**Latency / token tax** — стоимость одного вызова. Anthropic советует трекать: runtime каждого вызова, общее число вызовов, токены, ошибки.[30]

Когда это говорит о проблеме: куча повторяющихся вызовов = надо укрупнить тул или починить пагинацию; много ошибок по параметрам = надо лучше описание и примеры.[30]

### Метрики осмысленности использования (вопрос B)

| Метрика | Что считает | Нужен gold? |
|---|---|---|
| Tool selection accuracy | правильный тул выбран | да |
| Tool-call F1 | precision/recall по множеству вызовов vs ожидаемых[35] | да |
| Argument validity | схема + enum + бизнес-инвариант (сумма > 0) | нет, код |
| Tool-call match | имя + параметры = ожидаемым | да |
| Unnecessary calls | вызов без изменения состояния / повтор | эвристика + человек |
| Outcome after tools | бронирование существует, не «я забронировал»[32] | БД |
| pass^k | стабильность пути тулов на политике[42] | да |

**Tool selection accuracy** — «выбрал ли правильный инструмент из набора». Считается как доля запросов, где выбранный тул совпал с размеченным правильным.

**Tool-call F1** — сочетание двух чисел. Precision: доля сделанных вызовов, которые были нужны. Recall: доля нужных вызовов, которые были сделаны. F1 = гармоническое среднее `2·P·R/(P+R)`. Наказывает и за лишние вызовы, и за пропущенные.

**Argument validity** — проверяется кодом без LLM: JSON-схема, enum допустимых значений, бизнес-инвариант вроде «сумма перевода > 0». Это первый и самый дешёвый слой проверки тулов.

**Unnecessary calls** — вызов, который не изменил состояние (повторный запрос баланса, поиск с тем же запросом). Ловится эвристикой + человеческой выборкой.

**Outcome after tools** — финальное состояние мира. Не «агент сказал, что забронировал», а есть ли реальная бронь в БД. Это самый честный сигнал.[32]

### τ-bench: бенчмарк, который полезно знать по имени

τ-bench (Sierra Research, 2024) — бенчмарк для tool-agent-user: агент с API-тулами домена + политика (policy guidelines) + симулированный пользователь-LLM.[42]

Домены: airline (авиаперелёты) и retail (ритейл). Позже появился τ²-bench с banking-доменом — прямо релевантно банку.

Ключевая метрика: **pass^k** — агент должен успешно завершить задачу во **всех k** независимых прогонах.

Отличие от pass@k: pass@k — «хотя бы в одном из k», pass^k — «во всех k». Для банковского ответа важна стабильность: один раз из пяти ответить правильно — недостаточно.

Цифры для примера: Claude 3.5 Sonnet (tool-calling) на airline: pass^1 = 0.46, но pass^4 = 0.225. То есть даже лучшая модель «собирает» задачу только в 46% попыток, а стабильно — в 22.5%.

Ещё полезное из τ-bench — таксономия ошибок, которую можно цитировать на собеседовании:[42]

- `used_wrong_tool` — вызвал не тот тул;
- `used_wrong_tool_argument` — тот тул, но неверный аргумент;
- `took_unintended_action` — действие, которое не просили (списал деньги без подтверждения);
- `goal_partially_completed` — задача выполнена частично.

### Чего НЕ мерить

Не мерить «число тул-коллов» как качество. Много вызовов может быть и отличной работой (собрал все источники), и спамом.

Anthropic в своём research-judge смотрел tool efficiency именно так: «правильные тулы, разумное число раз», а не «максимум вызовов».[27]

На доске для Plata-каре конкретный набор:

```
code:  schema(args), «цифры баланса только из get_balance»,
       не вызвал cancel_card без confirmation
judge: «выбрал FAQ вместо tool на вопрос про лимит» — binary
online: wrong-tool rate, unintended money-action = 0
```

---

## 4. Как строить тулы (ACI) и не потеряться в куче

### Что такое ACI

ACI = Agent-Computer Interface.

Обычный API проектируется для людей или других программ: REST, документация, статус-коды.

Тул для агента проектируется для LLM: описание, которое модель читает глазами, и схема параметров, которую она должна заполнить.

Anthropic прямо говорят: вкладывай в ACI столько же усилий, сколько в HCI (Human-Computer Interaction). На SWE-bench они потратили на оптимизацию тулов больше времени, чем на системный промпт.[29]

Их пример: модель путалась с relative file paths после перехода в другую директорию. Починили не промптом, а тулом: обязали absolute paths — ошибка исчезла полностью.[29]

### Контракт тула: 9 правил

**Правило 1. Имя + описание 3–4 предложения.** Что делает, когда звать, когда НЕ звать, что НЕ возвращает. Модель не знает твой код; описание — единственный мостик.[49]

**Правило 2. Параметры однозначные.** `user_id`, а не `user`. Неоднозначное имя параметра — гарантированная ошибка заполнения.[30]

**Правило 3. Namespacing.** `cards_get_balance`, `faq_search`, а не два тула с похожими именами. Чёткие границы функциональности.[30][49]

**Правило 4. Poka-yoke.** Японский термин «защита от дурака»: сделай так, чтобы ошибиться было трудно. Enum допустимых статусов, абсолютные пути, неотрицательные суммы. Модель физически не сможет передать недопустимое значение.[29]

**Правило 5. Формат выхода близкий к естественному тексту.** Модели проще писать markdown, чем JSON-строку с экранированием. Не заставляй её считать количество строк в diff — это формат-оверхед, где она ошибается.[29]

**Правило 6. Возврат high-signal.** Верни «no matches in src/policies/», а не `[]`. Пустой массив не говорит модели, что делать дальше; осмысленный ответ — говорит.[30]

**Правило 7. Ошибки учат следующему ходу.** Ошибка должна содержать, как исправить: «status must be one of [active, frozen], got 'blocked'». Не «Error: 400».[30]

**Правило 8. Группируй действия.** Вместо трёх тулов `create_pr` / `merge_pr` / `review_pr` — один `github_pr` с параметром `action=create|merge|review`. Меньше тулов в контексте = меньше confusion.[49]

**Правило 9. Не держи сотни тулов в одном контексте.** 3–5 горячих тулов — напрямую; остальные — за router'ом или tool search. Сотни MCP-тулов в одном промпте — гарантированная потеря.[30]

### Как заставить модель не теряться в тулах

**Router раньше тулсета.** Сначала классификация «FAQ / account / human», потом уже у скилла свой маленький набор тулов. Модели проще выбрать из 5, чем из 50.[29][33]

**Сплит на агентов-как-тулы.** OpenAI: когда тулов становится много — дели на специализированных агентов, а не делай одного «бога» с 40 функциями. Агент-как-тул = оркестрация-тул: передал задачу, получил результат.[33]

**Eval тулов на реалистичных задачах.** Слабый таск — «найди логи для customer_id=9182» — учит захардкоженному пути. Сильный таск — «клиент 9182 списан трижды за одну покупку, найди всех пострадавших» — заставляет реально пользоваться тулами.[30]

**Не оверспецифицируй ожидаемую последовательность тулов.** Валидных путей к решению несколько. Мерить outcome + обязательные тулы, а не точный порядок.[30]

**Смотри CoT / thinking.** Anthropic заметили, что модель дописывает «2025» к запросу web search. Починили описанием тула, а не моделью. Трейсы thinking показывают, где модель путается.[30]

### Три типа тулов (OpenAI)

- **Data** — читать: поиск по БД, чтение PDF, web search.
- **Action** — менять мир: создать тикет, отправить сообщение, обновить запись.
- **Orchestration** — агент как тул: делегировать задачу другому агенту.

Правило безопасности: action на деньги / PII — только с подтверждением человека (HITL), пока уверенность не вырастет.[33]

**Читать:** Writing tools for agents[30]; Building Effective Agents (appendix ACI)[29]; Claude tool-use docs[49].

---

## 5. Что за слова из статей

### Arize / Phoenix

**Arize** — компания, делающая платформу для наблюдаемости и eval LLM-приложений.

**Phoenix** — их открытый продукт (self-hosted), **AX** — облачный enterprise-продукт.

Что делает Phoenix: собирает **трейсы** (каждый LLM-вызов, retrieval, тул-колл, промпт, ответ), позволяет смотреть их в UI, строить датасеты, гонять оффлайн-eval, эксперименты, prompt playground.

Технически построен на **OpenTelemetry** (стандарт инструментирования) и протоколе **OpenInference**.

Чем отличается от конкурентов: **observability-first**. Петля такая: прод-трейс → нашёл неизвестный фейл → датасет → регресс-тест → фикс. Phoenix — это «посмотреть, что реально произошло», а не «прогнать eval и получить цифру».

Кому подходит: командам, которые хотят self-host и контроль данных (банк — идеальный кандидат, данные не уезжают в облако).

Фраза на собеседовании: «Повесил бы OpenTelemetry-трейсы с первого дня. Phoenix — если нужен self-host, Braintrust — если команда живёт в eval playground».

### Braintrust

Коммерческая платформа (есть free tier), построенная вокруг **eval-first** подхода.

Workflow в их доках: Instrument → Observe → Annotate → Evaluate → Deploy.

Что умеет: датасеты, scorers, эксперименты, сравнение промптов A/B на golden set, CI на каждую правку промпта, логи и трейсы как продолжение eval.

Чем отличается от Phoenix: сильнее сторона **до** релиза — playground и систематическое сравнение версий промпта. Phoenix сильнее в **наблюдаемости прода**. Это не взаимоисключение, а фокус.

Anthropic в своём гайде по agent evals ставит Braintrust в один ряд с LangSmith/Langfuse как готовые harness'ы.[32]

Фраза на собеседовании: «Промпт — это код: версия в Git, eval в CI. Braintrust/LangSmith как harness, Phoenix как прод-наблюдаемость».

### Apache Tika

Java-библиотека/сервис для извлечения **текста и метаданных** из файлов: PDF, Office (docx/xlsx/pptx), HTML, EPUB и ещё ~1000 форматов.

Версия 4.x по умолчанию отдаёт текст в **Markdown** — специально «для LLM и RAG» (структура заголовков сохраняется).

Архитектурно умная деталь: парсинг происходит в отдельных процессах (fork). Если файл враждебный (бинарный мусор, эксплойт-попытка) — падает отдельный процесс, а не сервис.

Что Tika НЕ делает: это не OCR-движок и не RAG. Если PDF — скан без текстового слоя, Tika вернёт пустоту. Дальше нужен OCR (Tesseract, DocTR, или VLM-парсеры, которые у Tika тоже есть).

Роль в пайплайне Plata: слой ingestion **перед** чанкингом. Нативный PDF → Tika → есть текст? → чанкинг. Нет текста → OCR → поля → RAG по полям.

### Marimo

Python-ноутбук с реактивной моделью выполнения.

В отличие от Jupyter: ячейки образуют граф зависимостей. Поменял ячейку A — автоматически пересчитались ячейки, которые от неё зависят. Нет «скрытого состояния»: результат всегда соответствует коду, который видишь.

Ключевое для инженера: файл Marimo — это чистый `.py`, а не `.ipynb` (JSON). Значит, он git-friendly: диффы читаемые, мержи работают.

Зачем он в статьях про evals: Hamel и Shreya постоянно говорят «смотри на данные руками». Marimo удобен как **data viewer / annotation app** — скрипт, который агент может править как обычный Python-файл.

Не путать с eval-платформой: Marimo — это среда для анализа, а не система прогона eval'ов.

### Google PAIR Guidebook

**PAIR** = People + AI Research, команда Google, которая занимается «как сделать AI полезным для людей».

**Guidebook** — их гайд по проектированию AI-продуктов (не инженерная дока, а UX/PM-материал).

6 глав: (1) user needs + определение успеха, (2) данные, (3) onboarding + управление ожиданиями, (4) explainability + уверенность, (5) feedback и контроль, (6) ошибки.

Почему упоминается в eval-контексте: глава про «определи reward / success до модели» — это то же самое, что «начни с success-метрик, а не с архитектуры». Это не про метрики faithfulness.

На собеседовании полезен как источник фразы: «Automation vs augmentation — решить, что делает модель, а что человек, до того как писать код».

---

## 6. Best practice деплоя агентов

### Сначала: агент ли это вообще

OpenAI дают чёткое определение: агент = LLM управляет выполнением workflow + тулы + guardrails.[33]

Не агент: одноразовый чат, классификатор, сентимент-модель. Это «LLM-приложение», не «агент».

Строить агента имеет смысл, когда хотя бы одно из:[33]

1. сложные решения и исключения, где правила разваливаются;
2. правила уже такие запутанные, что их дорого поддерживать;
3. много неструктурированных данных, которые надо интерпретировать.

Иначе — детерминированный граф дешевле и надёжнее.[29][33]

### Порядок выката: 8 шагов

**Шаг 1. v0 = workflow, не свободный агент.** Router → RAG / tool / human. Свободный ReAct-цикл добавляет только тогда, когда workflow упёрся в потолок.[29]

**Шаг 2. Прототип на самой сильной модели.** Сначала baseline качества без ограничений по деньгам, потом подменять дешёвой там, где результат не падает.[33]

**Шаг 3. Тулы по типам.** Data / action / orchestration. Action на деньги — HITL + лимит ретраев + kill switch.[33]

**Шаг 4. Guardrails параллельно с генерацией.** OpenAI Agents SDK гоняет guardrail-проверки параллельно основной генерации (optimistic execution) и бросает исключение при нарушении. Jailbreak, PII, relevance — fail closed.[34][33]

**Шаг 5. Полный лог.** request_id, prompt_version, tool_calls, chunk_ids, tokens, $, handoff. Без этого eval мёртв.[40][32]

**Шаг 6. Canary.** 5–10% трафика, первичная бизнес-метрика, гарды (эскалация, жалобы, $), быстрый rollback. Промпт и tool schema — как код: версия, дифф, откат.

**Шаг 7. Долгие задачи — очередь.** OCR, research, обход документов — через очередь и job_id, не в HTTP-запросе.

**Шаг 8. Long-running — не полагайся на compaction.** Подробнее ниже.

### Долгоживущие агенты: почему compaction не спасает

Проблема: агент работает сессиями, каждая новая сессия начинается без памяти о прошлой. Контекстное окно конечно, а задача — нет.[31]

Наивный ответ — compaction (сжатие контекста). Anthropic показали, что этого мало: даже Opus 4.5 на Agent SDK в цикле проваливает «собери клон claude.ai», если дать только общий промпт.

Два типичных фейла:

1. Агент пытается сделать всё за раз («one-shot»), вылетает из контекста на середине, следующая сессия гадает, что было.
2. Агент видит, что «что-то уже сделано», и объявляет задачу выполненной.

Их решение — двухчастный harness:[31]

**Initializer agent** — первая сессия, специальный промпт. Пишет:

- `init.sh` — скрипт запуска dev-окружения;
- `claude-progress.txt` — журнал того, что сделано;
- `features.json` — список фич со статусами `passes: false`, которые агент меняет только на true после реальной проверки;
- начальный git-коммит.

**Coding agent** — каждая следующая сессия:

- читает `pwd` → git log → progress file → features.json;
- выбирает **одну** незакрытую фичу;
- делает её, тестирует как человек (браузер, curl), только потом ставит `passes: true`;
- коммитит и обновляет progress.

Зачем JSON для списка фич, а не Markdown: модель реже ломает JSON, чем Markdown. Антропологический факт про LLM, который стоит запомнить.[31]

Ключевой принцип: **оставляй окружение в чистом состоянии** — таком, в котором любой следующий агент (или человек) может продолжить без разбора завалов.

### Технические детали деплоя

- Сервис с **явным циклом** (`while` с tool_call) или SDK: OpenAI Agents (Agent / Runner / guardrails / handoffs / sessions), Anthropic Agent SDK.[34][31]
- Stateless HTTP + внешний стор диалога (Redis/DB). Не держать 200 ходов в RAM одного воркера — упал воркер, умер диалог.
- **Идемпотентность action-тулов**: `idempotency_key` на каждый мутирующий вызов. Повтор сети ≠ двойное списание.
- Sandbox для кода/браузера: OpenAI sandbox agents / контейнер. Не тот же под, что API банка.[34]
- Secrets — не в промпт. Vendor: no-train + регион (GDPR/152-ФЗ); иначе контур.
- Версионируй одним `config_version`: prompt, tool schema, index, model id — чтобы лог был связным.

**Читать:** OpenAI Practical Guide[33]; Effective harnesses[31]; Building Effective Agents[29].

---

## 7. Метрики: универсальные + LLM-as-judge

### Термины, без которых метрики не читаются

**TP / FP / TN / FN** — четыре клетки классификатора:

- TP (true positive) — фейл реально был, и мы его поймали.
- FP (false positive) — фейла не было, а мы зарубили.
- TN (true negative) — всё ок, и мы пропустили.
- FN (false negative) — фейл был, а мы его пропустили.

**Precision** — из всего, что мы назвали «фейлом», сколько реально фейлов: `TP / (TP + FP)`.

**Recall** — из всех реальных фейлов, сколько мы поймали: `TP / (TP + FN)`.

**F1** — гармоническое среднее: `2 · P · R / (P + R)`. Один балл, который наказывает за перекос в любую сторону.

**TPR** — то же, что recall: доля пойманных реальных фейлов.

**TNR** — доля реально-хороших, которые не зарубили ложно: `TN / (TN + FP)`.

**Accuracy** — `(TP + TN) / всего`. Самая обманчивая метрика при дисбалансе: если фейлов 5%, классификатор «всё ок» даст accuracy 95% и поймает ноль проблем.[41][50]

**pass@k** — вероятность, что хотя бы одна из k попыток успешна. Формула: `1 − (1 − p)^k`. Для генерации SQL: достаточно одного удачного варианта.

**pass^k** — вероятность, что ВСЕ k попыток успешны: `p^k`. Для банковского ответа: один правильный из пяти не годится, нужна стабильность.[42][32]

**Containment** — доля диалогов, которые бот «закрыл» без человека. Не равно качеству: бот может закрыть диалог враньём. Мерить resolution, а не containment.

**HITL** — human-in-the-loop: человек в контуре для критичных действий.

**Guard trip rate** — доля запросов, где сработал входной/выходной гард. Растёт — значит, либо атаки, либо слишком жёсткий гард.

### Универсальные метрики — считай кодом, без судьи

| Метрика | Формула | Когда |
|---|---|---|
| Exact match | 1 если output == gold | поля, JSON-ключи, статусы |
| Schema valid | парсится + required fields на месте | любой structured output |
| Execution accuracy | SQL/код исполнился и совпал с эталоном | Text-to-SQL (твой Kontur-кейс) |
| Precision / Recall / F1 | см. выше | поля OCR, tool-call, handoff |
| Recall@k / P@k / MRR / nDCG | см. раздел 1 | retrieval |
| pass@k / pass^k | см. выше | генерация vs стабильность |
| Latency p50/p95, tokens, $ | перцентили | всегда |
| Tool error rate | 4xx/5xx / все вызовы | ACI |
| Guard trip rate | сработал гард / запросы | safety |
| Containment vs resolution | закрыл ≠ решил | каре |

### LLM-as-judge: что это и как не наступить на грабли

LLM-as-judge = LLM, который оценивает ответ другого LLM.

Растёт популярность по необходимости: задачи становятся открытыми (пересказ, диалог, творчество), и n-gram метрики перестают отличать хорошее от плохого. А людей дорого звать на каждый прогон.[41]

Базовые правила:

1. **Один судья = один критерий.** Не «оцени качество 1–5», а «ответ опирается на контекст: да/нет».[41][50]
2. **Binary, не Likert.** Разница 3 vs 4 — шум. PASS/FAIL с критикой в 2–4 предложения.[41][50]
3. **Калибровка на holdout.** 30–80 человеческих меток, судья против них, TPR/TNR. Few-shot — только из train, holdout не трогать.[41]
4. **Сильная модель на судью, дешёвая — в прод**, если задача позволяет.
5. **Criteria drift.** Критерии «рождаются», когда смотришь новые выходы. Перекалибруй регулярно.[50]
6. **Cost per judge call.** LLM-judge с CoT — это деньги и латентность; не вешай его в горячий путь, если можно кодом.

Три способа скоринга судьёй:[41]

- **Direct scoring** — оценить один ответ без альтернативы. Для фактов, faithfulness, политики. Ответ либо верен, либо нет — pairwise тут не применим.
- **Pairwise comparison** — «какой из двух лучше по критерию X». Стабильнее для субъективного (тон, убедительность), но не для фактов.
- **Reference-based** — сравнить с эталонным ответом. Дороже: нужны референсы.

Как мерить самого судью:[41]

- **Classification** (для binary-задач): precision/recall судьи на разметке. Лучший выбор.
- **Correlation** (для рангов): Cohen's κ (согласие двух райтеров с поправкой на случайность, 0.41–0.60 = moderate), Kendall's τ и Spearman's ρ (согласие ранжирований). Корреляционные метрики легче переоценить — не учитывают шанс-согласие.

Известные баги судей: bias к длинным ответам (verbosity bias), завышение баллов. GPT-4 давал alpaca-7b 4.7/5, а люди — 2.9/5.[41]

### Частые судьи, которые реально используют

Все — один критерий, binary, критика, TPR/TNR на holdout.

**1. Faithfulness / groundedness**
Вопрос: каждый claim ответа следует из контекста?
Выход: PASS если все claims supported. Ragas делает claim-split автоматически.[36]

**2. Answer relevance**
Вопрос: ответ адресует вопрос, без ухода в сторону?
Не про правду. Неполный ответ = fail.[38]

**3. Policy / refusal**
«На вопрос про чужой баланс отказался и предложил авторизацию?» PASS/FAIL. Плюс code-check: account_id ≠ session_id.

**4. Tool efficiency** (judge Anthropic Research)
Правильные тулы, разумное число раз, без бесконечного поиска.[27]

**5. Citation accuracy**
Каждая ссылка поддерживает утверждение, к которому прикреплена.[27]

**6. Completeness**
Все запрошенные оси покрыты. Anthropic перепробовали «зоопарк судей» (несколько судей на компоненты) и оказалось стабильнее: один вызов, один промпт, балл 0–1 + pass/fail.[27]

**7. Handoff quality**
Эскалация вовремя, с саммари, без выдуманного лимита.

**8. Pairwise A/B**
«Какой ответ лучше по X?» для тона/убедительности. Для фактов — direct scoring.[41]

Промпт-скелет судьи:

```
You are grading ONE criterion: <name>.
PASS if and only if: <observable rule>.
FAIL if: <counterexamples>.
Reply JSON: {"verdict": "PASS"|"FAIL", "critique": "..."}.
Do not score style, length, or politeness unless they violate the rule.
```

### Мини-словарь жаргона, который встретится

**Golden set** — размеченный набор (вопрос → правильный ответ / chunk_id). Основа любого eval.

**Synthetic data** — сгенерированные примеры. Запускай их через реальную систему, чтобы получить настоящие трейсы, потом error analysis.[50]

**Error analysis** — посмотреть на трейсы, записать «первый важный фейл», сгруппировать в таксономию, посчитать частоты. Делается ДО написания метрик.[50]

**Axial coding** — шаг error analysis: группировка заметок в категории фейлов.[50]

**Open coding** — шаг error analysis: свободные заметки по каждому трейсу.[50]

**Benevolent dictator** — один ответственный человек (домен-эксперт), который ставит финальные метки, чтобы не было разнобоя.[50]

**Criteria drift** — критерии оценки меняются по мере просмотра новых выходов; судью надо перекалибровать.[50]

**Guardrail** — проверка на входе/выходе (jailbreak, PII, relevance). Отличие от evaluator: guardrail — инлайн, в горячем пути; evaluator — асинхронно, в batch.[50]

**Trace / transcript** — полная запись прогона: промпты, ответы, тул-коллы, reasoning.[32]

**Outcome** — конечное состояние мира после прогона: есть ли бронь в БД, создан ли тикет.[32]

**Harness** — инфраструктура, которая гоняет агента и собирает результат: даёт тулы, крутит цикл, записывает трейсы.[32]

**HITL / approval gate** — человек подтверждает критичное действие (деньги, PII, необратимое).

**Kill switch** — рубильник: мгновенный rollback плохой версии промпта/модели в проде.

**Canary** — выкат на 5–10% трафика перед полным.

**Idempotency** — повтор вызова не меняет состояние (дважды отправил запрос → одно списание).

**Prompt cache** — кэширование общего префикса промпта между вызовами: дешевле и быстрее.[26]

**Reranker** — маленькая модель, которая заново сортирует найденные куски по релевантности к запросу.[26]

**HyDE** — сгенерировать гипотетический идеальный ответ и искать по нему, вместо запроса.

**Query rewrite** — переписать пользовательский запрос для лучшего поиска (снять местоимения, добавить доменную терминологию).

**Multi-hop** — вопрос, ответ на который требует фактов из нескольких кусков/документов.

**Tool-call F1** — precision/recall по множеству тул-вызовов vs ожидаемых.[35]

**LLM-as-judge / LLM-evaluator** — LLM, оценивающий ответы другого LLM.[41]

---

## Стек чтения по темам

| Тема | Первоисточник |
|---|---|
| RAG retrieval + contextual chunks | [Anthropic Contextual Retrieval](https://www.anthropic.com/news/contextual-retrieval) |
| RAG eval: retrieval vs generation | [Evidently RAG evaluation](https://www.evidentlyai.com/llm-guide/rag-evaluation) |
| RAG в проде, что логировать | [Jason Liu Levels of RAG](https://jxnl.github.io/blog/writing/2024/02/28/levels-of-complexity-rag-applications/) |
| Формулы faithfulness / recall / relevancy | [Ragas metrics](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/) |
| Когда субагенты да | [Anthropic multi-agent research](https://www.anthropic.com/engineering/built-multi-agent-research-system) |
| Когда субагенты нет | [Cognition Don’t Build Multi-Agents](https://cognition.ai/blog/dont-build-multi-agents) |
| Workflow vs agent | [Building Effective Agents](https://www.anthropic.com/engineering/building-effective-agents) |
| Тулы / ACI | [Writing tools for agents](https://www.anthropic.com/engineering/writing-tools-for-agents) |
| Деплой агента | [OpenAI Practical Guide PDF](https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf) |
| Long-running harness | [Effective harnesses](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents) |
| pass^k, tool+user бенчмарк | [τ-bench](https://github.com/sierra-research/tau-bench) |
| Судья как классификатор | [Eugene Yan LLM evaluators](https://eugeneyan.com/writing/llm-evaluators/) |
| Transcript vs outcome | [Anthropic Demystifying evals](https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents) |
| Почему binary, error analysis | [Hamel FAQ](https://hamel.dev/blog/posts/evals-faq/) |

FAQ Hamel после этой доки — справочник «почему binary», а не учебник RAG.[50]

## Sources

[1] https://www.whoop.com/us/en/thelocker/how-does-whoop-strain-work-101
[2] https://www.whoop.com/us/en/thelocker/how-does-whoop-recovery-work-101
[26] https://www.anthropic.com/news/contextual-retrieval
[27] https://www.anthropic.com/engineering/built-multi-agent-research-system
[28] https://cognition.ai/blog/dont-build-multi-agents
[29] https://www.anthropic.com/engineering/building-effective-agents
[30] https://www.anthropic.com/engineering/writing-tools-for-agents
[31] https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents
[32] https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents
[33] https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf
[34] https://openai.github.io/openai-agents-python
[35] https://docs.ragas.io/en/stable/concepts/metrics/available_metrics
[36] https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/faithfulness
[37] https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_recall
[38] https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/answer_relevance
[39] https://www.evidentlyai.com/llm-guide/rag-evaluation
[40] https://jxnl.github.io/blog/writing/2024/02/28/levels-of-complexity-rag-applications
[41] https://eugeneyan.com/writing/llm-evaluators
[42] https://github.com/sierra-research/tau-bench
[49] https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools
[50] https://hamel.dev/blog/posts/evals-faq
