0Pricing
AI Prompt Engineering · Урок

Стратегии журналирования и документирования

Сохраняйте версии запросов, входные и выходные данные для воспроизводимой отладки.

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

Почему важно журналировать запросы

Без журналирования сбои запросов остаются незаметными, пока о них не сообщит пользователь. С журналированием можно:

  • Обнаруживать регрессии сразу после их возникновения
  • Точно воспроизводить любой прошлый сбой
  • Измерять улучшения со временем по мере развития запросов
  • Проверять поведение модели на соответствие требованиям и безопасность

Журналирование не является необязательным для рабочих систем запросов — это основа надёжных приложений на базе LLM.

Минимально необходимая запись журнала

При каждом взаимодействии с запросом необходимо как минимум записывать следующие поля:

  • timestamp: ISO 8601 UTC
  • prompt_id: какой шаблон запроса использовался
  • model: точное название и версия модели
  • temperature: параметр выборки
  • input: сообщение пользователя (или хеш, если есть PII)
  • output: ответ модели
  • latency_ms: время отклика
  • tokens_used: токены входных и выходных данных
import time, json
from datetime import datetime, timezone

def logged_call(prompt_id, system_prompt, user_message, model='gpt-4o', temperature=0.7):
    start = time.time()
    resp = client.chat.completions.create(
        model=model,
        messages=[
            {'role': 'system', 'content': system_prompt},
            {'role': 'user', 'content': user_message}
        ],
        temperature=temperature
    )
    latency = int((time.time() - start) * 1000)
    output = resp.choices[0].message.content
    log_entry = {
        'timestamp': datetime.now(timezone.utc).isoformat(),
        'prompt_id': prompt_id,
        'model': model,
        'temperature': temperature,
        'input': user_message,
        'output': output,
        'latency_ms': latency,
        'input_tokens': resp.usage.prompt_tokens,
        'output_tokens': resp.usage.completion_tokens
    }
    append_log(log_entry)
    return output

Формат структурированного журналирования

Используйте JSON с разделителями-новыми строками (JSONL) для файлов журнала. Каждая строка представляет собой полный корректный объект JSON. Этот формат:

  • Позволяет легко дополнять журнал без блокировок
  • Удобен для чтения с помощью jq, pandas и всех средств агрегации журналов
  • Подходит для потоковой обработки — каждую строку можно обрабатывать по мере поступления
import json

LOG_FILE = 'prompt_logs.jsonl'

def append_log(entry):
    with open(LOG_FILE, 'a') as f:
        f.write(json.dumps(entry) + '\n')

def read_logs():
    with open(LOG_FILE) as f:
        return [json.loads(line) for line in f if line.strip()]

# Query: all entries for prompt_id 'summarize_v3'
logs = read_logs()
summarize_logs = [e for e in logs if e['prompt_id'] == 'summarize_v3']
print(f'Total calls to summarize_v3: {len(summarize_logs)}')

Версионирование запросов

Запросы со временем меняются. Без версионирования нельзя воспроизвести прошлое поведение или сравнить выходные данные модели для разных версий запроса. Используйте идентификатор версии в каждой записи журнала.

Простое версионирование: строка семантической версии (например, v1.2.3) или хеш изменения Git. Храните версии запросов в отдельном файле, чтобы любую версию можно было получить для повторного воспроизведения.

PROMPTS = {
    'summarize': {
        'v1': 'Summarize the following text.',
        'v2': 'Summarize the following text in 3 sentences.',
        'v3': 'Summarize the following text in exactly 3 sentences. '
              'Start each sentence on a new line. No bullet points.'
    }
}

CURRENT_VERSIONS = {'summarize': 'v3'}

def get_prompt(prompt_id):
    version = CURRENT_VERSIONS[prompt_id]
    return version, PROMPTS[prompt_id][version]

version, prompt = get_prompt('summarize')
log_entry['prompt_version'] = version

Обработка PII в журналах

Входные данные пользователя могут содержать персональные данные, позволяющие установить личность (PII). Журналирование необработанных входных данных может нарушать GDPR или CCPA. Возможные варианты:

  • Хеширование: сохранять SHA-256 входных данных — можно воспроизводить для удаления дубликатов, но не для повторного воспроизведения
  • Маскирование: использовать регулярное выражение или модель NER, чтобы заменять PII перед журналированием
  • Раздельное хранение: журналировать PII в зашифрованном хранилище с контролем доступа; в основном журнале сохранять только ссылочный ID
import hashlib, re

def redact_pii(text):
    # Redact email addresses
    text = re.sub(r'[\w.-]+@[\w.-]+\.\w+', '[EMAIL]', text)
    # Redact phone numbers (US format)
    text = re.sub(r'\b\d{3}[-.]\d{3}[-.]\d{4}\b', '[PHONE]', text)
    return text

def hash_input(text):
    return hashlib.sha256(text.encode()).hexdigest()[:16]

log_entry['input'] = redact_pii(user_message)
log_entry['input_hash'] = hash_input(user_message)

Отслеживание задержки и стоимости

Журналы позволяют создавать панели мониторинга стоимости и задержки. Отслеживайте показатели для каждой версии запроса, чтобы обнаруживать регрессии производительности или стоимости после изменения запроса:

def compute_cost(entry, price_per_1m_input=5.0, price_per_1m_output=15.0):
    input_cost = entry['input_tokens'] / 1_000_000 * price_per_1m_input
    output_cost = entry['output_tokens'] / 1_000_000 * price_per_1m_output
    return input_cost + output_cost

