DSPy: Замяна на нестабилното проектиране на инструкции с компилирани LLM конвейери
Постоянно се сблъсквам с една и съща пречка, когато мисля за финансови AI конвейери: можете да изградите нещо, което работи чудесно върху вашите тестови случаи, и след това да наблюдавате как тихомълком се разпада, когато доставчик промени формата на фактурата си или се появи нов тип трансакция. Нестабилността почти винаги е в инструкциите (prompts) — ръчно изработени низове, които никой не иска да пипа. DSPy, представен от Khattab и др. от Станфорд и публикуван на ICLR 2024, предлага фундаментално различен начин за изграждане на LLM конвейери, който заслужава внимателно разглеждане от всеки, който се опитва да автоматизира счетоводна работа.
Документът
DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines (Khattab, Singhvi, Maheshwari et al., ICLR 2024) преформулира изграждането на LLM конвейери като програмен проблем, а не като проблем на проектирането на инструкции. Основното наблюдение е, че съвременните LLM приложения обикновено се изграждат като колекции от твърдо кодирани низове с инструкции — това, което авторите наричат „шаблони за инструкции“ — слепени заедно с контролен поток на Python. Когато моделът се промени или разпределението на задачите се измести, някой трябва да пренапише тези низове на ръка.
DSPy заменя шаблоните за инструкции с две абстракции: сигнатури и модули. Сигнатурата е типизирана, декларативна спецификация на това, което едно повикване на езиков модел (LM) трябва да направи, написана компактно като question -> answer или с изрични описания на полета в клас на Python. Модулът обвива сигнатурата със стратегия за разсъждение — ChainOfThought, ReAct, ProgramOfThought, MultiChainComparison и т.н. Критичното допълнение е компилатор (документът го нарича телепромптер), който приема DSPy програма, малък етикетиран набор от данни и метрика за валидиране, след което автоматично генерира few-shot демонстрации, избира измежду тях и създава инструкции, които са оптимизирани за тази метрика. Компилаторът не се нуждае от етикети на всяка междинна стъпка — той може да създаде демонстрации чрез стартиране на „учителска“ програма върху неетикетирани входни данни и филтриране на следите, които водят до правилни крайни резултати.
Ключови идеи
- Сигнатурите отделят намерението от изпълнението. Писането на
question, context -> answerе достатъчно за DSPy, за да знае как да конструира, извика и оптимизира базовото LM повикване. Разработчикът никога не пише низ с инструкции. - Компилацията е бутстрапинг, управляван от метрики. Оптимизаторът
BootstrapFewShotстартира програмата върху тренировъчни входни данни, събира следи от входове и изходи, където конвейерът успява, и ги използва като демонстрации — не се изисква човешка анотация на междинните стъпки на разсъждение. - Компилаторът отключва потенциала на малките модели. В GSM8K (математически текстови задачи), стандартният Llama2-13b постига 9,4% с zero-shot инструкции. След DSPy компилация с модули за рефлексия и ансамбли, той достига 46,9%. T5-Large (770 милиона параметъра), модел, който повечето хора отписаха за сложни разсъждения, постига 39,3% точно съвпадение на отговорите в HotPotQA, използвайки само 200 етикетирани примера.
- Експертните инструкции не са таванът. В GSM8K, GPT-3.5 със стандартни few-shot инструкции достига 25,2%. Експертно изработената верига от мисли (chain-of-thought) вдига това до около 72–73%. Компилираният конвейер на DSPy с рефлексия и ансамбли го изтласква до 81,6% — без човек да е писал инструкции.
- Програмите са композируеми. Конвейер за многостъпк ово извличане на въпроси и отговори (multi-hop retrieval QA) в DSPy е около 12 реда Python. Еквивалентът в LangChain, отбелязват авторите, съдържа 50 низа, надвишаващи 1000 знака ръчно изработено съдържание за инструкции.
- Три етапа на компилация. Оптимизаторът работи като: генериране на кандидати (бутстрапинг на следи), оптимизация на параметрите (произволно търсене или Optuna върху хиперпараметри) и оптимизация от по-висок ред (ансамбли, динамичен контролен поток).
Какво работи — и какво не
Емпиричните резултати са реални и съществени. Преминаването от 9,4% на 46,9% в GSM8K с Llama2-13b, при използване само на шепа етикетирани тренировъчни примери, не е инкрементално — това е видът разлика, която прави малките, евтини модели жизнеспособни за задачи, които преди изискваха GPT-4. Архитектурата също е наистина елегантна: сигнатурите са лесни за четене, модулите са композируеми и абстракцията не изглежда пропускли ва за демонстрираните задачи.
Ограниченията обаче са реални, въпреки че документът не ги обсъжда в специален раздел. Най-важното: компилаторът е толкова добър, колкото и вашата метрика. Ако вашата метрика за валидиране е неточна или не съответства на действителното качество на задачата — което е изключително често в практиката — оптимизаторът ще намери хитри начини да я максимизира, докато се проваля в това, което действително ви интересува. В структурирана област като счетоводството, бихте могли да дефинирате метрика като „счетоводният запис е балансиран“, но балансираният запис все още може да има напълно грешни кодове на сметки. Авторите знаят, че този проблем съществува, но го оставят на отговорността на разработчика.
Второ ограничение: компилацията все още изисква известни количества етикетирани данни. Документът твърди, че можете да използвате само 10 примера с BootstrapFewShot и че са необходими само входните стойности (а не междинни етикети). Това е вярно в най-добрия случай, но на практика надеждността на бутстрапинга се влошава, когато първоначалната програма не може да реши нито един тренировъчен пример. Ако вашият конвейер за финансов агент има почти нулева базова точност — както често се случва, когато изграждате нещо ново — компилацията може да буксува на едно място.
Трето и по-фино: DSPy оптимизира инструкциите и демонстрациите, но не оптимизира самата структура на програмата. Ако сте свързали модулите по начин, който е фундаментално погрешен за задачата, компилаторът няма да ви помогне. Дизайнът на програмата все още е задача на разработчика.
Защо това е важно за финансовия AI
Проблемът с нестабилността на инструкциите е може би най-голямата практическа пречка пред внедряването на финансови AI агенти в производството. Конвейер, който категоризира трансакции чрез напасване на описания към кодове на сметки, ще се влошава винаги, когато данните от търговците променят формата си, когато се появи нова категория разходи или когато сметкопланът бъде актуализиран. С DSPy дефинирате задачата абстрактно (transaction_description, chart_of_accounts -> account_code, confidence) и оставяте компилатора да открие оптималните демонстрации всеки път, когато разпределението се промени.
Специфично за Beancount, виждам конвейер, структуриран като три верижни DSPy модула: един, който извлича структурирани данни за трансакции от необработени банкови експорти, един, който търси най-подходящата сметка в съществуващия сметкоплан на главната книга, и един, който валидира получения счетоводен запис спрямо ограниченията на двойното записване. Всеки модул получава своя собствена сигнатура; цялата програма се компилира спрямо метрика, която проверява както счетоводната коректност, така и съответствието на формата. Проблемът с качеството на метриката удря най-силно тук — имате нужда от метрика, която улавя грешни кодове на сметки, а не само небалансирани записи — но това е решим инженерен проблем.
По-дълбокото значение: DSPy измества работата от „писане на по-добри инструкции“ към „писане на по-добри метрики и събиране на малки етикетирани набори от данни“. Това е много по-устойчива инженерна практика за производствена финансова система, която трябва да се развива успоредно с промените в регулациите, структурите на сметкоплановете и форматите на трансакциите.
Какво да прочетете след това
- OPRO: Large Language Models as Optimizers (Yang et al., arXiv:2309.03409) — подходът на Google DeepMind към оптимизация на инструкции чрез итеративно усъвършенстване, генерирано от LM; полезен контрапункт на подхода на DSPy за бутстрапинг.
- TextGrad: Automatic "Differentiation" via Text (Yuksekgonul et al., arXiv:2406.07496) — рамкира оптимизацията като обратно разпространение (backpropagation) чрез текстова обратна връзка, а не като бутстрапинг, управляван от метрики; показва силни резултати при задачи за кодиране и научни задачи, където подходът на DSPy е по-слаб.
- DSPy Assertions: Computational Constraints for Self-Refining Language Model Pipelines (Singhvi et al., arXiv:2312.13382) — добавя твърди и меки ограничения към програмите на DSPy, позволявайки на конвейерите да се самокоригират, когато изходите нарушават правилата на домейна; пряко приложимо към налагането на счетоводни инварианти.
