0Pricing
AI Prompt Engineering · Урок

Архитектура реестра запросов

Хранение запросов как версионируемых артефактов с метаданными и тегами.

«Архитектура реестра запросов» — бесплатный урок AI Prompt Engineering на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Prompt Engineering, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Prompt Engineering содержит 4 уроков всего.

Зачем нужен реестр промптов?

Без реестра промпты хранятся разрозненно в коде, конфигурационных файлах и памяти разработчиков. Реестр промптов — это централизованное хранилище, в котором каждый промпт рассматривается как версионируемый и отслеживаемый артефакт — так же, как программный код.

К преимуществам относятся воспроизводимость, возможность аудита, возможность отката и совместная работа команды.

Основные поля артефакта промпта

Каждый артефакт промпта должен содержать следующие поля:

  • prompt_id — уникальный стабильный идентификатор (например, summarize-article)
  • version — строка семантической версии (например, 2.1.0)
  • template — фактический текст промпта с заполнителями {variable}
  • metadata — автор, теги, целевая модель, дата создания, описание

Проектирование схемы базы данных

Реляционная схема для реестра промптов хранит промпты и историю их версий в отдельных таблицах, обеспечивая эффективный поиск и аудит.

-- prompts table: one row per unique prompt identity
CREATE TABLE prompts (
  prompt_id   VARCHAR(100) PRIMARY KEY,
  description TEXT,
  created_at  TIMESTAMP DEFAULT NOW()
);

-- prompt_versions table: one row per versioned artifact
CREATE TABLE prompt_versions (
  id          SERIAL PRIMARY KEY,
  prompt_id   VARCHAR(100) REFERENCES prompts(prompt_id),
  version     VARCHAR(20)  NOT NULL,
  template    TEXT         NOT NULL,
  author      VARCHAR(100),
  tags        TEXT[],
  model       VARCHAR(50),
  is_active   BOOLEAN DEFAULT FALSE,
  created_at  TIMESTAMP DEFAULT NOW(),
  UNIQUE(prompt_id, version)
);

Проектирование реестра на основе файлов

Для небольших команд реестр на основе файлов использует организованную структуру каталогов. Для каждого промпта создаётся папка, а каждая версия представлена YAML- или JSON-файлом внутри неё.

# Directory structure
prompts/
  summarize-article/
    1.0.0.yaml
    1.1.0.yaml
    latest -> 1.1.0.yaml  # symlink
  classify-sentiment/
    1.0.0.yaml

# Example: summarize-article/1.1.0.yaml
prompt_id: summarize-article
version: '1.1.0'
model: gpt-4o-mini
author: alice@company.com
tags: [summarization, articles, english]
created_at: '2024-06-01T10:00:00Z'
template: |
  Summarize the following article in {num_sentences} sentences.
  Focus on: {focus_area}.

  Article:
  {article_text}

Класс PromptRegistry на Python

Простой класс на Python инкапсулирует доступ к базе данных и предоставляет понятные методы: register(), get_active() и list_versions().

import psycopg2
import json
from datetime import datetime

class PromptRegistry:
    def __init__(self, dsn):
        self.conn = psycopg2.connect(dsn)

    def register(self, prompt_id, version, template, author, tags, model):
        with self.conn.cursor() as cur:
            # Ensure prompt identity exists
            cur.execute(
                'INSERT INTO prompts (prompt_id) VALUES (%s) ON CONFLICT DO NOTHING',
                (prompt_id,)
            )
            cur.execute(
                '''INSERT INTO prompt_versions
                   (prompt_id, version, template, author, tags, model)
                   VALUES (%s, %s, %s, %s, %s, %s)''',
                (prompt_id, version, template, author, tags, model)
            )
        self.conn.commit()
        print(f'Registered {prompt_id}@{version}')

    def get_active(self, prompt_id):
        with self.conn.cursor() as cur:
            cur.execute(
                'SELECT template, version FROM prompt_versions '
                'WHERE prompt_id=%s AND is_active=TRUE LIMIT 1',
                (prompt_id,)
            )
            row = cur.fetchone()
        if not row:
            raise ValueError(f'No active version for {prompt_id}')
        return {'template': row[0], 'version': row[1]}

