by Anysphere (open source) TypeScript / Node.js, OpenAI API, любой LLM с токенным лимитом
Priompt (priority + prompt) — библиотека для управления контекстным окном через приоритеты. Разработана командой Cursor: позволяет декларативно описать что должно попасть в промпт, и автоматически обрезает низкоприоритетный контент под токенный лимит. Возможности: - JSX-подобный синтаксис для компоновки промптов - Приоритеты: `<scope p={1}>` — высокоприоритетный блок всегда попадает - Автоматическая обрезка по токенному лимиту - Счётчик токенов через tiktoken - Preview-режим: посмотреть финальный промпт до отправки - TypeScript-типизация для всех компонентов - Используется в production в Cursor
# npm:
npm install @anysphere/priompt
# Пример:
import { render, scope, first } from "@anysphere/priompt";
const prompt = (
<scope p={1}>
<system>You are a helpful assistant.</system>
<first>
{recentMessages.map(m => <message role={m.role}>{m.content}</message>)}
</first>
<scope p={0.5}>
{/* Этот блок обрежется первым при нехватке токенов */}
<docs>{longDocumentation}</docs>
</scope>
</scope>
);
const rendered = await render(prompt, { tokenLimit: 4096 });
Priompt (priority + prompt) — это JSX-библиотека для промптинга. Она использует приоритеты для решения, что включать в окно контекста.
Priompt — это попытка создать библиотеку дизайна промптов, вдохновлённую веб-библиотеками, такими как React. Подробнее о мотивации можно прочитать здесь.
Установка через npm:
npm install @anysphere/priompt && npm install -D @anysphere/priompt-preview
или
yarn add @anysphere/priompt && yarn add --dev @anysphere/priompt-preview
или
pnpm add @anysphere/priompt && pnpm add -D @anysphere/priompt-preview
Прочитайте examples/README.md), чтобы запустить примеры.
Промпты рендерятся из JSX-компонента, который может выглядеть примерно так:
function ExamplePrompt(
props: PromptProps<{
name: string,
message: string,
history: { case: "user" | "assistant", message: string }[],
}>
): PromptElement {
const capitalizedName = props.name[0].toUpperCase() + props.name.slice(1);
return (
<>
<SystemMessage>
The user's name is {capitalizedName}. Please respond to them kindly.
</SystemMessage>
{props.history.map((m, i) => (
<scope prel={-(props.history.length - i)}>
{m.case === "user" ? (
<UserMessage>{m.message}</UserMessage>
) : (
<AssistantMessage>{m.message}</AssistantMessage>
)}
</scope>
))}
<UserMessage>{props.message}</UserMessage>
<empty tokens={1000} />
</>
);
}
Компонент рендерится только один раз. У каждого дочернего элемента есть приоритет, где более высокий приоритет означает, что элемент важнее для включения в промпт. Если приоритет не указан, элемент включается тогда и только тогда, когда включен его родитель. Абсолютные приоритеты указываются с помощью p, а относительные — с помощью prel.
В приведенном выше примере мы всегда включаем системное сообщение и последнее сообщение пользователя, а также включаем как можно больше сообщений из истории, где более поздние сообщения имеют приоритет над более ранними.
Ключевое обещание рендерера priompt:
Пусть $T$ — это лимит токенов, а $\text{Prompt}(p_\text{cutoff})$ — функция, создающая промпт путем включения всех областей (
scope) с приоритетом $p_\text{scope} \geq p_\text{cutoff}$ и никаких других. Тогда отрендеренный промпт равен $\text{\textbf{P}} = \text{Prompt}(p_\text{opt-cutoff})$, где $p_\text{opt-cutoff}$ — минимальное значение такое, что $|\text{Prompt}(p_\text{opt-cutoff})| \leq T$.
Строительными блоками промпта priompt являются:
<scope>: позволяет устанавливать абсолютные приоритеты p или относительные prel.<first>: первый дочерний элемент с достаточно высоким приоритетом будет включен, а все последующие дочерние элементы не будут. Это полезно для реализации резервных вариантов, например, фразы (результат опущен), когда результат слишком длинный.<empty>: для указания пустого пространства, полезно для выделения токенов под генерацию.<capture>: захватывает вывод и парсит его прямо внутри промпта.<isolate>: изолирует секцию промпта с собственным лимитом токенов. Это полезно для гарантии того, что начало промпта будет одинаковым для целей кэширования. Было бы неплохо расширить это, чтобы разрешить лимиты токенов вроде 100% - 100.<br/>: принудительно создает разрыв токена в определенном месте, что полезно для обеспечения точного совпадения токенизации между двумя частями промпта (например, при реализации такого функционала, как спекулятивные правки).<config>: позволяет указать несколько распространенных конфигурационных свойств, таких как токен остановки stop и maxResponseTokens, что может сделать дамп priompt более самодостаточным и помочь с оценкой (evals).Вы можете создавать сколько угодно компонентов, точно так же, как в React. Встроенные компоненты:
<UserMessage>, <AssistantMessage> и <SystemMessage>: для построения промптов на основе сообщений.<ImageComponent>: для добавления изображений в промпт.<Tools>, <ZTools>: для указания инструментов, которые ИИ может вызывать, используя JSON-схему или тип Zod.onEject и onInclude: колбэки, которые можно передать в любой scope. Они вызываются, когда область исключается или включается в финальный промпт. Это позволяет изменять логику в зависимости от того, слишком ли большой элемент для промпта.shouldBuildSourceMap в true рендерер вычисляет отображение между фактическими символами в промпте и частью дерева JSX, от которой они исходят. Это может быть полезно для определения источников промахов кэша в промпте.DO_NOT_DUMP к ключу ваших пропсов priompt, чтобы предотвратить его дамп. Это полезно для действительно больших объектов.Несколько вещей, которые было бы круто добавить:
<max>: указать лимит limit на количество токенов в области, но в отличие от <isolate>, включать вложенные области в глобальный расчет приоритетов.<first> с более высоким приоритетом содержит больше токенов, чем дочерний элемент с более низким приоритетом, реализованный в данный момент рендерер с двоичным поиском может вернуть (очень незначительно) неточный результат.Мы очень приветствуем вклад! Весь этот репозиторий находится под лицензией MIT.