Самоулучшающиеся системы запросов
Циклы «обратная связь → критика → переписывание», автоматически оптимизирующие запросы.
«Самоулучшающиеся системы запросов» — бесплатный урок AI Prompt Engineering на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Prompt Engineering, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Prompt Engineering содержит 4 уроков всего.
Цикл самоулучшения
Система самоулучшения запросов создает цикл обратной связи: запускает запрос на тестовых случаях, оценивает результаты, подвергает запрос критике, переписывает его и повторяет процесс. Каждая итерация должна давать измеримо лучшие результаты. Это автоматическая оптимизация запросов без вмешательства человека на каждом цикле.
Обзор архитектуры цикла
Цикл самоулучшения состоит из пяти компонентов: Исполнитель (запускает запрос), Оценщик (оценивает результаты), Критик (выявляет слабые места запроса), Переписыватель (улучшает запрос) и История (отслеживает все итерации). Каждый из них реализован в виде вызова LLM.
import anthropic
client = anthropic.Anthropic(api_key='YOUR_API_KEY')
class SelfImprovingPromptSystem:
def __init__(self, initial_prompt, test_cases, eval_fn, max_iterations=5):
self.current_prompt = initial_prompt
self.test_cases = test_cases
self.eval_fn = eval_fn
self.max_iterations = max_iterations
self.history = [] # [(iteration, prompt, score, critique)]
def run(self):
for i in range(self.max_iterations):
print(f'=== Iteration {i+1}/{self.max_iterations} ===')
score = self._evaluate_prompt()
print(f'Score: {score:.2f}')
self.history.append((i, self.current_prompt, score))
if score >= 0.95:
print('Target score reached. Stopping.')
break
critique = self._critique_prompt(score)
self.current_prompt = self._rewrite_prompt(critique)
return self.best_prompt()
def best_prompt(self):
return max(self.history, key=lambda x: x[2])[1]Исполнитель: запуск запроса
Исполнитель применяет текущий запрос к каждому тестовому случаю и собирает результаты. Это обычный цикл вызовов LLM — ничего особенного, однако крайне важно отслеживать, какой тестовый случай породил каждый результат.
def _execute_prompt(self, prompt):
outputs = []
for case in self.test_cases:
response = client.messages.create(
model='claude-haiku-4-5', # use cheaper model for execution
max_tokens=500,
messages=[
{'role': 'user', 'content': prompt + '\n\nInput: ' + case['input']}
]
)
outputs.append({
'case_id': case['id'],
'input': case['input'],
'expected': case['expected'],
'actual': response.content[0].text
})
return outputs
# Bind method to class (demonstration)
SelfImprovingPromptSystem._execute = _execute_prompt
# Sample test cases
test_cases = [
{'id': 1, 'input': 'The meeting was cancelled.', 'expected': 'negative'},
{'id': 2, 'input': 'Great product, love it!', 'expected': 'positive'},
{'id': 3, 'input': 'It arrived on time.', 'expected': 'neutral'},
]
print(f'Test suite: {len(test_cases)} cases')Оценщик: оценка результатов
Оценщик сравнивает результаты исполнителя с ожидаемыми ответами и выставляет им оценки. Можно использовать точное совпадение, нечеткое совпадение или отдельного судью на основе LLM для задач со свободной формой ответа.
def _evaluate_prompt(self):
outputs = self._execute(self.current_prompt)
correct = 0
failed_cases = []
for out in outputs:
# Exact match for classification tasks
if out['expected'].lower() in out['actual'].lower():
correct += 1
else:
failed_cases.append(out)
score = correct / len(outputs)
self._last_failed_cases = failed_cases
return score
# For open-ended tasks: LLM-as-judge evaluator
JUDGE_PROMPT = '''Rate the quality of this AI response (1-5).
Task: {task_description}
Input: {input}
Expected approach: {expected}
Actual response: {actual}
Return only the integer score (1-5). No explanation.'''
def llm_judge_score(task_desc, input_text, expected, actual):
response = client.messages.create(
model='claude-haiku-4-5', max_tokens=5,
messages=[{'role': 'user', 'content':
JUDGE_PROMPT.format(
task_description=task_desc, input=input_text,
expected=expected, actual=actual
)}]
)
try:
return int(response.content[0].text.strip()) / 5.0
except ValueError:
return 0.5Критик: выявление слабых мест запроса
Критик анализирует случаи с ошибками и текущий запрос, чтобы выявить конкретные слабые места. Это ключевой этап метазапросов — модель подвергает критике собственный запрос.
CRITIC_PROMPT = '''You are a prompt engineering expert analyzing why a prompt fails.
Current prompt:
{current_prompt}
Failed test cases (where the prompt gave wrong outputs):
{failed_cases}
For each failure, explain:
1. What went wrong in the output
2. Which part of the prompt caused or failed to prevent this
3. A specific, actionable fix
End with a prioritized list of the top 3 prompt improvements to make.
Be specific — quote the relevant prompt section and suggest the exact replacement.'''
def _critique_prompt(self, score):
failed_json = '\n'.join(
f'Input: {c["input"]}\nExpected: {c["expected"]}\nActual: {c["actual"]}'
for c in self._last_failed_cases[:5]
)
response = client.messages.create(
model='claude-opus-4-5', max_tokens=1000,
messages=[{'role': 'user', 'content':
CRITIC_PROMPT.format(
current_prompt=self.current_prompt,
failed_cases=failed_json
)}]
)
return response.content[0].textПереписыватель: улучшение запроса
Переписыватель использует анализ критика и создает улучшенную версию запроса. Главное ограничение: это должно быть целевое улучшение, а не полная переработка.
REWRITER_PROMPT = '''You are a prompt engineer. Improve the prompt based on the critique below.
Current prompt:
{current_prompt}
Critique and suggested improvements:
{critique}
Rules for rewriting:
1. Make TARGETED changes based on the critique — do not rewrite everything
2. Keep all parts of the prompt that were working well
3. Apply all prioritized fixes from the critique
4. Do not add unnecessary verbosity — conciseness is a quality
5. Output ONLY the improved prompt, no explanation
Improved prompt:'''
def _rewrite_prompt(self, critique):
response = client.messages.create(
model='claude-opus-4-5', max_tokens=1000,
messages=[{'role': 'user', 'content':
REWRITER_PROMPT.format(
current_prompt=self.current_prompt,
critique=critique
)}]
)
new_prompt = response.content[0].text.strip()
print(f'Prompt updated. Length: {len(new_prompt)} chars '
f'(was {len(self.current_prompt)} chars)')
return new_promptПолный цикл: объединение компонентов
Ниже приведен полный цикл самоулучшения, в котором все компоненты объединены и предусмотрено базовое выявление сходимости.
def run_improvement_loop(initial_prompt, test_cases, max_iterations=5,
target_score=0.90):
history = []
current_prompt = initial_prompt
last_failed_cases = []
for i in range(max_iterations):
print(f'\n--- Iteration {i+1} ---')
# Execute
outputs = execute_prompt(current_prompt, test_cases)
# Evaluate
score, failed_cases = evaluate_outputs(outputs)
last_failed_cases = failed_cases
print(f'Score: {score:.2f} ({len(failed_cases)} failures)')
history.append({'iteration': i, 'prompt': current_prompt, 'score': score})
if score >= target_score:
print(f'Target score {target_score} reached!')
break
if not failed_cases:
print('No failures to learn from. Stopping.')
break
# Critique and rewrite
critique = critique_prompt(current_prompt, failed_cases)
current_prompt = rewrite_prompt(current_prompt, critique)
best = max(history, key=lambda x: x['score'])
print(f'\nBest prompt at iteration {best["iteration"]+1} with score {best["score"]:.2f}')
return best['prompt'], historyУправление затратами в циклах самоулучшения
Циклы самоулучшения могут быть дорогостоящими: каждая итерация выполняет несколько вызовов API. Стратегии управления затратами позволяют поддерживать приемлемую стоимость цикла.
# Cost optimization strategies
# 1. Use cheap model for execution, expensive model for critique/rewrite
def execute_prompt(prompt, test_cases):
# Use cheapest capable model
model = 'claude-haiku-4-5'
# ... execute ...
pass
def critique_prompt(prompt, failed_cases):
# Use best model for reasoning about why the prompt fails
model = 'claude-opus-4-5'
# ... critique ...
pass
# 2. Limit test suite size (sample from larger set)
import random
def get_evaluation_sample(full_test_suite, sample_size=20):
if len(full_test_suite) <= sample_size:
return full_test_suite
return random.sample(full_test_suite, sample_size)
# 3. Early stopping: stop if score doesn't improve
def has_converged(history, patience=2, min_delta=0.02):
if len(history) < patience + 1:
return False
recent_scores = [h['score'] for h in history[-patience:]]
best_recent = max(recent_scores)
baseline = history[-(patience+1)]['score']
return (best_recent - baseline) < min_delta
print('Cost optimization: cheap model for execution, expensive for critique')Отслеживание происхождения запросов
В цикле самоулучшения каждую версию запроса следует прослеживать до анализа сбоев, который вызвал ее появление. Такая информация о происхождении помогает выявлять неожиданные регрессии и поддерживает проверку человеком.
class PromptLineage:
def __init__(self):
self.lineage = []
def record(self, iteration, prompt, score, critique=None,
failed_case_ids=None):
self.lineage.append({
'iteration': iteration,
'prompt': prompt,
'score': score,
'critique_summary': critique[:100] if critique else None,
'failed_case_ids': failed_case_ids or [],
'prompt_length': len(prompt.split())
})
def print_history(self):
print('Prompt improvement history:')
for entry in self.lineage:
print(
f' Iter {entry["iteration"]}: '
f'score={entry["score"]:.2f} '
f'words={entry["prompt_length"]} '
f'failures={len(entry["failed_case_ids"])}'
)
def get_best(self):
return max(self.lineage, key=lambda x: x['score'])
lineage = PromptLineage()
lineage.record(0, 'Initial simple prompt', 0.60)
lineage.record(1, 'Improved with examples', 0.75, 'Missing edge cases')
lineage.record(2, 'Added edge case handling', 0.92, 'Minor format issue')
lineage.print_history()Визуализация истории версий запроса
Визуализация изменения оценок по итерациям помогает определить, сходится ли цикл и насколько быстро. Простая текстовая диаграмма сразу показывает тенденции без библиотеки для работы с графикой.
def visualize_improvement_history(history):
'''
history: list of (iteration, prompt, score)
Prints an ASCII chart of score progression.
'''
if not history:
print('No history to visualize.')
return
max_score = 1.0
bar_width = 40
print('\nScore Progression:')
print('-' * (bar_width + 20))
for iteration, prompt, score in history:
filled = int(score * bar_width)
bar = '#' * filled + '-' * (bar_width - filled)
marker = ' <-- BEST' if score == max(h[2] for h in history) else ''
print(f'Iter {iteration:2d}: [{bar}] {score:.3f}{marker}')
final_score = history[-1][2]
best_score = max(h[2] for h in history)
gain = best_score - history[0][2]
print('-' * (bar_width + 20))
print(f'Initial: {history[0][2]:.3f} -> Best: {best_score:.3f} (gain: +{gain:.3f})')
# Example
sample_history = [
(0, 'v0', 0.60),
(1, 'v1', 0.72),
(2, 'v2', 0.78),
(3, 'v3', 0.77),
(4, 'v4', 0.85)
]
visualize_improvement_history(sample_history)Выявление регрессий
Переписанный запрос может устранить некоторые ошибки, одновременно нарушив ранее успешно пройденные случаи, — это регрессия. После каждой итерации обязательно проверяйте наличие регрессий, сравнивая, какие конкретно тестовые случаи изменили статус.
def detect_regressions(old_outputs, new_outputs):
old_results = {o['case_id']: o['correct'] for o in old_outputs}
new_results = {o['case_id']: o['correct'] for o in new_outputs}
regressions = []
improvements = []
for case_id in old_results:
was_correct = old_results[case_id]
is_correct = new_results.get(case_id, False)
if was_correct and not is_correct:
regressions.append(case_id)
elif not was_correct and is_correct:
improvements.append(case_id)
print(f'Fixed: {len(improvements)} cases. Regressed: {len(regressions)} cases.')
if regressions:
print(f'WARNING: Regression on cases: {regressions}')
print('Consider reverting to previous prompt version.')
return regressions, improvements
# Example
old = [{'case_id': 1, 'correct': True}, {'case_id': 2, 'correct': False}]
new = [{'case_id': 1, 'correct': False}, {'case_id': 2, 'correct': True}]
detect_regressions(old, new) # Fixed: 1, Regressed: 1Быстрая проверка
Какова цель компонента «Критик» в цикле самоулучшающегося запроса?
Итоги: системы самоулучшения запросов
Системы самоулучшения запросов автоматизируют цикл оптимизации запросов:
- Структура цикла: запуск → оценка → критика → переписывание → повторение
- Исполнитель: запускает запрос на тестовых случаях (используйте недорогую модель)
- Оценщик: оценивает результаты с помощью точного совпадения или судьи на основе LLM
- Критик: метазапрос, выявляющий конкретные слабые места запроса по результатам сбоев
- Переписыватель: выполняет целевое улучшение на основе критики (используйте лучшую модель)
- Сходимость: останавливает цикл при достижении целевой оценки или прекращении ее роста
- Выявление регрессий: всегда проверяйте, что улучшения не нарушают успешно пройденные случаи
Часто задаваемые вопросы
Урок «Самоулучшающиеся системы запросов» бесплатный?
Да — полный текст урока «Самоулучшающиеся системы запросов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Prompt Engineering, подпишись на CoddyKit PRO. Курс AI Prompt Engineering содержит 4 уроков всего.
Чему я научусь в уроке «Самоулучшающиеся системы запросов»?
Циклы «обратная связь → критика → переписывание», автоматически оптимизирующие запросы. Ты практикуешь AI Prompt Engineering с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Prompt Engineering?
Предыдущий опыт не требуется. AI Prompt Engineering на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Самоулучшающиеся системы запросов»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Prompt Engineering?
Да. Каждый урок AI Prompt Engineering включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Что такое метазапросы
- Запросы, генерирующие запросы
- Самоулучшающиеся системы запросов
- Оценка и отбор в самоулучшении