Подробный разбор схемы метаданных

Богатые метаданные делают реестр полезным не только для простого хранения. Основные поля метаданных:

  • автор — ответственное лицо и контактная информация
  • теги — доступные для поиска метки, такие как ['production', 'summarization', 'v2']
  • модель — целевая модель (промпт может зависеть от конкретной модели)
  • журнал изменений — понятное человеку описание того, что изменилось
  • набор_проверок — ссылка на набор данных для оценки этого промпта
# Extended metadata example
prompt_metadata = {
    'prompt_id': 'extract-key-dates',
    'version': '2.0.0',
    'author': 'bob@company.com',
    'tags': ['extraction', 'dates', 'contracts', 'production'],
    'model': 'gpt-4o',
    'changelog': 'Added support for relative dates (next quarter, end of year)',
    'test_suite': 's3://company-evals/extract-key-dates/v2-testset.jsonl',
    'created_at': '2024-07-15T09:30:00Z',
    'is_active': True
}

Подстановка переменных в шаблон

В шаблонах промптов используется синтаксис заполнителей. Реестр формирует итоговый промпт, подставляя в шаблон переменные времени выполнения. Использование Python-выражения str.format_map() безопасно и просто.

class PromptRegistry:
    # ... (previous methods)

    def render(self, prompt_id, variables: dict) -> str:
        artifact = self.get_active(prompt_id)
        template = artifact['template']
        try:
            rendered = template.format_map(variables)
        except KeyError as e:
            raise ValueError(f'Missing variable {e} for prompt {prompt_id}')
        return rendered

# Usage
registry = PromptRegistry(dsn='postgresql://...')
prompt = registry.render(
    'summarize-article',
    {
        'num_sentences': 3,
        'focus_area': 'financial impact',
        'article_text': 'Apple reported record revenue of $119B...'
    }
)
print(prompt)
# Output: Summarize the following article in 3 sentences.
# Focus on: financial impact. ...

Активация версии

В рабочей среде одновременно должна быть активной только одна версия промпта. Активация должна выполняться атомарно: деактивировать текущую версию и активировать новую — всё в одной транзакции, чтобы не возникало промежутков без активной версии.

def activate_version(self, prompt_id, version):
    with self.conn.cursor() as cur:
        # Deactivate all current versions
        cur.execute(
            'UPDATE prompt_versions SET is_active=FALSE '
            'WHERE prompt_id=%s AND is_active=TRUE',
            (prompt_id,)
        )
        # Activate target version
        cur.execute(
            'UPDATE prompt_versions SET is_active=TRUE '
            'WHERE prompt_id=%s AND version=%s',
            (prompt_id, version)
        )
        if cur.rowcount == 0:
            self.conn.rollback()
            raise ValueError(f'Version {version} not found for {prompt_id}')
    self.conn.commit()
    print(f'Activated {prompt_id}@{version}')

Вывод списка и поиск промптов

Реестр полезен только в том случае, если вы можете находить его содержимое. Поддерживайте поиск по тегам и вывод списка всех версий заданного промпта.

def list_versions(self, prompt_id):
    with self.conn.cursor() as cur:
        cur.execute(
            'SELECT version, author, is_active, created_at '
            'FROM prompt_versions WHERE prompt_id=%s '
            'ORDER BY created_at DESC',
            (prompt_id,)
        )
        return cur.fetchall()

def search_by_tag(self, tag):
    with self.conn.cursor() as cur:
        cur.execute(
            'SELECT prompt_id, version, tags FROM prompt_versions '
            'WHERE %s = ANY(tags)',
            (tag,)
        )
        return cur.fetchall()

# Usage
for v in registry.list_versions('summarize-article'):
    print(v)  # ('1.1.0', 'alice', True, datetime(...))

for p in registry.search_by_tag('production'):
    print(p)  # ('summarize-article', '1.1.0', ['production', 'summarization'])

Конечные точки API реестра

