Асновы LangChain: што ён рашае і калі можна яго абсалютна прыхіліць.
Дазвольце дакле з’ясаваць, што насправды прыносіць LangChain, чаму викорыстанне інструментаў з калькамі да калькавання з кількох падаўчыкаў спрабоўвае прымусіць команды да яго викорыстоўвання, якія пакеты трэба інсталаваць, і калі кращы выбор – просты обгорак.
Крыніцы: [Day 11/100] LangChain 101: Why It Exists, and When You Should (and Should Not) Use It
Агент з пяцідзесятай лініяй перестае быць пяцідзесятай лініяй
Дадзенне новага інструмента ў маленькі агент, створаны ручна, — это дашча. Вы пісвеце функцыю, зарэгіструеце яе ў слоўніку, апісвеце яе ў схеме, а сам цыкл ніколі не змініцца. Частка работы з агентам выглядае прыемнаю, і гэта прыводзіць людзей да думкі, што весь процес застанэцца такім жа маленькім.
Тады выявляюцься справжнія выклікі. Канцэнтры OpenAI час а час звяртае код 503, таму патрэбны практыкі падзялу з адзінамі паузамі. Інодзе прадаўцы службы не працуюць прыблізна 20 хвілін, таму трэба маты другога прадаўца як альтэратыўную апцыю. Корыстувальнік, які чакае 8 секунд, пакуль завершыцца чатыро спраўы, будзе думаць, што прыкладнэе програма заціснулася, таму дадаецца практыка стрімування токенаў. Служба, якая працуе пасля, адхоўчыць адпаведзь, калі якісь лячак будзе вернуты як строка, а не як число, таму тепер патрэбна перакананасць у структураваным выходным дадзенні. А калі дзесяць чалавек будуць яго вжываць адночасна, блакування спраў стане непрыемным, таму ўсё павінна стаць асінхронным.
Усё гэта ствараецца на адміністрацыі чыстых бібліятэкаў openai і requests, і агент паступова вырастае да прыблізна пяціста ліній коду. Лічыба малая частка гэтага коду стосуецца таго, каму насправды прызначаны агент. Це парсінг JSON, экспансія часу паўтарэння прабаў і пераклад месяжоў між форматамі, якія кожны прадаўцы стварыў самастаятельна. Кожная команда вынужана пісаць гэтыя базовыя элементы, і ніхто не хоча пісаць іх зноў.
Гэтыя базовыя элементы — гэта тое, што пропонуе продукт LangChain. Ёсць інфармацыя пра тое, чым насправды ёсць гэтая бібліятэка, проблемы, якія яна створана для рашэння, а таксама ситуацыі, калі ўжыванне яе ёсць памылкай.
Чым ёсць (і чым не ёсць) бібліятэка
LangChain не запускае ваш агента за вас, не хостуе нічога і не паказвае кращага спосабу разумавання для модэля. Узяце яго як набор каналаваў для абмену данымі плюс гатовыя рэалізацыі для тых частак прыемленае лінгвістычная машина, якія інакш быяў бы трэба напісаць самастаўна.
Якщо вы вже створылі прымітнага агента, то большую частку эых элементаў вы створылі пад разнымі імёнамі. Спіс паведамленняў, якія вы перадаеце, адпавядае ChatMessageHistory. Схема інструмента стае Tool, або простая функцыя, адзначаная @tool. Цикл while, які продовжвае вызываць модель пакуль тая не перастане запрашваць інструменты, — гэта тое, што раней выканана была AgentExecutor. А прымітны вызыв client.chat.completions.create ператвараецца на ChatModel. Ніч гэтага не ўтварае новых канцэпцый. Змянілася толькі тое, што гэтыя элементы тепер маюць спяшаную структуру, якую вже викорыстоўваюць тыясячы іншых проектаў, а стандартныя комбінацыі ўжо падготавлены.
Проблемы, для якіх ён быў створаны
Пачатакніце з вызову інструменту, адзін там ўсё чытка паказваецца. OpenAI прыкладзеў tool_calls да паведамлення асістэнта і хочаць, каб рэзультат быў вярнуты як паведамленне з role: "tool". Anthropic адправляе блакі з контэнтам tool_use і чакае блакі tool_result у наступным кроку корыстніка. Gemini выкарыстоўвае элементы function_call і function_response. Аднойчы ідея, тры API, тры форматы, якія не пасуваюць адзін да другога.
Калі є толькі адміністратар адаптараў, гэта ніколі не стварае проблем. Калі ж патрабуецца другі адміністратар, можа таму, што першы не працуе або стаў дорожэй, трэба або пісаць та падтрымліваць трохі адміністратараў, або викорыстоўваць код, які вже сабралася з тым упорацавацца. Гэта была первоначальная прычына стварэння LangChain: адна інтарфейсная схема ChatModel, дзе методы .invoke() і .bind_tools() працуюць аднакова, незалежна ад таго, якая компанія стварыла токены. Пераход з ChatOpenAI на ChatAnthropic абарачваецца толькі змінайімпорту, а не перапісвам коду для обробкі паведамленняў.
Другая проблема — структура. Калібраванне запиту ў модель, а затым модельі ў парсер, а пасля парсера знову ў іншую модель значыць неабходнае наявнасць коду-з’ўязку на кожнай межы, і гэты код павінен перапісывацца ўсё час, калі ланцюг обробкі змінюецца. LangChain выравнюе гэтыя межы. Механізм для гэтага — LCEL, язык выразаў LangChain, які заслуговыў бы на аднальны апіс.
Трэцяя проблема — проста можлівасць пераўтамовыць. Агент, які робіць больш, чым проста вызывае модель, патрэбуе таго, што вучылі іншыя: чытальнікі для або чатырох дзесяткаў форматаў дакументаў, кліенты для дзесяткаў баз дадзеных у вектарнай форме, стратэгіі павторных спроб, стрімінг, групаванне, элементы для трэйсінгу. Наладжаная інтеграцыя практычна завжды краща, чым парсер PDF, які вы напісалі у пятніцу і тепер павінны дэбагаваць.
Што вы отрымаеце, не пісаючы гэта самі
Адын інтэрфейс модэляў. ChatOpenAI, ChatAnthropic, ChatGoogleGenerativeAI і ChatOllama для локальных модэляў усе выкананыя .invoke(), .stream() і .bind_tools(). Ваш код больш не павінен думаць пра тое, хто трэнаваў модэль, які выкананы за адзін вызыв.
Композабельныя элементы. За дапамогою аператара пайпа LCEL вы пішаце prompt | model | parser і атрымуеце адзін об’ект, які працуе як кожны з яго складовых. Ён можа выкананыя стрімам, пакетамі і асінхронна без дадатковых зусіль.
Стойкасць як вызыв методу. Кожны Runnable мае .with_retry() і .with_fallbacks(), таму вам не трэба самостайна пісаць цыкл перапрыявлення або пераключальнік падаўчыка.
Стрымінг і асінхронна обработка паўсюду. Кожны об’ект класу Runnable мае методы .stream(), .ainvoke(), .astream() і .batch(), нават калі сам аб’ект, які вы абгортавалі, ніколі не падтрымаваў іх ужо з самага початку.
Вялікі каталог інтеграцый. З’явяцеліка дакументаў, хранілішча вектараў, інструменты пошуку, выкананне коду, інструменты баз дадзенаў. Большасць з іх падтрымваюць самі виробнікі, і ўсе яны викорыстоўваюць аднойчы і той самы інтерфейс Runnable.
Просты шлях да LangGraph. Калі ваш агент патрабуе цыклів, збераганага стану або крока затверджэння ад чалавека, якога не можна адобразіць у лінейнай ланцюговай структуре, цыкл керування пераходзіць у LangGraph. Ён пабудаваны на тых сабе абстракціях, таму тое, што вы выучылі ў LangChain, можа быць вялікім падспорам у LangGraph.
Калі яго можна прыхіліць
Большасць введацкіх матэрыялаў прыхіляюцься да гэтай часткі, і самэ ўжо ў гэтай частцы крыюцца найбольшыя проблемы пазней.
Адзін прадаўцы, адзіны вызов, назаўсёды. Якщо весь продукт заключаецца у «падаць гэты запит да GPT-4o і павернуць текст», то просты власны кэйс лёгча чытаць і дыягноставаць, чым выкарыстоўванне залежнасцяў, якія включаюць сотні проміжных пакетаў.
Вам трэба бачыць кожны запит. Абстракцыі ўжыткавыя саме таму, што яны маскуюць деталі, а гэта маскаванне заводзіць да проблем, калі ўсё ламаецца. Аналіз следа выканання через шасць слоёў кэйсаў Runnable прычынай таго, што трэба дасягнуць фактычнага бага, — это втрата часу. Команды, якія хочуць чытаць кожную лінію коду і HTTP-запыту, часта застаюцца ближэй да самага SDK.
Шляхі, дзе крытычныя затрымкі. Кожны слой дадае некалькі додатковых витрачэнняў. Палягле ў порываў з модэлем, які выкананы за 500 мс або больш, гэтыя витрачэння зазвычай є незначнымі. Але на шляхах з высокай навантажаннем, дзе кожны мілісекунда мае значэнне, гэтыя витрачэння іноды стаюць крытычнымі.
Апгрэйды без фіксаціі версіі. LangChain рэгулярна выклікае змяны, якія можуць спануліць працэсы. Проект, які выкарыстоўвае найновейшую версію, зрэшты стане свядомы змяненага імпорту чыста змененага стандартнага рэшэння. Забезпечыце фіксацію версіі, як і для іншых залежнасцей, і апгрэйдавайце тады, калі самі гэта адначыніце, а не тады, калі гэта зрабіць pip.
Усё гэта не значыць, што вы ніколі не должны выкарыстоўваць LangChain. Це значыць, што рашэнне павінна быць прымененае для кожнага проекта ціхамо, а не з прычыны прыzwычкі.
Калькі пакеты і якія
LangChain складаецца з калькі пакетаў, і важна знать, калькі з іх робіць, перш чым чыгнуць які-небудзь з іх.
langchain-coreмае базовыя абстракцыі:Runnable,BaseChatModel,BaseTool, шаблоны запытанняў і парсеры выходных даных. У яго мало залежнасцей, і практычна всё інша будуе на яму.
langchain дадзе можлівасць ствараць ланцоў, агентаў і логікі запошуку на базе основнага калекшана, не прыкрепляючы вас да конкрэтнага прадаўца.langchain-community сабралі вялікі набор трэціх сторон, якія інтегруюцься з калекшанам. Дзеяныя з іх выдатныя, а дзеяныя занедбаныя, адтолькі ўсі можаюць дапамагаць. Перад тым, як выкарыстоўваць такую інтеграцыю ў працоўным сераўеры, пераканайцеся, калі ёй была востаннія адчынена апдэйт.langchain-openai, langchain-anthropic і langchain-google-genai, падтрымваюцца разам з сае SDK кожнага прадаўца. Інсталюйце толькі тыя, якія вы насправды выкарыстоўвайце.langchain-classic — гэта месца, куды былі перанесеныя канстрактары агентаў з версіі 1.0: AgentExecutor, create_tool_calling_agent і стары create_react_agent. Існуючы код продовжае працаваць, але новыя агенты не трэба ствараць тут.langgraph — это аддытывны пакет, які працюе на базе langchain-core і адмініструець цыкл кантролю для всіх сцэнарыяў, якія не ўзроўненыя. З версіі langchain v1 рэкамендаваны інструмент для стварэння агентаў, langchain.agents.create_agent, реалізаваны яшчэ ў самым langgraph.Ёсць толькі тры пакеты, якія патрэбны для пачатку работы:
uv add langchain langchain-openai python-dotenv
Замена простаг кліента на ChatOpenAI
Не чакайце нічага значнага на гэтым кроку. Мета — заменіць просты кліент OpenAI на кэшэў для модэлю чату з LangChain і пераканацца, што вы отрымаеце тую ж самую адпаведь.
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv()
llm = ChatOpenAI(model="gpt-4o", temperature=0)
response = llm.invoke("What is the weather in Paris?")
print(response.content)
llm.invoke(...) вяртае вам AIMessage, а не чыстыя словнікавыя даны, але .content дае вам тэкст так сама, як і .choices[0].message.content. Пад спадом ChatOpenAI як і раней выкарыстоўвае той самы канец-пункт OpenAI, які вы моглі бы выклікаты ручна, і сама модель абсалютна не зменілася. Разлік у тым, што llm тепер являе сабою Runnable, таму .stream(), .batch() і .ainvoke() вялікі час вучыцца, і вам не патрэбна была пісаць жадны з іх.
for chunk in llm.stream("Tell me one fact about Paris."):
print(chunk.content, end="", flush=True)
Запустіце яго, і токены будуць прыходзіць па частках. Вы ніколі не пісалі обробніка стрімінгу, і гэты маленькі цикл практычна ўсё, што паводзіцца як аргумент для бібліятэкі.
Кілька слоў пра назвы модэляў: у прыкладах як заменнік викорыстоўваецца "gpt-4o". Спісы прадаўцоў зміняюцца быстрей, чым які-небудзь туторіял можа за гэтым стаць, таму кожную строеку model="..." трэба спрыямаць як місцэ для заполнення і падставіць тое, што пропонуе ваш прадаўцы, калі запускаеце код. Механізм застаецца тым жа.
Спробуйце гэта на сваём агенте
- Дадзіце
langchainіlangchain-openaiу ваш проект за дапамоюuv add. - У кроку з модэлем вашага агента заменіце первісны вызов
OpenAI()наChatOpenAIі не чыніце змян у рэште цыклу. Пераканаецеся, што агент будзе працаваць абсалютна так жа, як і раней.
llm.stream(...) для простага запытання без якіх-лібо інструментаў і выдрукуйце кожны фрагмент, як толькі ён прыходзіць. Такім чынам у вас з’являецца можлівасць стрімінгу для методу, які вы ніколі не реалізавалі.Пасля гэтых змян ваш агент не стае розумнейшым. Але вы отрымалі базу, дзе прабавы занова, альтернатыўныя рашэнні, стрімінг і змена прадаўця коштаюць проста выкліку методу, а не ста гэтых ліній коду.