Common.AI.Prompts — Универсальный резолвер и сборщик промптов
Автор: Art Yg
Версия: 1.0
Prompts — инфраструктурный helper для унифицированной работы с промптами в сценариях Metabot и внутри других плагинов (например, Common.AI.ImageGen, LLMQuery/LMClient).
Он решает типовую боль: не держать промпты в коде, не копировать одно и то же “склеивание”, и иметь единый механизм:
- подтянуть промпт из таблицы по ссылке (
$name,@name) - применить макросы на основе
leadиbot - нормализовать вход (строка/массив/что угодно)
- собрать итоговый текст промпта из блоков (
system/user/last)
Зачем он существует
В продуктовых сценариях промпты обычно:
- растут и ветвятся по агентам (
agentName) - переезжают в таблицы/консоль управления (а не в JS)
- используют переменные окружения (lead/bot attrs)
- собираются из нескольких частей (“системный каркас” + “задача” + “контекст”)
Без Prompts это превращается в дублирование:
- вручную ходим в
table.find(...) - вручную проверяем, что
$aliasнашли - вручную подставляем
lead.getAttr(...) - вручную склеиваем массивы
Prompts стандартизирует это как маленький инфраструктурный слой, который можно подключать где угодно.
Основные принципы
-
Lead-first —
leadпередаётся явно (без неявной магии). -
Bot runtime —
botберётся из глобального окружения runtime. -
Refs only if asked — в таблицу лезем только если ты используешь
$...или@.... - Strict by default — если промпт по ссылке не найден → ошибка (чтобы не генерить “пустоту” молча).
- Composable — подходит и как отдельный helper, и как зависимость внутри других плагинов.
Минимальные требования
Для работы в “inline-режиме” достаточно:
-
lead - вызвать
Prompts.buildText(...)
Для работы с табличными ссылками ($... / @...) дополнительно нужно:
- наличие
table.findв runtime - таблица промптов (
promptTable) - агент (
agentName) — обязателен только для$name
Что умеет Prompts
1) Ссылки на промпты из таблицы
Поддерживаются два типа ref:
-
$name— промпт из таблицы для конкретного агента -
@name— промпт из таблицы для общего агента<<common>>
Пример:
-
$avatar_brief_generator→agentName = "orion" -
@safety_rules→agentNameне нужен (используется<<common>>)
2) Макросы (переменные из lead и bot)
Prompts умеет подставлять значения из атрибутов:
-
{{$key}}→ изlead(attr или json) -
{{$$key}}→ изbot(attr или json)
Поведение:
- если найден json-атрибут → подставляет
JSON.stringify(value) - иначе подставляет строковый
getAttr - если не найдено → подставляет пустую строку
3) Нормализация входов и сборка блоков
На вход можно дать:
-
null/undefined→ станет[] -
string→ станет[string] -
array→ останется array -
object/number→ станет[String(value)]
Дальше Prompts собирает:
-
system[] -
user[] -
last[] -
all[]=system + user + last
И может вернуть:
- либо blocks (
buildBlocks) - либо финальную строку (
buildText) черезjoin("\n\n")
Конфигурация
Все методы используют общие опции.
DEFAULTS
{
promptTable: "gpt_prompts",
agentName: null, // обязателен для "$name"
strict: true, // если промпт не найден → throw
applyMacros: true
}
Важные правила
-
agentNameобязателен только если ты используешь$name -
promptTableобязателен для$nameи@name - если нет
$/@refs — можно вообще не передаватьagentName/promptTable
API Prompts
Prompts.toArray(value)
Нормализует значение в массив строк.
Используется внутри, но можно использовать и снаружи.
Prompts.resolveOne(ref, opts)
Резолвит одну строку:
-
$name→ ищет вpromptTableпоagentName -
@name→ ищет вpromptTableпо агенту<<common>> - обычная строка → возвращается как есть
Prompts.resolveMany(list, opts)
Резолвит список refs/строк → массив строк.
Prompts.applyMacros(str, lead)
Применяет:
-
{{$key}}из lead -
{{$$key}}из bot
Prompts.buildBlocks(input, opts)
Собирает блоки по секциям:
{
system: [],
user: [],
last: [],
all: []
}
Prompts.buildText(input, opts)
Собирает итоговый prompt как строку:
- вызывает
buildBlocks - склеивает
all.join("\n\n")
Табличный формат промптов
Prompts ожидает, что таблица (promptTable) содержит хотя бы поля:
-
agent_name -
name -
prompt
Запрос выполняется через:
table.find(promptTable, ["prompt"], [
["agent_name", agent],
["name", name]
]);
Использование
Ниже примеры именно “для статьи”: чтобы читатель увидел, что есть несколько режимов и зачем это.
Примеры
Пример 1 — Inline: без таблиц, без агента
Подходит для простых сценариев и MVP.
const Prompts = require("Common.AI.Prompts");
const prompt = Prompts.buildText(
{
system: [
"You are a strict image generator. Output must be cinematic, realistic, and calm."
],
user: [
"Create a vertical mythotech Operator icon in Aurum Void aesthetic."
]
},
{ lead } // agentName/promptTable не нужны
);
// prompt — готовая строка, без обращений к таблицам
Пример 2 — Табличный промпт агента: $alias
Если используешь $..., то agentName обязателен, иначе будет throw.
const Prompts = require("Common.AI.Prompts");
const prompt = Prompts.buildText(
{
system: [
"$avatar_brief_generator" // берём из таблицы для агента orion
],
user: [
"Output should be a codex-grade artifact. No neon. No superhero vibe."
]
},
{
lead,
agentName: "orion",
promptTable: "gpt_prompts"
}
);
Пример 3 — Общий промпт: @alias (agentName не нужен)
@name всегда читается из агента <<common>>.
const Prompts = require("Common.AI.Prompts");
const prompt = Prompts.buildText(
{
system: [
"@safety_rules",
"@style_aurum_void"
],
user: [
"Create an abstract Operator icon."
]
},
{
lead,
promptTable: "gpt_prompts"
}
);
Пример 4 — Макросы: подтягиваем контекст из lead и bot
const Prompts = require("Common.AI.Prompts");
// допустим:
// lead.getAttr("actor_stage") = "scaling"
// bot.getAttr("BRAND_TONE") = "discipline, meaning, depth"
const prompt = Prompts.buildText(
{
system: [
"Tone: {{$$BRAND_TONE}}"
],
user: [
"Stage: {{$actor_stage}}",
"Create a vertical Operator artifact."
]
},
{
lead,
applyMacros: true
}
);
Пример 5 — Сборка blocks отдельно (для отладки/логирования)
Иногда полезно видеть, какие блоки получились до склейки.
const Prompts = require("Common.AI.Prompts");
const blocks = Prompts.buildBlocks(
{
system: ["@style_aurum_void", "$avatar_brief_generator"],
user: ["Generate an icon for current stage: {{$actor_stage}}"]
},
{
lead,
agentName: "orion",
promptTable: "gpt_prompts"
}
);
// blocks.system / blocks.user / blocks.all — можно сохранить в lead или tracer
Пример 6 — Мягкий режим (strict=false)
Иногда нужен режим “не падать”, а вернуть сообщение/заглушку.
const Prompts = require("Common.AI.Prompts");
const prompt = Prompts.buildText(
{
system: ["$missing_alias"],
user: ["Create an icon."]
},
{
lead,
agentName: "orion",
promptTable: "gpt_prompts",
strict: false
}
);
// В strict=false при отсутствии промпта вернётся текст-предупреждение (а не throw)
Использование внутри других плагинов
Common.AI.Prompts специально сделан как “маленький кусок инфраструктуры”, чтобы:
- не тащить целиком LMClient ради таблиц промптов
- не повторять код резолва в каждом AI-плагине
- унифицировать поведение
$alias/@aliasи макросов
Где он уже естественно применяется
-
Common.AI.ImageGen— чтобы собирать итоговыйimage.promptиз блоков и таблиц - потенциально любой “AI transport plugin”: STT, TTS, embeddings, classification, etc.
Частые ошибки и как их избежать
1) Использовали $alias, но не указали agentName
Это ошибка по контракту, будет throw:
- потому что
$alias— агентный промпт - без
agentNameневозможно определить неймспейс
Решение: передай agentName.
2) Использовали $alias / @alias, но не указали promptTable
Это тоже ошибка:
- потому что непонятно, откуда искать
Решение: передай promptTable (или оставь дефолт "gpt_prompts").
3) Хотели “просто текст”, но случайно начали строку с $
Если текст реально должен начинаться с $, то сейчас это будет воспринято как ref.
Практический паттерн: не начинай “сырой текст” с $/@.
Если прям надо — лучше добавить пробел или явную экранировку на уровне твоего контента.
Итог
Common.AI.Prompts — это базовый инфраструктурный helper, который:
- даёт единый формат сборки промптов (
system/user/last) - вытаскивает промпты из таблиц (
$agent,@common) - подставляет переменные из lead/bot
- снижает дублирование и делает AI-плагины проще и чище
Он может использоваться:
- как самостоятельный utility в сценариях
-
как зависимость внутри более крупных AI-плагинов (например,
ImageGen)
Если в твоей архитектуре дальше появятся новые источники промптов (реестр провайдеров, JSON-конфиги, версии промптов, AB-тесты) — вот этот слой и будет правильным местом для расширения. И это как раз тот случай, где можно внезапно сделать себе ловушку, если начать “подмешивать” сюда бизнес-логику — держи его инфраструктурным, иначе потом будет боль.
Нет комментариев