Два прозореца със страницата на пускане в pacod-benchmark, светъл и тъмен, един върху друг, всеки с предупреждение, показателите на пускането и таблица с клетки с обръщения в секунда, закъснения, собственото забавяне на шлюза и добавката.
Завършено пускане през деня и през нощта: управляван срещу прокси режим върху демо шлюза, девет готови клетки и собственото предупреждение на пускането, че числата му показват поведение, а не капацитет.

pacod-benchmark измерва pacod-gw (режимите му managed и proxy, минималния шлюз, вариантите на внедряване) и други шлюзове — в много конфигурации в едно пускане. Всяка клетка се измерва спрямо базова линия, която се обръща директно към mock доставчика, така че остатъкът е това, което е добавил шлюзът. Интерфейсът му съставя наборите, следи пусканията секунда по секунда и ги сравнява; отчетите отиват в конзолата на шлюза.

Бенчмаркове на шлюза

Възможности

Набори с оси

Една базова конфигурация и полетата, които тя променя, посочени с пътя си през точки. Всяка комбинация става клетка; онези, които не могат да се изпълнят, отпадат с посочена причина.

Базова линия за всяка клетка

Същото натоварване от същите агенти, директно към мока. Добавката на една клетка е закъснението ѝ минус това на базовата ѝ линия, включително онова, което собственият таймер на шлюза не вижда.

Сцена, която се поставя и прибира

Върху управляван pacod-gw всяко пускане създава своя временна организация, работни пространства, ключове и mock доставчици и после ги премахва, като започва от ключовете.

Всеки шлюз като цел

pacod-gw-lite, друга инсталация или друг продукт като външна цел — с ключ, който се поставя за всяко пускане и никога не се съхранява.

Поведение или капацитет

Всяко пускане казва какво показват числата му и защо — къде са стояли агентите, стигал ли им е процесорът, ползвало ли е нещо друго шлюза или мока.

Еднакво с еднакво

Сравняват се от две до шест пускания наведнъж и на един ред попадат само клетки с точно същата конфигурация, на която и цел да са изпълнени.

Отчети по договора на шлюза

Отчетът на едно пускане е в собствения формат на pacod-gw и минава собствените му проверки. Публикувайте го в конзолата или го изтеглете и качете там.

Опашка и графици

По едно пускане наведнъж, останалите — в опашка, която може да се пренарежда. Графиците пускат набор всеки ден или на няколко часа и могат да публикуват отчета му накрая.

Команден ред

pacod-bench run изпълнява набор върху цел от терминала — без база данни, вход и интерфейс — и записва отчета във файл.

Диаграмата на платформата с осветен pacod-benchmark и приглушени останали проекти, с коралова пунктирна линия от него към pacod-gw и златиста точкова дъга към pacod-id.
Мястото на pacod-benchmark: вътре в платформата, но извън пътя на обръщенията на всеки продукт. Кораловата линия е натоварването, което подава на pacod-gw, и отчетите, които публикува там, а златистата дъга е собственият му вход през pacod-id; приглушени са обръщенията на продуктите към LLM през pacod-gw (зелено), входът и ролите през pacod-id (златисто) и използването им от хора (сиво).

Екранни снимки

Светлият екран с наборите на pacod-benchmark с два набора с име Managed against proxy mode, във версии 2 и 1, с осите и комбинациите им, над форма за нов набор и поле за внасяне.
Наборите. Всеки е една конфигурация и осите, които променя, тук натоварване и режим в шест комбинации; нов набор започва от пример или се внася в YAML или JSON.
Светлият екран с пусканията на pacod-benchmark с едно завършено пускане върху demo-gateway, пуснато от admin@pacod.demo, и бутони за пускане от набор и за сравняване на пускания.
Пусканията, най-новите първи: всяко е набор върху цел, с клетките си (тук шест и три базови линии), състоянието си и кой го е пуснал. Две до шест от тях могат да се сравнят.
Светлият екран с агентите за натоварване на pacod-benchmark с бележка за поведение и капацитет и един агент, local, свободен и в стека, който се обръща към http://pacod-gw:8080, с 2 процесора, ограниченията си и версията на договора си.
Агентите за натоварване: тези на pacod-gw, които правят всяко обръщение на една клетка. Този тук работи в стека на демото, затова числата му показват поведение; за капацитет са нужни агенти на други машини.
Тъмното табло на pacod-benchmark с таблица на последните пускания и панел с агента local, свободен, в стека, с 2 процесора, и ограничения от 256 клетки и 4 часа на пускане.
Таблото през нощта: последните пускания и агентите за натоварване — дали са свободни, къде стоят и ограниченията на едно пускане.
Тъмният екран с бенчмаркове в конзолата на pacod-gw със списък на публикуван отчет и форма за качване на друг.
Отчети на pacod-benchmark, публикувани в конзолата в Platform → Testing → Benchmarks.