Предоставьте доступ к реестру через REST API, чтобы все сервисы (серверная часть, конвейеры машинного обучения, инструменты оценки) использовали единый источник достоверных данных. Основные конечные точки:

  • POST /prompts/{id}/versions — зарегистрировать новую версию
  • GET /prompts/{id}/active — получить активный шаблон
  • PUT /prompts/{id}/activate/{version} — активировать версию
  • GET /prompts — вывести список всех промптов с метаданными
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()
registry = PromptRegistry(dsn='postgresql://user:pass@localhost/prompts')

class VersionPayload(BaseModel):
    version: str
    template: str
    author: str
    tags: list
    model: str

@app.post('/prompts/{prompt_id}/versions')
def register_version(prompt_id: str, payload: VersionPayload):
    registry.register(
        prompt_id, payload.version, payload.template,
        payload.author, payload.tags, payload.model
    )
    return {'status': 'registered'}

@app.get('/prompts/{prompt_id}/active')
def get_active(prompt_id: str):
    try:
        return registry.get_active(prompt_id)
    except ValueError as e:
        raise HTTPException(404, str(e))

@app.put('/prompts/{prompt_id}/activate/{version}')
def activate(prompt_id: str, version: str):
    registry.activate_version(prompt_id, version)
    return {'status': 'activated'}

Журнал аудита и история изменений

Каждое событие активации, деактивации и регистрации должно регистрироваться с отметкой времени и указанием исполнителя. Этот журнал аудита необходим для отладки инцидентов в рабочей среде и соблюдения нормативных требований.

CREATE TABLE prompt_audit_log (
  id          SERIAL PRIMARY KEY,
  prompt_id   VARCHAR(100),
  version     VARCHAR(20),
  action      VARCHAR(50),  -- 'registered', 'activated', 'deactivated'
  actor       VARCHAR(100), -- user or service that performed the action
  reason      TEXT,
  created_at  TIMESTAMP DEFAULT NOW()
);

-- Trigger to auto-log activations
CREATE OR REPLACE FUNCTION log_activation()
RETURNS TRIGGER AS $func$
BEGIN
  IF NEW.is_active != OLD.is_active THEN
    INSERT INTO prompt_audit_log (prompt_id, version, action)
    VALUES (NEW.prompt_id, NEW.version,
            CASE WHEN NEW.is_active THEN 'activated' ELSE 'deactivated' END);
  END IF;
  RETURN NEW;
END;
$func$ LANGUAGE plpgsql;

CREATE TRIGGER trg_activation
AFTER UPDATE ON prompt_versions
FOR EACH ROW EXECUTE FUNCTION log_activation();

Быстрая проверка

В схеме базы данных реестра промптов какое поле гарантирует, что в каждый момент в рабочей среде обслуживается только одна версия промпта?

Краткий обзор архитектуры реестра

Реестр промптов централизует управление промптами, рассматривая их как артефакты с версиями. Основные проектные решения:

  • Отдельная таблица идентификаторов (prompt_id) и таблица версий (версия, шаблон, метаданные)
  • Единый флаг is_active с атомарным переключением предотвращает ошибки с двумя активными версиями
  • Богатые метаданные (автор, теги, модель, журнал изменений) поддерживают поиск и аудит
  • Слой REST API делает реестр доступным для всех сервисов
  • Журнал аудита помогает соблюдать нормативные требования и разбирать инциденты

Реестры на основе файлов подходят для небольших команд; для рабочих систем с несколькими командами и высокой доступностью предпочтительны реестры на основе БД.

Часто задаваемые вопросы

Урок «Архитектура реестра запросов» бесплатный?

Да — полный текст урока «Архитектура реестра запросов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Prompt Engineering, подпишись на CoddyKit PRO. Курс AI Prompt Engineering содержит 4 уроков всего.

Чему я научусь в уроке «Архитектура реестра запросов»?

Хранение запросов как версионируемых артефактов с метаданными и тегами. Ты практикуешь AI Prompt Engineering с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать AI Prompt Engineering?

Предыдущий опыт не требуется. AI Prompt Engineering на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Архитектура реестра запросов»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке AI Prompt Engineering?

Да. Каждый урок AI Prompt Engineering включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Архитектура реестра запросов
  2. Контроль версий запросов
  3. Стратегии развёртывания и отката
  4. Мониторинг производительности запросов в рабочей среде
← Назад к AI Prompt Engineering