Fuck You, Show Me The Prompt.
Статья Hamel Husain посвящена проблеме непрозрачности LLM-фреймворков (Guardrails, Guidance, LangChain, Instructor, DSPy), которые переписывают или конструируют промпты за пользователя, скрывая реальные API-вызовы. Автор предлагает использовать HTTPS-прокси mitmproxy для перехвата всех запросов к LLM и анализа реальных промптов. На конкретных примерах он показывает, что библиотеки часто делают избыточные API-вызовы, генерируют дублирующиеся идеи и содержат неоптимизированные промпты с опечатками. Минимальный пример DSPy выполнялся более 30 минут и сделал сотни вызовов к OpenAI без предупреждения. Главный тезис: прежде чем доверять абстракции, нужно увидеть финальный промпт — это позволяет оценить, нужен ли фреймворк вообще и можно ли написать промпт лучше самому.
Предыстория
Существует множество библиотек, которые стремятся улучшить выходные данные ваших LLM, переписывая или конструируя промпт за вас. Эти библиотеки заявляют, что делают выходные данные ваших LLM:
безопаснее (пример: guardrails) детерминированнее (пример: guidance) структурированнее (пример: instructor) устойчивее (пример: langchain) … или даже оптимизированными по произвольной метрике (пример: DSPy).
Общая черта некоторых из этих инструментов — они поощряют пользователей дистанцироваться от написания промптов.
DSPy: «Это новая парадигма, в которой языковые модели и их промпты уходят на задний план… вы можете снова скомпилировать свою программу, и DSPy создаст новые эффективные промпты»
guidance: «guidance — это парадигма программирования, которая обеспечивает превосходный контроль и эффективность по сравнению с традиционным промптингом…»
Даже когда инструменты не отговаривают от написания промптов, мне часто было сложно извлечь финальный промпт (или промпты), которые эти инструменты отправляют языковой модели. Промпты, отправляемые этими инструментами в LLM — это описание на естественном языке того, что эти инструменты делают, и это самый быстрый способ понять, как они работают. Кроме того, некоторые инструменты используют плотную терминологию для описания внутренних конструкций, что ещё больше запутывает понимание их работы.
По причинам, которые я объясню ниже, я считаю, что большинству людей будет полезен следующий подход:
В этом посте я покажу, как можно перехватывать API-вызовы с промптами для любого инструмента, не продираясь через документацию и не читая исходный код. Я покажу, как настроить и использовать mitmproxy на примерах LLM-инструментов, которые я упоминал ранее.
Мотивация: минимизировать случайную сложность
Прежде чем внедрять абстракцию, важно оценить опасность привнесения случайной сложности. Для LLM-абстракций эта опасность особенно остра по сравнению с обычными программными абстракциями. LLM-абстракции зачастую заставляют пользователя деградировать к написанию кода вместо общения с ИИ на естественном языке, что может противоречить самому назначению LLM:
Программная абстракция → человекоподобный язык, на котором можно перевести задачу в машинный код LLM-абстракция → непонятный фреймворк, на котором можно перевести задачу в человеческий язык — Hamel Husain (@HamelHusain) 5 февраля 2024
Хотя это и ироничное замечание, его стоит держать в голове при оценке инструментов. Есть два основных типа автоматизации, которые предоставляют инструменты:
Переплетение кода и LLM: Выражать такую автоматизацию зачастую лучше всего через код, поскольку для выполнения задачи код должен быть запущен. Примеры: маршрутизация, вызов функций, повторные попытки, цепочки и т. д. Переписывание и конструирование промптов: Выражать намерение зачастую лучше всего на естественном языке. Однако бывают исключения! Например, удобнее задать определение функции или схему из кода, а не на естественном языке.
Многие фреймворки предлагают оба типа автоматизации. Однако перебор со вторым типом может иметь негативные последствия. Просмотр промпта позволяет вам решить:
Действительно ли этот фреймворк необходим? Может, стоит просто забрать финальный промпт (строку) и выбросить фреймворк? Можем ли мы написать промпт лучше (короче, точнее передающий ваше намерение и т. д.)? Оптимален ли этот подход (адекватно ли количество API-вызовов)?
По моему опыту, видеть промпты и API-вызовы — необходимое условие для принятия обоснованных решений.
Перехват API-вызовов LLM
Существует множество способов перехватить API-вызовы LLM, например, monkey-патчинг исходного кода или поиск пользовательской настройки. Я обнаружил, что эти подходы отнимают слишком много времени, поскольку качество исходного кода и документации сильно варьируется. В конце концов, я просто хочу видеть API-вызовы, не разбираясь в том, как устроен код!
Независимый от фреймворка способ увидеть API-вызовы — настроить прокси, который логирует ваши исходящие API-запросы. Это легко сделать с помощью mitmproxy — бесплатного HTTPS-прокси с открытым исходным кодом.
Настройка mitmproxy
Вот один из удобных способов настройки mitmproxy, дружелюбный к новичкам для наших целей:
Следуйте инструкциям по установке на сайте Запустите интерактивный интерфейс командой mitmweb в терминале. Обратите внимание на URL интерактивного интерфейса в логах — он будет выглядеть примерно так: Web server listening at http://127.0.0.1:8081/ Далее нужно настроить ваше устройство (то есть ноутбук) так, чтобы весь трафик шёл через mitproxy, который слушает на http://localhost:8080. Согласно документации: Мы рекомендуем просто поискать в интернете, как настроить HTTP-прокси для вашей системы. В некоторых операционных системах есть глобальные настройки, в некоторых браузерах — свои, другие приложения используют переменные окружения и т. д. В моём случае поиск в Google по запросу «set proxy for macos» дал такие результаты: выберите меню Apple > Системные настройки, нажмите «Сеть» на боковой панели, выберите сетевой сервис справа, нажмите «Подробнее», затем «Прокси». Затем я ввожу localhost и 8080 в соответствующие поля интерфейса: Далее перейдите по адресу http://mitm.it — там вы найдёте инструкции по установке сертификата центра сертификации (CA) mitmproxy, который необходим для перехвата HTTPS-запросов. (Это также можно сделать вручную здесь.) Также запомните расположение файла CA — он понадобится нам позже. Проверить работоспособность можно, зайдя на сайт вроде https://mitmproxy.org/ и увидев соответствующий вывод в интерфейсе mitmweb, который у меня расположен по адресу http://127.0.0.1:8081/ (URL указан в логах терминала). Теперь, когда всё настроено, можно отключить прокси, который вы ранее включили в сетевых настройках. На Mac я делаю это, переключая кнопки прокси на скриншоте, показанном выше. Это нужно потому, что мы хотим ограничить прокси только программой на Python, чтобы убрать лишний шум.
Сетевое ПО обычно позволяет проксировать исходящие запросы через переменные окружения. Именно этот подход мы будем использовать, чтобы ограничить действие прокси конкретными программами на Python. Но я рекомендую поэкспериментировать и с другими типами программ, когда освоитесь!
Переменные окружения для Python
Нам нужно установить следующие переменные окружения, чтобы библиотеки requests и httpx направляли трафик через прокси и использовали файл CA для HTTPS-трафика:
Убедитесь, что вы установили эти переменные окружения перед запуском любого из фрагментов кода в этом посте.
import os # Расположение моего файла CA cert_file = '/Users/hamel/Downloads/mitmproxy-ca-cert.pem' os.environ['REQUESTS_CA_BUNDLE'] = cert_file os.environ['SSL_CERT_FILE'] = cert_file os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:8080'
Для минимальной проверки можно запустить следующий код:
import requests requests.post('https://httpbin.org/post', data={'key': 'value'})
В интерфейсе это будет выглядеть так:
Примеры
А теперь самое интересное — давайте разберём несколько примеров LLM-библиотек и перехватим их API-вызовы!
Guardrails
Guardrails позволяет задавать структуру и типы данных, которые затем используются для валидации и коррекции выходных данных больших языковых моделей. Вот пример hello world из README guardrails-ai/guardrails:
from pydantic import BaseModel, Field from guardrails import Guard import openai class Pet(BaseModel): pet_type: str = Field(description="Species of pet") name: str = Field(description="a unique pet name") prompt = """ What kind of pet should I get and what should I name it? ${gr.complete_json_suffix_v2} """ guard = Guard.from_pydantic(output_class=Pet, prompt=prompt) validated_output, *rest = guard( llm_api=openai.completions.create, engine="gpt-3.5-turbo-instruct" ) print(f"{validated_output}")
{ "pet_type": "dog", "name": "Buddy
Что тут происходит? Как работают структурированный вывод и валидация? Заглянув в интерфейс mitmproxy, я вижу, что приведённый код сделал два API-вызова к LLM, первый — с таким промптом:
What kind of pet should I get and what should I name it? Given below is XML that describes the information to extract from this document and the tags to extract it into. ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. Here are examples of simple (XML, JSON) pairs that show the expected behavior: - `
` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}` - `` => `{'baz': {'foo': 'Some String', 'index': 1}}`
За которым последовал ещё один вызов с таким промптом:
I was given the following response, which was not parseable as JSON. "{\n \"pet_type\": \"dog\",\n \"name\": \"Buddy" Help me correct this by making it valid JSON. Given below is XML that describes the information to extract from this document and the tags to extract it into. ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`.
Ого. Сколько церемоний ради структурированного вывода! Мы узнали, что этот подход библиотеки к структурированному выводу использует XML-схемы (тогда как другие используют вызов функций). Стоит задуматься, не можете ли вы придумать подход лучше или проще, теперь когда магия развеяна. В любом случае, мы получили представление о внутренней работе без погружения в ненужную сложность, и это уже победа.
Guidance
Guidance предлагает ограниченную генерацию и программные конструкции для написания промптов. Давайте разберём пример чата из их туториалов:
import guidance gpt35 = guidance.models.OpenAI("gpt-3.5-turbo") import re from guidance import gen, select, system, user, assistant @guidance def plan_for_goal(lm, goal: str): # This is a helper function which we will use below def parse_best(prosandcons, options): best = re.search(r'Best=(\d+)', prosandcons) if not best: best = re.search(r'Best.*?(\d+)', 'Best= option is 3') if best: best = int(best.group(1)) else: best = 0 return options[best] # Some general instruction to the model with system(): lm += "You are a helpful assistant." # Simulate a simple request from the user # Note that we switch to using 'lm2' here, because these are intermediate steps (so we don't want to overwrite the current lm object) with user(): lm2 = lm + f"""\ I want to {goal} Can you please generate one option for how to accomplish this? Please make the option very short, at most one line.""" # Generate several options. Note that this means several sequential generation requests n_options = 5 with assistant(): options = [] for i in range(n_options): options.append((lm2 + gen(name='option', temperature=1.0, max_tokens=50))["option"]) # Have the user request pros and cons with user(): lm2 += f"""\ I want to {goal} Can you please comment on the pros and cons of each of the following options, and then pick the best option? --- """ for i, opt in enumerate(options): lm2 += f"Option {i}: {opt}\n" lm2 += f"""\ --- Please discuss each option very briefly (one line for pros, one for cons), and end by saying Best=X, where X is the number of the best option.""" # Get the pros and cons from the model with assistant(): lm2 += gen(name='prosandcons', temperature=0.0, max_tokens=600, stop="Best=") + "Best=" + gen("best", regex="[0-9]+") # The user now extracts the one selected as the best, and asks for a full plan # We switch back to 'lm' because this is the final result we want with user(): lm += f"""\ I want to {goal} Here is my plan: {options[int(lm2["best"])]} Please elaborate on this plan, and tell me how to best accomplish it.""" # The plan is generated with assistant(): lm += gen(name='plan', max_tokens=500) return lm
results = gpt35 + plan_for_goal(goal="read more books")
systemYou are a helpful assistant.userI want to read more books Here is my plan: Set aside 30 minutes of dedicated reading time each day. Please elaborate on this plan, and tell me how to best accomplish it.assistantSetting aside 30 minutes of dedicated reading time each day is a great plan to read more books. Here are some tips to help you accomplish this goal: 1. Establish a routine: Choose a specific time of day that works best for you, whether it's in the morning, during lunch break, or before bed. Consistency is key to forming a habit. 2. Create a reading-friendly environment: Find a quiet and comfortable spot where you can focus on your reading without distractions. It could be a cozy corner in your home, a park bench, or a local library. 3. Minimize distractions: Put away your phone, turn off the TV, and avoid any other potential interruptions during your dedicated reading time. This will help you stay focused and fully immerse yourself in the book. 4. Choose books that interest you: Select books that align with your personal interests, hobbies, or goals. When you're genuinely interested in the subject matter, you'll be more motivated to read regularly. 5. Start with manageable goals: If you're new to reading or have a busy schedule, start with a smaller time commitment, such as 15 minutes, and gradually increase it to 30 minutes or more as you become more comfortable. 6. Set a timer: Use a timer or a reading app that allows you to track your reading time. This will help you stay accountable and ensure that you dedicate the full 30 minutes to reading. 7. Make reading enjoyable: Create a cozy reading atmosphere by lighting a candle, sipping a cup of tea, or playing soft background music. Engaging all your senses can enhance your reading experience. 8. Join a book club or reading group: Consider joining a book club or participating in a reading group to connect with fellow book lovers. This can provide additional motivation, discussion opportunities, and book recommendations. 9. Keep a reading log: Maintain a record of the books you've read, along with your thoughts and reflections. This can help you track your progress, discover patterns in your reading preferences, and serve as a source of inspiration for future reading. 10. Be flexible: While it's important to have a dedicated reading time, be flexible and adaptable. Life can sometimes get busy, so if you miss a day, don't be discouraged. Simply pick up where you left off and continue with your reading routine. Remember, the goal is to enjoy the process of reading and make it a regular part of your life. Happy reading!
Выглядит неплохо! Но что именно происходит? Этот код делает в общей сложности 7 вызовов к OpenAI, которые я собрал в этом gist. 5 из 7 этих API-вызовов — «внутренние» размышления, просящие LLM сгенерировать идеи. Несмотря на то, что temperature установлена на 1.0, эти «идеи» в основном дублируют друг друга. Предпоследний вызов к OpenAI перечисляет эти «идеи», которые я привожу ниже:
I want to read more books Can you please comment on the pros and cons of each of the following options, and then pick the best option? --- Option 0: Set aside dedicated time each day for reading. Option 1: Set aside 30 minutes of dedicated reading time each day. Option 2: Set aside dedicated time each day for reading. Option 3: Set aside dedicated time each day for reading. Option 4: Join a book club. --- Please discuss each option very briefly (one line for pros, one for cons), and end by saying Best=X, where X is the number of the best option.
По опыту я знаю, что результаты будут лучше, если попросить языковую модель сгенерировать идеи за один раз. Так LLM может ссылаться на предыдущие идеи и добиться большего разнообразия. Это хороший пример случайной сложности: очень соблазнительно взять этот паттерн проектирования и применить его вслепую. Это не столько критика конкретного фреймворка, поскольку из кода ясно видно, что будет 5 независимых вызовов. В любом случае, проверять свою работу, инспектируя API-вызовы — хорошая идея!
Langchain
Langchain — это мультиинструмент для всего, что связано с LLM. Многие полагаются на Langchain при первом знакомстве с LLM. Основная библиотека LangChain, как правило, не скрывает от вас промпты, однако есть экспериментальные функции, которые это делают. Давайте рассмотрим одну из них — SmartLLMChain:
from langchain.prompts import PromptTemplate from langchain_experimental.smart_llm import SmartLLMChain from langchain_openai import ChatOpenAI hard_question = "I have a 12 liter jug and a 6 liter jug.\ I want to measure 6 liters. How do I do it?" prompt = PromptTemplate.from_template(hard_question) llm = ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo")
chain = SmartLLMChain(llm=llm, prompt=prompt, n_ideas=2, verbose=True) result = chain.run({})
print(result)
Idea 1: 1. Fill the 12 liter jug completely. 2. Pour the contents of the 12 liter jug into the 6 liter jug. This will leave you with 6 liters in the 12 liter jug. 3. Empty the 6 liter jug. 4. Pour the remaining 6 liters from the 12 liter jug into the now empty 6 liter jug. 5. You now have 6 liters in the 6 liter jug. Idea 2: 1. Fill the 12 liter jug completely. 2. Pour the contents of the 12 liter jug into the 6 liter jug. This will leave you with 6 liters in the 12 liter jug. 3. Empty the 6 liter jug. 4. Pour the remaining 6 liters from the 12 liter jug into the now empty 6 liter jug. 5. You now have 6 liters in the 6 liter jug. Improved Answer: 1. Fill the 12 liter jug completely. 2. Pour the contents of the 12 liter jug into the 6 liter jug until the 6 liter jug is full. This will leave you with 6 liters in the 12 liter jug and the 6 liter jug completely filled. 3. Empty the 6 liter jug. 4. Pour the remaining 6 liters from the 12 liter jug into the now empty 6 liter jug. 5. You now have 6 liters in the 6 liter jug. Full Answer: To measure 6 liters using a 12 liter jug and a 6 liter jug, follow these steps: 1. Fill the 12 liter jug completely. 2. Pour the contents of the 12 liter jug into the 6 liter jug until the 6 liter jug is full. This will leave you with 6 liters in the 12 liter jug and the 6 liter jug completely filled. 3. Empty the 6 liter jug. 4. Pour the remaining 6 liters from the 12 liter jug into the now empty 6 liter jug. 5. You now have 6 liters in the 6 liter jug.
Круто! Так что же произошло? Хотя этот API выводит логи с большим количеством информации (доступны в этом gist), паттерн API-запросов интересен:
Два отдельных API-вызова для каждой «идеи». Ещё один API-вызов, включающий обе идеи как контекст, с промптом: You are a researcher tasked with investigating the 2 response options provided. List the flaws and faulty logic of each answer options. Let'w work this out in a step by step way to be sure we have all the errors: Финальный API-вызов, который берёт критику из шага 2 и генерирует ответ.
Неочевидно, что такой подход оптимален. Не уверен, что для этой задачи нужны 4 отдельных API-вызова. Может, критику и финальный ответ можно сгенерировать за один шаг? Кроме того, в промпте есть опечатка (Let'w), а также чрезмерный фокус на негативе — на поиске ошибок, — что заставляет меня сомневаться, что этот промпт был оптимизирован или протестирован.
Instructor
Instructor — это фреймворк для структурированных выходных данных.
Извлечение структурированных данных с помощью Pydantic
Вот базовый пример из README проекта, который позволяет извлекать структурированные данные, используя Pydantic для определения схемы.
import instructor from openai import OpenAI from pydantic import BaseModel client = instructor.patch(OpenAI()) class UserDetail(BaseModel): name: str age: int user = client.chat.completions.create( model="gpt-3.5-turbo", response_model=UserDetail, messages=[{"role": "user", "content": "Extract Jason is 25 years old"}])
Мы можем увидеть, как это работает, изучив API-вызов, записанный в mitmproxy:
{ "function_call": { "name": "UserDetail" }, "functions": [ { "description": "Correctly extracted `UserDetail` with all the required parameters with correct types", "name": "UserDetail", "parameters": { "properties": { "age": { "title": "Age", "type": "integer" }, "name": { "title": "Name", "type": "string" } }, "required": [ "age", "name" ], "type": "object" } } ], "messages": [ { "content": "Extract Jason is 25 years old", "role": "user" } ], "model": "gpt-3.5-turbo" }
Отлично. Для структурированного вывода — он делает именно то, что я хочу, и корректно использует API OpenAI так, как я бы использовал его вручную (определяя схему функции). Я бы назвал этот конкретный API абстракцией с нулевой стоимостью — он делает ровно то, что я ожидаю, с минимальной площадью поверхности.
Валидация
Однако у instructor есть и другие API, которые более агрессивны и пишут промпты за вас. Например, рассмотрим этот пример валидации. Прохождение этого примера должно вызвать те же вопросы, что и разбор SmartLLMChain из Langchain выше. В этом примере вы увидите 3 API-вызова к LLM для получения правильного ответа, причём финальный payload выглядит так:
{ "function_call": { "name": "Validator" }, "functions": [ { "description": "Validate if an attribute is correct and if not,\nreturn a new value with an error message", "name": "Validator", "parameters": { "properties": { "fixed_value": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "If the attribute is not valid, suggest a new value for the attribute", "title": "Fixed Value" }, "is_valid": { "default": true, "description": "Whether the attribute is valid based on the requirements", "title": "Is Valid", "type": "boolean" }, "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "The error message if the attribute is not valid, otherwise None", "title": "Reason" } }, "required": [], "type": "object" } } ], "messages": [ { "content": "You are a world class validation model. Capable to determine if the following value is valid for the statement, if it is not, explain why and suggest a new value.", "role": "system" }, { "content": "Does `According to some perspectives, the meaning of life is to find purpose, happiness, and fulfillment. It may vary depending on individual beliefs, values, and cultural backgrounds.` follow the rules: don't say objectionable things", "role": "user" } ], "model": "gpt-3.5-turbo", "temperature": 0 }
Конкретно мне интересно, можно ли свернуть эти шаги в два LLM-вызова вместо трёх. Кроме того, я задаюсь вопросом: правильный ли подход — использовать обобщённые функции валидации (как в приведённом payload) для критики выходных данных? Я не знаю ответа, но это интересный паттерн проектирования, который стоит исследовать.
Что касается LLM-фреймворков, этот мне действительно нравится. Основная функциональность — определение схем через Pydantic — очень удобна. Код также легко читается и понятен. Тем не менее, мне оказалось полезно перехватить API-вызовы instructor, чтобы получить другой взгляд на происходящее.
В instructor есть возможность задать уровень логирования для просмотра сырых API-вызовов, однако мне нравится использовать подход, не зависящий от фреймворка :)
DSPy
DSPy — это фреймворк, который помогает оптимизировать промпты для максимизации произвольной метрики. У DSPy довольно крутая кривая обучения, отчасти потому что он вводит множество новых технических терминов, специфичных для своего фреймворка, таких как компиляторы и телепромптеры. Однако мы можем быстро заглянуть за эту сложность, посмотрев на API-вызовы, которые он совершает!
Давайте запустим минимальный рабочий пример:
import time import dspy from dspy.datasets.gsm8k import GSM8K, gsm8k_metric start_time = time.time() # Set up the LM turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250) dspy.settings.configure(lm=turbo) # Load math questions from the GSM8K dataset gms8k = GSM8K() trainset, devset = gms8k.train, gms8k.dev
class CoT(dspy.Module): def __init__(self): super().__init__() self.prog = dspy.ChainOfThought("question -> answer") def forward(self, question): return self.prog(question=question)
from dspy.teleprompt import BootstrapFewShotWithRandomSearch # Set up the optimizer: we want to "bootstrap" (i.e., self-generate) 8-shot examples of our CoT program. # The optimizer will repeat this 10 times (plus some initial attempts) before selecting its best attempt on the devset. config = dict(max_bootstrapped_demos=8, max_labeled_demos=8, num_candidate_programs=10, num_threads=4) # Optimize! Use the `gms8k_metric` here. In general, the metric is going to tell the optimizer how well it's doing. teleprompter = BootstrapFewShotWithRandomSearch(metric=gsm8k_metric, **config) optimized_cot = teleprompter.compile(CoT(), trainset=trainset, valset=devset)
Несмотря на то что это официальный минимальный рабочий пример из быстрого старта, этот код выполнялся более 30 минут и сделал сотни вызовов к OpenAI! Это стоило значительного времени (и денег), особенно для точки входа в библиотеку, когда человек просто хочет попробовать. Никакого предупреждения об этом не было.
DSPy сделал сотни API-вызовов, потому что итеративно семплировал примеры для few-shot промпта и выбирал лучшие по метрике gsm8k_metric на валидационной выборке. Я смог быстро это понять, пролистав API-запросы, залогированные в mitmproxy.
DSPy предоставляет метод inspect_history, позволяющий просмотреть последние n промптов и их результатов:
turbo.inspect_history(n=1)
Я убедился, что эти промпты совпадают с последними API-вызовами в mitmproxy. В целом, у меня возникло желание, возможно, оставить промпт и избавиться от библиотеки. Тем не менее, мне любопытно посмотреть, как эта библиотека будет развиваться.
Мой личный опыт
Ненавижу ли я LLM-библиотеки? Нет! Я думаю, что многие библиотеки из этого поста могут быть полезны, если использовать их осмысленно в подходящих ситуациях. Однако я слишком часто наблюдал, как люди попадают в ловушку использования этих библиотек, не понимая, что они делают.
Одна из вещей, на которой я сосредоточен как независимый консультант — убедиться, что мои клиенты не берут на себя случайную сложность. Очень соблазнительно внедрять дополнительные инструменты на волне ажиотажа вокруг LLM. Просмотр промптов — один из способов побороть этот соблазн.
Я настороженно отношусь к фреймворкам, которые слишком сильно отдаляют человека от LLM. Шепча «Fuck you, show me the prompt!» при использовании этих инструментов, вы получаете возможность решать самостоятельно.1
Благодарности: Спасибо Jeremy Howard и Ben Clavie за вдумчивое рецензирование этого поста.
Сноски
Шептать необязательно. Говорить вслух тоже можно — дайте знать остальным!↩︎