Эта статья будет полезна Python-разработчикам, работающим с языковыми моделями (LLM).
Недавно мне понадобился инструмент для формирования промптов в Python-коде. Не хотелось использовать сложные решения, поэтому я создал небольшую библиотеку FlexiPrompt. Вот ее основные преимущества:
-
Легко интегрируется в существующий код
-
Позволяет быстро и гибко настраивать диалог с LLM
-
Может разделить одну LLM на несколько агентов, настраивая общение через шаблоны
Как это выглядит в коде
Вот простой пример использования FlexiPrompt:
from flexi_prompt import FlexiPrompt
fp = FlexiPrompt()
inner_fp = FlexiPrompt({"another_field1": "nested value1, "})
inner_fp.another_field2 = "nested value2"
inner_fp.another_field1().another_field2()
fp.final_prompt = "Here is: $inner_fp, $some_field, $some_callback"
fp.inner_fp = inner_fp
fp.some_field = 42
fp.some_callback = input # Пример: введите "user input"
print(fp.final_prompt().build())
# Вывод: Here is: nested value1, nested value2, 42, user input
Пример использования: Улучшение ответов LLM с самоконтролем
Давайте рассмотрим интересный пример использования FlexiPrompt. Мы создадим систему, где языковые модели будут оценивать и улучшать свои собственные ответы. Вот как это работает:
-
Получаем запрос от пользователя
-
Генерируем ответ первой нейросетью
-
Просим две разные нейросети оценить ответ и берем среднюю оценку
-
Генерируем ответ второй нейросетью
-
Снова оцениваем ответ
-
Если один из ответов получает максимальную оценку, сохраняем его как лучший и завершаем процесс
-
Повторяем шаги 2-6 до 5 раз, сохраняя лучший ответ
-
Выдаем лучший ответ пользователю
Реализация
Для этого примера мы будем использовать API OpenAI и Anthropic. Вот основная структура кода:
from flexi_prompt import FlexiPrompt
from openai import OpenAI
from anthropic import Anthropic
# Настройка API ключей и клиентов
from google.colab import userdata
os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")
os.environ["ANTHROPIC_API_KEY"] = userdata.get("ANTHROPIC_API_KEY_TEST1")
def get_openai_answer(question, openai):
openai_compleion = openai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": question},
],
)
return openai_compleion.choices[0].message.content
def get_antropic_answer(question, antropic):
message = antropic.messages.create(
max_tokens=4096,
temperature=0,
model="claude-3-haiku-20240307",
system="You are a helpful assistant.",
messages=[{"role": "user", "content": [{"type": "text", "text": question}]}],
)
return message.content[0].text
fp = FlexiPrompt()
# Настройка промптов
fp.question = "Your question here"
fp.rate_prompt = """
Rate the answer to the question from 1 to 9, where 1 is the worst answer.
Be rigorous in your evaluation. Give back only one number as your answer.
Question:
$question
Answer:
$answer
"""
# Основной цикл
MAX_ATTEMPTS = 5
THRESHOLD_SCORE = 9
best_rate = 0
best_answer = ""
for attempt in range(MAX_ATTEMPTS):
fp.answer = get_openai_answer(fp.question().build(), openai)
answer_rate = get_answer_rate(fp.rate_prompt().build(), openai, antropic)
if answer_rate > best_rate:
best_rate = answer_rate
best_answer = fp.answer
fp.answer = get_antropic_answer(fp.question().build(), antropic)
answer_rate = get_answer_rate(fp.rate_prompt().build(), openai, antropic)
if answer_rate > best_rate:
best_rate = answer_rate
best_answer = fp.answer
if best_rate >= THRESHHOLD_SCORE:
break
print(best_answer)
print("The answer rate is:", best_rate)
Этот подход позволяет получать более качественные ответы от языковых моделей, используя их собственные способности для самооценки и улучшения. Полный код примера есть на гитхабе.
Сравнение с альтернативами
Я смотрел Haystack, LangChain и несколько мелких библиотек.
Большинство out-of-the-box решений пихают кучу функций помимо промптинга. Под капотом почти все юзают jinja.
Jinja сама по себе - более тяжелое решение и не заточена под промпты. Подойдет для масштабных проектов.
FlexiPrompt заточена простые проекты. Не нужно городить классы и абстракции, но получаешь гибкость на выходе.
Планы
Пока есть очевидные вещи, которые надо добавить: возможность экранирования спец символов и безопасное добавление строк.
В дальнейшем хочется дубовый и надежный парсинг ответа, который будет учитывать вольности ответов ЛЛМ. Я думаю, это должна быть конвертация из строки в объект или срабатывание триггеров.
Автор: dmitriy_minaev