def prompt_stats(prompt_id, version):
    logs = [e for e in read_logs()
            if e['prompt_id'] == prompt_id and e.get('prompt_version') == version]
    if not logs:
        return
    avg_latency = sum(e['latency_ms'] for e in logs) / len(logs)
    total_cost = sum(compute_cost(e) for e in logs)
    print(f'{prompt_id} {version}: {len(logs)} calls, avg {avg_latency:.0f}ms, total ${total_cost:.4f}')

Журналирование оценки выходных данных

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

def evaluated_call(prompt_id, system_prompt, user_message, evaluator_fn):
    output = logged_call(prompt_id, system_prompt, user_message)
    score = evaluator_fn(user_message, output)
    # Update the last log entry with the evaluation score
    logs = read_logs()
    last = logs[-1]
    last['eval_score'] = score
    last['eval_pass'] = score >= 0.8
    # Rewrite the last line
    with open(LOG_FILE, 'a') as f:
        # In practice, use a DB or separate eval log
        pass
    return output, score

Документирование запросов

Для каждого шаблона запроса должна существовать сопутствующая документация, содержащая:

  • Назначение: какую задачу выполняет этот запрос
  • Переменные: какие заполнители существуют и какие значения они ожидают
  • Известные ограничения: входные данные, на которых запрос заведомо даёт сбой
  • История версий: что изменилось в каждой версии и почему
  • Проверочные примеры: ссылка на набор проверок для этого запроса
PROMPT_DOCS = {
    'summarize': {
        'purpose': 'Summarize a single text passage into 3 sentences.',
        'variables': {'text': 'The passage to summarize (max 2000 tokens)'},
        'known_limitations': [
            'Fails to preserve numbers accurately for texts with many statistics',
            'May not summarize correctly for non-English text'
        ],
        'versions': {
            'v1': 'Initial version — vague length instruction',
            'v2': 'Added 3-sentence limit',
            'v3': 'Added line-break and no-bullet formatting fix'
        },
        'test_suite': 'tests/test_summarize.py'
    }
}

Использование централизованных служб журналирования

Для рабочих систем записывайте журналы в централизованный сервис, а не в локальные файлы:

  • LangSmith: встроенная в LangChain платформа трассировки и оценки
  • Weights and Biases Prompts: отслеживание экспериментов с запросами
  • Datadog / Grafana: стандартные операционные панели мониторинга с настраиваемыми показателями
  • Supabase / PostgreSQL: запрос журналов с помощью языка структурированных запросов для разового анализа

Структура остаётся той же; меняется только место назначения.

# Example: writing to Supabase
from supabase import create_client

supabase = create_client('https://xxx.supabase.co', 'your-anon-key')

def log_to_supabase(entry):
    supabase.table('prompt_logs').insert(entry).execute()

# Now query with SQL:
# SELECT prompt_id, prompt_version, AVG(latency_ms), COUNT(*)
# FROM prompt_logs
# WHERE timestamp > NOW() - INTERVAL '7 days'
# GROUP BY prompt_id, prompt_version
# ORDER BY COUNT(*) DESC;

Оповещение о всплесках сбоев

Настройте оповещения, когда доля сбоев превышает заданный порог. Например, если более 10% вызовов промпта возвращают некорректный JSON за 5-минутный период, отправляйте оповещение.

from collections import deque
from datetime import datetime, timezone, timedelta

recent_results = deque(maxlen=100)  # sliding window

def track_and_alert(prompt_id, success, alert_fn, threshold=0.10):
    recent_results.append({'success': success, 'time': datetime.now(timezone.utc)})
    window = [
        r for r in recent_results
        if r['time'] > datetime.now(timezone.utc) - timedelta(minutes=5)
    ]
    if not window:
        return
    fail_rate = sum(1 for r in window if not r['success']) / len(window)
    if fail_rate > threshold:
        alert_fn(f'ALERT: {prompt_id} failure rate {fail_rate:.0%} in last 5 min')

Хранение и архивирование

Определите политику хранения журналов:

  • Необработанные журналы вызовов: 30 дней (скользящий период) — большой объём, необходимы для отладки недавних проблем
  • Агрегированные показатели: 1 год — необходимы для анализа тенденций и прогнозирования затрат
  • Журналы сбоев: бессрочно — необходимы для анализа закономерностей, связанных с первопричинами

Сжимайте и архивируйте необработанные журналы через 30 дней. Никогда не удаляйте журналы сбоев — это ваша накопленная база знаний по проектированию промптов.

Проверка знаний

Каково основное преимущество использования формата JSONL (JSON с разделителями строк) для журналов промптов по сравнению с одним большим массивом JSON?

Итоги: ведение журналов и документация

Основные методы ведения журналов и документирования промптов:

  • Записывайте каждый вызов: временная метка, идентификатор промпта, версия, модель, температура, входные данные, выходные данные, задержка, токены
  • Используйте формат JSONL: удобен для добавления записей и доступен для запросов с помощью стандартных инструментов
  • Версионируйте промпты: каждое изменение получает новую версию; журналы ссылаются на эту версию
  • Обрабатывайте PII: удаляйте или хешируйте конфиденциальные входные данные перед записью в журнал
  • Отслеживайте затраты и задержку: выявляйте ухудшения после обновления промпта
  • Настраивайте оповещения о всплесках сбоев: отслеживайте долю сбоев в скользящем окне

На этом завершается курс 17 по отладке сбоев промптов. Далее: инъекция промпта и защита от неё.

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

Урок «Стратегии журналирования и документирования» бесплатный?

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

Чему я научусь в уроке «Стратегии журналирования и документирования»?

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

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

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

Сколько времени занимает урок «Стратегии журналирования и документирования»?

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

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

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

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

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