Защо бенчмарк

Шлюзът се преценява по две неща: какво добавя към всяко извикване и как се държи, когато много извиквания пристигат наведнъж. Нито едното, нито другото личи от един тест с натоварване и една настройка. Най-голямата част от времето на едно извикване е на доставчика; делът на шлюза е малък и се мени с режима, диалекта, кеша, одита, натоварването и внедряването. pacod-benchmark измерва този дял във всички тях в едно пускане и всеки резултат казва дали може да се приеме за капацитет на шлюза, или само за неговото поведение.

Свое натоварване не създава. Извикванията идват от агентите за натоварване на pacod-gw — същите, които тестерът в конзолата ползва за своите тестове с натоварване, — а отчетите са във формат, който pacod-gw определя и проверява. Бенчмаркът планира, поставя сцената, пуска агентите едновременно, сумира секундите им и казва за какво могат да се приемат числата.

Как е изграден

браузър ──► pacod-caddy ──► bench-ui (SvelteKit, вход чрез pacod-id)
                                │ само от сървъра, с токена на човека
                                ▼
                        bench-api (pacod-bench serve) ──► PostgreSQL "bench"
                          │  админ токен      ──► API за управление на pacod-gw (сцена, живи числа, отчети)
                          │  токен на агенти  ──► агенти на pacod-gw ──► шлюзът или външен URL
                          │                                       └─► мокът, директно (базови линии)
                          └  сервизен клиент  ──► pacod-id (роли, фийд, ключове)
pacod-bench run (команден ред): същият контролер, без база, без интерфейс, файл с отчет
  • Контролерът е библиотека, pacod-bench-engine, без база данни и без сървър. Услугата и командният ред изпълняват един и същ код.
  • Услугата, pacod-bench serve, съдържа API-то, опашката от пускания, планировчика и базата PostgreSQL bench. Слуша само във вътрешната мрежа pacod-admin-net: вика я единствено сървърът на интерфейса.
  • Интерфейсът е фронтенд на SvelteKit, който вика API-то от своя сървър с токена на влезлия човек. Страницата на едно пускане го следи на живо чрез поток от server-sent events.
  • Агентите, мокът и типовете на отчета са на pacod-gw, взети при фиксиран commit. Промяна в договора на агента или на отчета започва в pacod-gw и се пренася оттам.

Набор

Наборът е базова конфигурация и осите, които тя променя, в YAML или JSON. Това е примерът examples/suites/modes.yaml:

name: Managed against proxy mode
description: The same calls in both modes at three load levels, behind the same mock.
base:
  request: { interface: openai, mode: managed, stream: true, prompt_words: 60, max_tokens: 64 }
  mock: { family: openai, delay_ms: 200, chunk_delay_ms: 20, chunks: 20 }
  workspace: { cache: "off", audit: on_request }
  load:
    mode: concurrency
    stages: [{ duration_s: 0, target: 10 }, { duration_s: 60, target: 10 }]
    timeout_s: 120
    warmup_s: 5
axes:
  request.mode: [managed, proxy]
  load.target: [10, 50, 100]

Два режима при три нива на натоварване дават шест клетки, а трите нива — три базови линии. Какво съдържа конфигурацията на една клетка:

