Самый частый комментарий про исходники решения - победителя Enterprise RAG Challenge звучит примерно так:
Я и сам удивился. Не ожидал, что checklists в сочетании со structured outputs - это настолько мощная связка, что может вытягивать сразу десятки-сотни сущностей из документа за раз.
Удивление было уже давно, целый месяц назад (это вечность по современным меркам 😂). А сейчас уже в ряде проектов у нас сложные пайплайны переделаны под эту связку. Сложными они были раньше, теперь скучные, и это хорошо.
Основная сложность теперь не в написании кода, а в написании такого дерева Structured Output объектов, чтобы оно работало как Checklist, заполняя который последовательно, мы приходим к правильным выводам.
Получается прямо захардкоженный Chain-of-thought, который выполняется одним запросом! Причем в нем можно сразу закладывать ветвления и выделять места для дополнительных рассуждений (см ниже).
Поэтому я сейчас всех консультируемых клиентов с подходящими кейсами тороплю - пробуйте эту связку. Времени она тратит немного, а вот разработку может сократить заметно. Ну и код становится скучным и эффективным. Все, как мы любим.
Пара подсказок сразу:
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=messages,
response_format=ChainOfThoughtPydanticClass
)
(1) Не используем function calling syntax для передачи structured output, а передаем response_format
(2) gpt-4o-2024-08-06 работает хорошо. Вроде даже на Azure она уже есть со structured output.
(3) порядок полей очень важен! Они заполняются последовательно. Сначала можно ставить наводящие вопросы или даже давать место для размышлений (например, List[ThinkStep]).
(4) Там, где нужно делать классификацию, используем Literal. Опциональные поля помечаем как Optional, используем смело Dict/List. Ну и вообще знакомимся с фишками из Typing, которые поддерживает OpenAI API.
(5) Optional + вложенный класс - это возможность для GPT в рамках для Chain-of-thought пройтись по опциональной ветке размышлений. Еще есть anyOf, но так далеко с программированием на constrained decoding автомате я пока не заходил 😁
(6) Там, где важен формат ответа, ставим описание и примеры в Field. Это GPT тоже увидит. Можно повторить в названии и в описании. Например:
class Size(BaseModel):
width: float
height: float
size_inches: Optional[Size] = Field(None, description="Dimensions in inches (width, length)")
Понятно, что подход не универсальный. Но в продуктовых задачах для бизнеса такие самодельные ChainOfThought могут решать очень много задач одним запросом.
Тогда получится мой любимый вид продукта с LLM под капотом - когда снаружи он работает на одном уровне с передовыми решениями своего сектора, а то и лучше. Но у него есть маленький секрет - вместо монструозно сложной конструкции из кучи разных технологий (RAG/LangChain/Hybrid search/Agents итп) там просто пара простых и эффективных использований LLM. В итоге AI кода в решении - 5%, а все остальные 95% - это обычные интеграции, интерфейсы.
И такие типы продуктов очень важны для маленьких компаний и стартапов. При необходимости роста не нужно искать и нанимать редких ML специалистов. Код же простой и он на 95% обычен. Тут нужно искать обычных Front-End, Back-End и Full-stack итп. И нанять их можно будет сильно проще, т.к. зовут их не в обычный продукт, а в модный LLM-driven. Выигрывают от этого все.
Только вчера фаундер одного MedTech стартапа сказал, что после такого описания (с приоритизацией кейсов под этот паттерн) у него стратегия развития поменялась кардинально. И теперь pre-seed раунд на пару миллионов евро не выглядит недостижимым.
В общем, паттерн простой и мощный. Особенно, если приоритизировать кейсы, где он сработает хорошо (такие есть в каждой отрасли), а конкурентам оставлять более сложные.
Ваш, @llm_under_hood 🤗
PS: Я писал раньше про кейс с новой AI Platform для клиента. И он тоже теперь сильно использует этот паттерн на "одну кружку чая".
Заварил чайник, чтобы сесть разобраться в исходниках. К концу первой кружки удивился, что так мало кода.
Я и сам удивился. Не ожидал, что checklists в сочетании со structured outputs - это настолько мощная связка, что может вытягивать сразу десятки-сотни сущностей из документа за раз.
Удивление было уже давно, целый месяц назад (это вечность по современным меркам 😂). А сейчас уже в ряде проектов у нас сложные пайплайны переделаны под эту связку. Сложными они были раньше, теперь скучные, и это хорошо.
Основная сложность теперь не в написании кода, а в написании такого дерева Structured Output объектов, чтобы оно работало как Checklist, заполняя который последовательно, мы приходим к правильным выводам.
Получается прямо захардкоженный Chain-of-thought, который выполняется одним запросом! Причем в нем можно сразу закладывать ветвления и выделять места для дополнительных рассуждений (см ниже).
Поэтому я сейчас всех консультируемых клиентов с подходящими кейсами тороплю - пробуйте эту связку. Времени она тратит немного, а вот разработку может сократить заметно. Ну и код становится скучным и эффективным. Все, как мы любим.
Пара подсказок сразу:
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=messages,
response_format=ChainOfThoughtPydanticClass
)
(1) Не используем function calling syntax для передачи structured output, а передаем response_format
(2) gpt-4o-2024-08-06 работает хорошо. Вроде даже на Azure она уже есть со structured output.
(3) порядок полей очень важен! Они заполняются последовательно. Сначала можно ставить наводящие вопросы или даже давать место для размышлений (например, List[ThinkStep]).
(4) Там, где нужно делать классификацию, используем Literal. Опциональные поля помечаем как Optional, используем смело Dict/List. Ну и вообще знакомимся с фишками из Typing, которые поддерживает OpenAI API.
(5) Optional + вложенный класс - это возможность для GPT в рамках для Chain-of-thought пройтись по опциональной ветке размышлений. Еще есть anyOf, но так далеко с программированием на constrained decoding автомате я пока не заходил 😁
(6) Там, где важен формат ответа, ставим описание и примеры в Field. Это GPT тоже увидит. Можно повторить в названии и в описании. Например:
class Size(BaseModel):
width: float
height: float
size_inches: Optional[Size] = Field(None, description="Dimensions in inches (width, length)")
Понятно, что подход не универсальный. Но в продуктовых задачах для бизнеса такие самодельные ChainOfThought могут решать очень много задач одним запросом.
Тогда получится мой любимый вид продукта с LLM под капотом - когда снаружи он работает на одном уровне с передовыми решениями своего сектора, а то и лучше. Но у него есть маленький секрет - вместо монструозно сложной конструкции из кучи разных технологий (RAG/LangChain/Hybrid search/Agents итп) там просто пара простых и эффективных использований LLM. В итоге AI кода в решении - 5%, а все остальные 95% - это обычные интеграции, интерфейсы.
И такие типы продуктов очень важны для маленьких компаний и стартапов. При необходимости роста не нужно искать и нанимать редких ML специалистов. Код же простой и он на 95% обычен. Тут нужно искать обычных Front-End, Back-End и Full-stack итп. И нанять их можно будет сильно проще, т.к. зовут их не в обычный продукт, а в модный LLM-driven. Выигрывают от этого все.
Только вчера фаундер одного MedTech стартапа сказал, что после такого описания (с приоритизацией кейсов под этот паттерн) у него стратегия развития поменялась кардинально. И теперь pre-seed раунд на пару миллионов евро не выглядит недостижимым.
В общем, паттерн простой и мощный. Особенно, если приоритизировать кейсы, где он сработает хорошо (такие есть в каждой отрасли), а конкурентам оставлять более сложные.
Ваш, @llm_under_hood 🤗
PS: Я писал раньше про кейс с новой AI Platform для клиента. И он тоже теперь сильно использует этот паттерн на "одну кружку чая".