NODEDC_1C/docs/ADDRESS/1010.txt

373 lines
18 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Да. И ключевая мысль тут такая: **эта система не заменяет ваш продуктовый контур**, а вешается **рядом** с ним как внешний тестовый раннер и слой контроля качества.
То есть не так: “перенесли ассистента в Promptfoo/DeepEval и теперь он там живёт”.
А так: **ваш ассистент остаётся у вас в коде**, со всеми маршрутами, настройками, тулзами и ограничениями. Eval-система просто **вызывает его**, подаёт тестовые сценарии и проверяет, что он ответил правильно. Promptfoo умеет ходить в произвольный HTTP endpoint, а также подключать кастомный Python provider; у него же есть поддержка system/user/assistant сообщений и multi-turn chat threads. ([Promptfoo][1])
Как это выглядит по-человечески.
## Что именно вы разворачиваете
Самый приземлённый вариант:
* ваш текущий backend ассистента;
* рядом Promptfoo **или** DeepEval;
* желательно ещё Langfuse **или** Phoenix для трассировок и накопления провалов.
Langfuse умеет хранить traces/sessions, собирать datasets, в том числе из production traces, и запускать experiments по этим датасетам. Phoenix тоже self-hosted, с tracing/evals/datasets/experiments и умеет гонять evals поверх уже собранных trace-данных. ([langfuse.com][2])
## Вы минуете интерфейс или нет
**Для основных eval-тестов — да, интерфейс лучше миновать.**
И это нормально.
Почему:
* интерфейс даёт лишний шум;
* вам важнее проверить не кнопку и не рендер, а **маршрут, параметры, tool use, числа, память диалога и финальный ответ**;
* если тест идёт прямо в backend-контур, вы проверяете именно логику ассистента.
Практически я бы делал так:
**Слой 1. Основной eval-контур — мимо UI.**
Promptfoo или DeepEval бьёт прямо в ваш backend endpoint, например `/assistant/chat` или специальный `/assistant/test-run`.
**Слой 2. Маленький smoke-набор через UI.**
510 сценариев, просто чтобы не развалился фронт, история сообщений, отображение ответа, кнопки и т.д.
То есть **95% качества ассистента** проверяется не через UI, а через его реальный backend contract.
## Что именно туда “подключается”
Есть 3 рабочих способа.
### Вариант A. Через HTTP endpoint
Самый простой.
У вас уже есть endpoint, который принимает:
* system context,
* историю диалога,
* user message,
* maybe org/company/period,
* maybe debug flags.
Тогда Promptfoo просто шлёт туда POST-запрос. Это его штатный режим. Для OpenAI-compatible или вообще любых HTTP endpoint он умеет собирать body, headers и доставать ответ из нужного поля JSON. ([Promptfoo][1])
### Вариант B. Через Python wrapper
Если ассистент лучше вызывать не по HTTP, а прямо функцией внутри кода, Promptfoo умеет кастомный Python provider, а DeepEval вообще изначально Python-first и ближе к pytest-стилю. ([Promptfoo][3])
### Вариант C. Через “test harness” endpoint
Часто это лучший вариант для сложных ассистентов.
Вы делаете отдельную ручку, условно:
`POST /internal/eval/run`
И она:
* принимает сообщение и историю,
* запускает **ровно тот же** роутер и те же тулзы, что и боевой контур,
* но дополнительно возвращает debug:
* какой маршрут выбрался,
* какие параметры извлеклись,
* какие тулзы вызвались,
* какие SQL/MCP/1C-запросы ушли,
* какие evidence вернулись,
* почему был выбран именно этот ответ.
Вот это уже идеальная почва для нормальных evals.
## Где хранится промпт
Тут важный момент.
Есть два режима:
### 1) Тестировать “изолированный промпт”
Это когда system prompt реально хранится в Promptfoo/DeepEval-конфиге. Такой режим полезен, если вы хотите сравнивать версии системного промпта отдельно от продукта. Promptfoo поддерживает prompt files, сообщения в chat-формате и эксперименты с разными prompt/provider комбинациями. Langfuse тоже умеет prompt management, versioning и experiments against datasets. ([Promptfoo][4])
### 2) Тестировать реальный продуктовый контур
Это когда системный промпт живёт у вас в коде, а eval-система просто вызывает ваш ассистент как чёрный ящик.
**Для вашего случая я бы начинал именно со второго.**
Потому что у вас не “просто промпт”, а целая продуктовая логика: маршруты, ограничения, диалог, бухгалтерские сценарии, tool calling.
Иначе вы получите ложную картину: “в Promptfoo всё хорошо”, а в реальном продукте всё поплыло.
## Что считать корректным ответом
Вот тут и находится главный узел.
У вас **не может быть одного типа проверки** для всех кейсов. Нужен гибрид.
### Тип 1. Жёсткая проверка
Для кейсов вроде:
* посчитать НДС,
* показать остатки,
* топ контрагентов,
* сколько заплатили подрядчикам,
* остатки по счёту 51,
* прогноз НДС на период.
Тут правильность — это:
* правильный маршрут;
* правильные фильтры;
* правильный период;
* правильная организация;
* правильные числа;
* правильная сортировка.
То есть тут нужен не “похоже на хороший ответ”, а почти unit/integration test.
Пример:
* expected route = `vat_forecast`
* expected entity = `ООО Ромашка`
* expected period = `2026-03`
* expected total = `1_245_330.17`
* допуск по числам = 0.01
### Тип 2. Структурная проверка
Например:
* ответ должен содержать таблицу по контрагентам;
* не должен выдумывать документы;
* должен явно указать период;
* если данных нет, должен честно сказать, что не хватает данных;
* должен дать evidence/source refs.
### Тип 3. Rubric / LLM-as-a-judge
Для свободного диалога.
Например:
* объяснил ли понятным языком;
* не ушёл ли за рамки бухгалтерского домена;
* не потерял ли ограничение;
* не начал ли советовать что-то юридически/налогово опасное без оговорок;
* корректно ли обработал follow-up.
Promptfoo для этого использует `llm-rubric`, а DeepEval — G-Eval и conversational G-Eval для whole-conversation оценки. Promptfoo отдельно предупреждает, что у model-graded assertions PASS/FAIL зависит от `pass` и `threshold`, и без порога можно получить “зелёный” тест даже при низком score. DeepEval даёт кастомные judge-метрики и отдельно умеет conversational G-Eval для оценки целого диалога, а не одного ответа. ([Promptfoo][5])
И вот это очень похоже на вашу проблему:
**“кодекс сказал зелёное, а руками тестишь — пиздец”**
часто означает одно из трёх:
1. критерии слишком общие;
2. judge-модель оценивает “по стилю”, а не по факту;
3. нет жёсткого threshold и нет проверки маршрута/чисел/tool use.
## Как я бы устроил это у вас пошагово
### Шаг 1. Не пытаться сразу “умную автопочинку”
Сначала нужен **контур истины**.
Минимальный набор:
* 100200 кейсов;
* разбивка по типам;
* единые поля expected behavior.
Пример структуры кейса:
```json
{
"id": "vat_001",
"history": [],
"input": "Посчитай НДС к уплате за март 2026 по ООО Альфа",
"expected": {
"route": "vat_summary",
"entities": {
"company": "ООО Альфа",
"period": "2026-03"
},
"must_call_tools": ["vat_turnover_query"],
"must_not_call_tools": ["counterparty_top_query"],
"answer_checks": {
"contains_period": true,
"must_include_total": true
},
"numeric_targets": {
"vat_due": 1245330.17,
"tolerance": 0.01
}
}
}
```
### Шаг 2. Подключить ассистента как black box
Лучше всего — через HTTP.
То есть eval-система не знает, как внутри устроены ваши маршруты.
Она просто шлёт запрос и получает:
* final answer,
* trace/debug json.
### Шаг 3. Разбить проверки на 4 уровня
Я бы делал именно так:
**A. Router correctness**
Правильно ли выбран маршрут.
**B. Tool / query correctness**
Те ли инструменты/запросы пошли.
**C. Factual correctness**
Правильные ли числа и факты.
**D. Dialogue quality**
Не потерялся ли контекст, не ушёл ли в болтовню, не выдумал ли лишнего.
### Шаг 4. Собирать реальные фейлы из жизни
Вот тут очень полезен Langfuse или Phoenix.
Идея такая:
* боевые диалоги пишутся в traces;
* хорошие/плохие помечаются;
* из них рождается dataset;
* dataset пополняется не вручную с нуля, а из реальных провалов.
Langfuse это прямо позиционирует как сценарий: datasets, experiments, traces, production feedback. Phoenix тоже умеет запускать evals на traces. ([langfuse.com][2])
### Шаг 5. Только после этого впрягать Codex/Aider/OpenHands
И тут принцип очень важный:
**Codex не должен быть судьёй.**
Он должен быть:
* генератором кейсов,
* генератором follow-up вопросов,
* генератором adversarial сценариев,
* фиксером кода/промпта/роутера,
* но не финальным источником истины.
Иначе он начинает сам себя хвалить.
Aider, например, умеет автоматически гонять линтеры и тесты после своих изменений и пытаться чинить найденные проблемы. OpenHands позиционируется как open-source coding-agent платформа/SDK с локальным запуском агентов и CLI. ([aider.chat][6])
## Как подключить “мощь кодекса”, чтобы он сам задавал вопросы
Вот это уже реально хорошая идея. Но делать это надо не “кодекс сам всё решит”, а в двух ролях:
### Роль 1. Генератор тестов
Codex берёт:
* ваши маршруты,
* текущие кейсы,
* реальные trace-провалы,
* описание домена,
и генерирует новые вопросы:
* пограничные;
* двусмысленные;
* с пропущенным периодом;
* с несколькими организациями;
* со сменой темы внутри диалога;
* с follow-up типа “а по прошлому месяцу?”;
* с конфликтующими параметрами.
То есть он расширяет ваш eval suite.
### Роль 2. Фиксер
После падения тестов Codex получает:
* failing cases,
* expected behavior,
* actual trace,
* diff последних изменений,
и правит:
* router,
* extraction,
* system prompt,
* guardrails,
* answer composer,
* test suite.
А потом снова гоняется eval.
То есть получается петля:
**реальные traces / ручные кейсы → eval suite → падения → codex fixer → повторный прогон → merge only if green**
## Что я бы рекомендовал конкретно вам
Для вашего кейса я бы не делал слишком жирную схему сразу.
### Стартовый стек
**Promptfoo + Langfuse + Codex/Aider**
Почему:
* Promptfoo быстро подключается к HTTP endpoint и удобен для регрессионных прогонов, rubric-assertions и multi-turn сценариев. ([Promptfoo][1])
* Langfuse хорошо подходит как накопитель traces/datasets/experiments. ([langfuse.com][2])
* Codex/Aider использовать не как judge, а как test generator + fixer. ([aider.chat][6])
### Когда брать DeepEval
Если захотите больше Python-first логики и метрик уровня:
* component tests,
* conversational metrics,
* custom LLM judge,
* synthetic dataset generation.
DeepEval прямо под это и заточен: unit-test style, end-to-end/component level, custom LLMs for evaluation, conversational G-Eval для многоходовых диалогов. ([DeepEval][7])
## Самый важный практический вывод
Вам не нужна “система, куда вставил промпт и она magically поняла, что хорошо”.
Вам нужен **контракт качества**:
* какой маршрут должен быть выбран;
* какие параметры должны быть извлечены;
* какие тулзы должны вызваться;
* какие числа должны получиться;
* как ассистент должен вести себя в follow-up;
* где он обязан честно сказать “не знаю / не хватает данных”.
Пока этого слоя нет, любой Codex, хоть очень умный, будет красить вам зелёное “по ощущению”.
А когда этот слой появится, уже можно сделать очень сильную штуку:
* Codex сам генерит новые кейсы,
* сам находит слабые места,
* сам правит код,
* но **истину проверяет не он**, а ваш eval harness.
Если хочешь, следующим сообщением я могу сразу дать **конкретную схему под ваш стек**:
`Qwen 2.5 локально + backend ассистента + Promptfoo + Langfuse + Codex`,
с прямым пошаговым сценарием “что ставить, какой endpoint сделать, какой JSON возвращать и как начать с первых 20 кейсов”.
[1]: https://www.promptfoo.dev/docs/providers/http/ "HTTP/HTTPS API | Promptfoo"
[2]: https://langfuse.com/docs/evaluation/experiments/datasets "Datasets - Langfuse"
[3]: https://www.promptfoo.dev/docs/providers/python/ "Python Provider | Promptfoo"
[4]: https://www.promptfoo.dev/docs/configuration/chat/ "Chat Conversations and Multi-Turn Threads | Promptfoo"
[5]: https://www.promptfoo.dev/docs/configuration/expected-outputs/model-graded/llm-rubric/ "LLM Rubric | Promptfoo"
[6]: https://aider.chat/docs/usage/lint-test.html?utm_source=chatgpt.com "Linting and testing"
[7]: https://deepeval.com/docs/getting-started "Quick Introduction | DeepEval by Confident AI - The LLM Evaluation Framework"