Част Полета
request Интерфейсът (OpenAI, Anthropic, Gemini, собственият, embeddings), режим managed или proxy, поточно или наведнъж, дължина на промпта, max_tokens, HTTP/1.1 или HTTP/2, допълнителни заглавки
mock Диалектът, на който шлюзът говори към доставчика, и закъснението на мока: преди заглавката, между частите, брой части в отговор
workspace Кешът на отговорите (off, shadow, enforce), одитът, хранилището на отговори, какво става при пълен спул за използването
key Инспекцията (full или none — сляпото препращане в режим proxy), политиката за отчитане на използването, таван на едновременните извиквания
load Затворен кръг от извикващи (concurrency) или отворен поток от пристигания (rate), етапите му, времето за изчакване на едно извикване, таван на извикванията, загряване, което не влиза в числата

Оста посочва поле с пътя му през точки; load.target мащабира всеки етап, така че една рампа си остава рампа и при друг връх. Комбинациите, които не могат да се изпълнят, отпадат, и планът казва защо: режимът proxy иска мока на собствения си диалект, собственият интерфейс е само в режим managed, кешът никога не отговаря в режим proxy и на embeddings, а една и съща конфигурация два пъти остава веднъж. Редът grouped държи заедно клетките, на които трябва една и съща конфигурация на шлюза, за да се презарежда той възможно най-рядко; shuffled ги разпръсва в фиксиран ред, за да не съвпадне бавният дрейф на машината с една от осите. Повторенията изпълняват всяка конфигурация отново.

Онова, което решава само внедряването (анализи включени или не, хранилището на кеша, схемата за мащабиране, минималният шлюз, хардуерът), не е ос. Всяко такова внедряване се регистрира като отделна цел и получава същия набор. Примерите в хранилището покриват двата режима, три закъснения на доставчика (20, 200 и 1000 ms), кеша на отговорите, стъпки от извиквания в секунда, превода между диалекти и външна цел. Пълният формат е в docs/suite-format.md.

Едно пускане, стъпка по стъпка

  1. План. Наборът се разгръща за целта. Външна цел отказва осите, които бенчмаркът не може да зададе там (mock, workspace, key). Пускането пази плана си заедно с копия на набора, целта и агентите.
  2. Проверка на агентите. Всеки агент трябва да отговаря, да говори по този договор, да е свободен и да допуска своя дял от всяка клетка.
  3. Поставяне на сцената върху управляван шлюз: прочитане на отпечатъка му, създаване на собствените mock доставчици на бенчмарка, на временна организация bench-<run>, на работно пространство за всеки вариант с изрично обвързване на кеша и даване на достъп до mock моделите. После изчакване, докато всяка инстанция на шлюза приложи конфигурацията.
  4. Изпълнение на всяка клетка. Задаване на профила на мока, ако се е сменил, и създаване на ключа на клетката (само нейният режим и модел, валиден до 15 минути след планирания ѝ край), после отново изчакване на инстанциите. Изчакване агентите да се освободят и около три секунди наблюдение на извикванията на шлюза в ход (не се очакват такива). Пускане на всички агенти наведнъж. Веднъж в секунда — събиране на новите секунди на всеки агент, на всеки две секунди — собствените числа на шлюза. После спиране на агентите, изтриване на ключа, обобщение и отбелязване на онова, което читателят трябва да знае.
  5. Прибиране на сцената: първо ключовете, после останалото от най-новото към най-старото. Каквото не е могло да се изтрие, се изброява като остатък, който администратор да разчисти. Отказът минава по същия път, а ако API-то спре по време на пускане, агентите спират сами и следващото стартиране прибира онова, което пускането е създало.

Базовите клетки се изпълняват първи: същата заявка и същото натоварване от същите агенти директно към мока, без никакъв шлюз. Ключът им за сравнение обхваща само онова, което стига до мока, затова клетка managed и клетка proxy делят една базова линия. Докато тече натоварването на една клетка, в шлюза не се записва нищо, и тестовете проверяват това.

Как се четат числата

Страницата на пускането показва за всяка клетка устойчивия прозорец — от края на загряването до края на последния етап:

Число Какво е
Извиквания в секунда Средното за устойчивия прозорец
Първи байт, цяло извикване Персентили на закъснението, както са ги видели агентите
Шлюз Собственото забавяне на шлюза (x-gw-delay-ms, до заглавката на отговора); n/a, където целта не изпраща такова
Добавка Закъснението на клетката минус това на базовата ѝ линия: какво е добавила целта, включително онова, което собственото число на шлюза пропуска, например препращането на поток
Отметки Агент в стека на шлюза, с недостиг на процесор или закъснял; друг трафик към шлюза; промяна на конфигурацията по време на клетката; изтекли времена; кратък устойчив прозорец

Агент на машината на шлюза отнема процесор от шлюза, а мрежата на Docker добавя свое време. Затова едно пускане се брои за капацитет само когато всеки агент е обявен за отдалечен, никой не е останал без процесор и не е закъснял, никакъв друг трафик не е стигнал до шлюза и целта обявява мока си за изцяло свой. Иначе е поведение, и страницата на пускането и отчетът му го казват, с причините. В pacod-demo агентът дели машината с шлюза, а мокът обслужва и тестовете с натоварване на тестера, така че всяко пускане върху демо шлюза показва поведение.

Ключът за сравнение на клетката е SHA-256 на цялата ѝ конфигурация като каноничен JSON. Клетките с един и същ ключ са изпълнили една и съща конфигурация — във всяко пускане и върху всяка цел. Run again върху завършено пускане слага на опашката неговото собствено копие на набора върху същата цел със същите агенти, така че клетките му имат същите ключове като тези на първото пускане.

Цели и агенти

Цел Какво прави бенчмаркът там Как се добавя
Управляван pacod-gw Поставя сцената на всяко пускане през API-то за управление, с токен на администратор на платформата В интерфейса от администратор, с токена; или от конфигурацията (BENCH_GW_ADMIN_URL, BENCH_GW_TARGETS)
Външна цел Нищо: праща извиквания към URL и модел, с ключ, поставен за всяко пускане В интерфейса от администратор

Външна цел е сравнима с управляван шлюз само ако стои пред същия мок със същия профил, който тя обявява. Цел, която може да стигне до платен доставчик, иска таван на извикванията във всяка клетка и потвърждение на разхода при всяко пускане, а всяко извикване се прави само веднъж. Проверката на цел прави едно извикване през нея със собствена пробна сцена, за да личи там мок извън списъка с разрешени изходящи адреси на шлюза, а не по-късно като пускане, пълно с грешки.

Агентите са агентите за натоварване на pacod-gw, изброени в BENCH_AGENTS или добавени от администратор в интерфейса. Агентът изпълнява един тест наведнъж, така че бенчмаркът и тестерът в конзолата никога не натоварват един агент едновременно; клетката чака зает агент и после се пропуска с отметка.

Къде работи агентът Как
В собствения стек на бенчмарка bench-load-agent
В стек на pacod-gw Неговият gw-load-agent (docker-compose.load.yml)
На друга машина deploy/load-agent/ на pacod-gw, насочен към публичната равнина за данни на шлюза
В Kubernetes Helm чартът на pacod-gw, loadAgent.enabled

Всеки агент се обявява като in_stack, same_host или remote и само отдалечени агенти могат да направят пускане за капацитет.

Отчети

Отчетът на завършено пускане, pacod-bench-report/1, се изгражда със собствения crate на pacod-gw и се проверява с неговата собствена валидация. Съдържа целта; фактите за внедряването, всеки от тях наблюдаван от шлюза или обявен от оператора; агентите и къде са стояли; поведение или капацитет и защо; и всяка клетка с числата, хистограмите, секундите и отметките ѝ. Не съдържа тайни и назовава кой е изпълнил пускането, а не кой го е публикувал, така че изтегленият и публикуваният отчет са един и същ.

  • Publish to pacod-gw го изпраща към API-то за управление на шлюза; той се появява в Platform → Testing → Benchmarks.
  • Download дава същите байтове, за качване във всяка конзола на pacod-gw. Шлюзът пази отчет веднъж на SHA-256, така че един и същ отчет два пъти е един.
  • pacod-bench validate report.json проверява файл така, както го прави шлюзът.

Отчетът е най-много 1 MiB: секундите на дългите пускания се разреждат, числата и хистограмите — никога.

Роли и сигурност

Ролите са на pacod-id, описани в authz/bench.yaml и изпращани към pacod-id при всяко стартиране. Няма подразбираща се роля: профил без роля вижда „no access“.

Роля Може
bench.viewer Да чете агенти, цели, набори, пускания и резултати; да изтегля отчети
bench.operator Освен това да пише набори, да пуска и спира пускания, да публикува отчети
bench.admin Освен това да добавя шлюзове, външни цели и агенти, да променя настройките на пусканията, да трие пускания, да разчиства остатъци, да дава роли

Пускането действа върху управляван шлюз с токена на администратор на платформата, който принадлежи на бенчмарка, а не с правата на извикващия, затова ролите operator и admin са за хора, на които това може да се повери. Моделът на заплахите казва какво струва това и какво го ограничава:

  • Списък с позволеното. Клиентът на API-то за управление има метод за всяко извикване, което пускането прави, и няма общ. Трие само под временната организация на самото пускане и записва доставчици и модели само под mock имената на бенчмарка.
  • Запечатани токени. Токенът на шлюз, добавен в интерфейса, се шифрова със собствен ключ за данни (AES-256-GCM), който е обвит с BENCH_KEK — ключ от средата или от файл, никога от базата. Идентификаторът на целта и API-то ѝ за управление са вплетени в шифроването: токен, копиран към друга цел, или такъв, чийто адрес е сменен в базата, не се отваря. Нито отговор, нито пускане, нито отчет, нито лог го съдържат.
  • Извън обсег. API-то слуша само в pacod-admin-net и Caddy никога не го публикува. Одитният дневник на шлюза назовава бенчмарка при всяка промяна, която той прави.
  • Ключове. Поставеният ключ на външна цел остава в паметта и никога не се записва. Временният ключ на клетката позволява само нейния режим и модел.
  • Агенти. Агентът сравнява токена за постоянно време, а самият токен е поне 32 знака. API-то говори с агентите по обикновен HTTP, затова агентите на други машини имат нужда от частна мрежа или VPN.
  • Графиците се задействат с правомощията на създателя си: при всяко задействане бенчмаркът проверява дали създателят все още може да пуска, и ако не, изключва графика.

Интерфейсът влиза като поверителен клиент на pacod-id с PKCE. API-то приема само токени, издадени за собствената му аудитория, и ги отказва, ако копието му на ролите е по-старо от минута.

Внедряване и експлоатация

Услуга Какво
bench-api pacod-bench serve: API-то, опашката от пускания и планировчикът; метрики на порт 9190
bench-ui Интерфейсът, зад Caddy
bench-load-agent, bench-load-mock Само в самостоятелния стек: агентът за натоварване и mock доставчикът на pacod-gw
bench-gw-lite Профил dev-gateway: pacod-gw-lite пред мока, за регистриране като външна цел

docker-compose.yml съдържа собствените услуги на бенчмарка; docker-compose.standalone.yml добавя неговите копия на общите компоненти (PostgreSQL, pacod-id, Caddy, Prometheus) и собствени агент и мок. На лаптоп make dev-up пуска стека на http://localhost:18380 (вход като admin@pacod-bench.test, парола pacod-bench-dev), насочен към стек за разработка на pacod-gw на същата машина. Бенчмаркът управлява този шлюз, щом получи един от токените му на администратор на платформата — в интерфейса или в .env.

pacod-demo зарежда само първия файл, до останалите продукти, и го насочва към демо шлюза, неговия агент за натоварване и неговия мок; интерфейсът е на bench.demo.pacod.io. Собствен Helm чарт е планиран.

Наведнъж върви едно пускане; останалите чакат в опашка, която страницата Runs пренарежда. Графиците пускат набор върху управляван шлюз по UTC — всеки ден в зададен час в избрани дни от седмицата или на всеки няколко часа. Задействане се пропуска, докато предишното пускане на графика още чака или върви, а задействане, пропуснато с повече от 15 минути, не се наваксва. Настройките на администратора могат да свалят границите на едно пускане (клетки, секунди, чакане на зает агент) под тези на средата, които по подразбиране са 256 клетки и четири часа.

Всяка услуга пише по едно събитие на ред в stdout, като JSON или текст. Runbook-ът описва конфигурацията, токена на шлюза, целите, агентите, смяната на BENCH_KEK и отстраняването на проблеми.

Командният ред

pacod-bench run изпълнява набор от терминала и записва отчета му. Файлът на целта казва къде са тайните, никога самите тайни:

name = "lite on the laptop"
product = "pacod_gw_lite"          # pacod_gw, pacod_gw_lite, third_party
[external]
base_url = "http://127.0.0.1:18381"
model = "openai/gpt-mock"
key_env = "BENCH_TARGET_KEY"
paid = false
mock = { family = "openai", delay_ms = 200, chunk_delay_ms = 20, chunks = 20 }
[declared]
layout = "lite"
arch = "arm64"

Управляван шлюз получава вместо това [managed] — с API-то си за управление, променливата с токена си и мока, както шлюзът го достига. Ctrl-C прекъсва, като първо се прибира сцената. scripts/build-binaries.sh изгражда командния ред като собствен изпълним файл за Linux (статичен) и macOS.

Какво е тествано и къде са границите

  • Контролерът се тества срещу имитация на API-то за управление и агент в процеса: управлявано пускане поставя сцената си, мери и я прибира, без да записва нищо по време на натоварването на клетка; външно пускане не поставя нищо и пази само подсказка за ключа; зает агент спира пускането, преди нещо да е започнало.
  • API-то се тества срещу PostgreSQL: набор от API-то до края му, отказ по време на пускане, шлюз, добавен в интерфейса, който никога не показва токена си, рестарт, който прибира останалото от прекъснато пускане, опашката, настройките, агенти, добавени в интерфейса, и графици, които се задействат, публикуват и никога не наваксват пропуснато задействане.
  • Договорите на агента за натоварване и на отчета се тестват спрямо собствените fixtures на pacod-gw, а make vendor-check отказва копие на pacod-gw, което не е от commit.
  • В pacod-demo всяко пускане върху демо шлюза показва поведение, а не капацитет: агентът работи в стека на шлюза на същата машина, а мокът е общ. За числа за капацитет са нужни агенти на други машини и собствен мок.
  • Токенът на пускането е на администратор на платформата: pacod-gw още няма по-тясна роля за него.
  • Клетка с кеш мери попадение, а не смес от попадения и пропуски: всяко извикване на клетката праща едно и също тяло.
  • Собствен Helm чарт още няма.

Документът за архитектурата показва как е устроен кодът — от плана до отчета.

Бърз старт

Стекът, до стек за разработка на pacod-gw

make dev-up
open http://localhost:18380

Командният ред, без Docker

make plan SUITE=examples/suites/modes.yaml
cargo run -p pacod-bench -- run --suite examples/suites/modes.yaml --target target.toml --agents local=http://127.0.0.1:18096 --out report.json
cargo run -p pacod-bench -- validate report.json

Работи с

  • pacod-gw: Шлюзът, който измерва. Управлява агентите за натоварване и мока на pacod-gw, поставя сцената на всяко пускане през API-то за управление и публикува отчетите си в Platform → Testing → Benchmarks.
  • pacod-id: Входът в интерфейса минава през pacod-id, а API-то проверява всеки извикващ спрямо ролите от authz/bench.yaml, които изпраща към pacod-id при всяко стартиране.
  • pacod-demo: Работи на bench.demo.pacod.io до останалите продукти, управлява демо шлюза с токен, който bootstrap издава, и ползва собствените агент за натоварване и мок на шлюза.