Հիմնական նավիգացիա

Հարմարեցված RAG լուծումների գործնական օրինակներ

Իրական օրինակները ցույց են տալիս, թե ինչպես են հարմարեցված retrieval-augmented generation համակարգերը լուծում ձեռնարկությունների գիտելիքի բարդ խնդիրները։

Վերջերս RAG-ը երբեմն վատ համբավ է ձեռք բերում. ոմանք կարծում են, թե այն չափազանց պարզ է (սկսելը՝ այո, մասշտաբավորելը՝ ոչ այնքան), իսկ մյուսների կարծիքով՝ դրան փոխարինել են «գործակալային համակարգերը» (որոնցից շատերը, մի փոքր խորանալու դեպքում, շատ արագ սկսում են խիստ նմանվել RAG-ին…)։

Այս բլոգում մեկ-երկու գործնական օրինակով ցույց ենք տալիս, թե ինչպես ենք լուծում մի շարք տարածված խնդիրներ, մասնավորապես՝

  • Խառը՝ տեքստային և թվային տվյալների մշակումը, և թե ինչու պարզունակ RAG-ը չի հաղթահարում այն. հիմնաբառերը համընկնում են, իսկ թվերն իմաստային նշանակություն չունեն։

  • Ինչու է օգտակար նախ ամփոփման ներդիր վեկտորներ նախագծելը. յուրաքանչյուր հատվածի համար ստեղծեք կարճ նկարագրական ամփոփում, ապա ներդիր վեկտորը և հարցումը կառուցեք այդ ամփոփման հիման վրա։

  • Ինչպես ստեղծել համատեքստային ամփոփումներ. ներառեք մայր փաստաթղթի համատեքստը, որպեսզի նման կառուցվածքով վիճակագրական տվյալները հնարավոր լինի տարբերակել։

  • Երբ ապավինել կոդին և Pydantic մոդելներին. եթե բովանդակությունը պետք է պահպանվի բառացի, հուսալիության համար հատուկ կոդը և/կամ Pydantic մոդելը համատեղեք LLM կանչերի հետ։

Հատուկ RAG լուծումների ստեղծում

Հիմունքները

RAG համակարգերը գործարկում են ամենատարբեր լուծումներ՝ աջակցության բոտերից մինչև գիտելիքի ներքին օգնականներ։

Սովորաբար համակարգի ներսում կատարվում են հետևյալ քայլերը՝

  1. Սկզբնաղբյուր փաստաթղթերը բաժանել հատվածների

  2. Յուրաքանչյուր հատվածը ներկայացնել ներդիր վեկտորով վեկտորային տարածությունում

  3. Հարցման պահին առբերել լավագույն K հատվածները

  4. Այդ հատվածների հիման վրա ստեղծել պատասխան

Հանրահայտ գործիքակազմերը՝ LangChain-ը, LlamaIndex-ը և OpenAI-ի Filestore-ը, այս քայլերը գրեթե տարրական են դարձնում։ Սակայն իրական աշխատանքային շղթաներում հանդիպում են տվյալներ, որոնք պարզապես խիտ տեքստ չեն, և հիմնական RAG-ը կարող է դժվարանալ։ Հաջորդ բաժիններում կներկայացնենք տվյալների հետ կապված խնդիրների կոնկրետ օրինակներ և, բարդության ավելացմանը զուգընթաց, աստիճանաբար կկառուցենք լուծումը։

Երբ ամեն ինչ բարդանում է

  1. Երբ ձեր տվյալները միայն տեքստ չեն (ինչն իրականում հազվադեպ չէ)

Դիտարկենք խաղային համատեքստից վերցված տվյալների հետևյալ հատվածը։

JSON

{ "Attack": { "Range": { "default": 5, "with_Draconic_Ascension": 5.5, }, "Speed": { "default": "2 seconds", "with_Draconic_Ascension": "2.2 seconds", }, "Damage": { "default": 10, "with_Draconic_Ascension": 12, } }} 

Ներդիր վեկտորներն աշխատում են բառերի միջև իմաստի և քերականության միջոցով սովորած կապերի շնորհիվ։ Վերոնշյալ տվյալներում տեքստն ու թվերը խառնված են, իսկ այս կոնկրետ համատեքստից դուրս թվերը բառերի հետ որևէ կապ չունեն։ Այսինքն՝ տվյալների այս հատվածը, ըստ էության, որոշ չափով նկարագրական բառերի և դրանց հաջորդող պատահական թվերի համադրություն է։

Սա խնդիր չէր լինի, եթե մեր բոլոր տվյալներն այս տեսակի լինեին, քանի որ դեռ կկարողանայինք դրանք առբերել՝ օգտվելով առկա մի քանի նկարագրական բառերի ներդիր վեկտորներից (կամ պարզապես կիրառել text-to-SQL)։ Իսկ ի՞նչ կլինի, եթե այս հատվածը թաղված լինի բազմաթիվ խիտ տեքստային հատվածների մեջ, որոնցում նույնպես հանդիպում են նույն բառերը։ Օրինակ՝

JSON

{"Draconic_Ascension": { "description": "Transform into dragon form. Attack range and speed are increased by 10%, damage is boosted by 20%.", "details": { "activation_conditions": "Can only be activated when HP is below 50%or Fury meter is full.", "visual_effects": "Wings unfurl, scales shimmer with embers, voice lines change to echoing growls.", "lore": "An ancient bloodline awakens. The bearer of the mark channels the soul of the last Flamewing Wyrm, becoming a living storm of fire and fury." } }}

Այժմ պատկերացրեք, որ ուզում ենք գտնել «What is the attack range with Draconic Ascension?» հարցի պատասխանը։ Ամենայն հավանականությամբ չենք կարողանա առբերել անհրաժեշտ հատվածը, քանի որ այն կկորչի նույն հիմնաբառերը պարունակող այլ հատվածների աղմուկի մեջ։

Հիմնական խնդիրն այն է, որ չենք կարողանում հստակ տարբերակել տվյալների այս հատվածները, թեև նույն թեմայի վերաբերյալ դրանք տարբեր տեսակի տեղեկություններ են պարունակում։ Իսկ հնարավո՞ր է դրանք որևէ կերպ հարստացնել կամ բարելավել։ Իհարկե հնարավոր է :smile:

  1. Հարստացրեք ձեր տվյալները՝ ամփոփելով դրանք. այո՛, ճիշտ կարդացիք

Հատվածն անմիջապես ներդիր վեկտորի վերածելու փոխարեն կարող ենք նախ ստեղծել տվյալները նկարագրող ամփոփում, ապա դրա հիման վրա կառուցել ներդիր վեկտորը և կատարել առբերումը։ Պատասխանի ստեղծման փուլում կշարունակենք օգտագործել ամփոփմանը կապակցված սկզբնական տվյալները։

Այսպիսով, վերևում ներկայացված երկու հատվածների համար կարող ենք ստեղծել այսպիսի ամփոփումներ՝

  1. Հարձակման հեռավորության, արագության և վնասի վիճակագրություն (կանխադրված և Draconic Ascension-ի կիրառմամբ)։

  2. Draconic Ascension-ի նկարագրությունն ու մանրամասները՝ ներառյալ ակտիվացման պայմանները, տեսողական էֆեկտները և պատմությունը։

Այնուհետև հարցումը նույնպես լրացնում ենք, որպեսզի այն «համապատասխանի» ամփոփմանը։ Օրինակ՝ «What is the attack range with Draconic Ascension?» հարցը կվերածենք «What is the statistics of attack range with Draconic Ascension?» հարցի։ Սա հատկապես կարևոր է, երբ առբերման հարցումը տալիս են տեխնիկական ոլորտներից դուրս օգտատերերը՝ ~~«ազատ ոճով»~~ սովորական մարդկային լեզվով։ Ի վերջո, նրանք պարտավոր չեն իմանալ կամ մտահոգվել, թե ինչպես է աշխատում RAG-ը՝ ճշգրտությունն ու ամբողջականությունը առավելագույնի հասցնելու համար։

Գծապատկեր, որը ցույց է տալիս, թե երբ է ամեն ինչ բարդանում։

  1. Մի՛ կտրեք որևէ բան համատեքստից (սա առհասարակ կյանքում էլ է կիրառելի)

Այժմ դիտարկենք հաջորդ իրավիճակը, երբ գործ ունենք միանման տեսք ունեցող հսկայական քանակությամբ տվյալների հատվածների հետ, ինչպես ստորև։

Plain Text

# Chunk one{ "Attack": { "Range": { "default": 9, }, ... }}# Chunk two{ "Attack": { "Range": { "default": 6, }, ... }}# Chunk three{ "Attack": { "Range": { "default": 7, }, ... }}

Եթե շարունակենք նույն մոտեցմամբ, պատկերացրեք, որ հարցնում ենք՝ «what is character X’s attack range?»։ Մեր նոր ստեղծած ամփոփումների պարագայում ստիպված կլինեինք բախտի վրա հիմնված գուշակության խաղ խաղալ, քանի որ դրանք նույնպես շատ նման կլինեին։ Այդ դեպքում ինչպե՞ս կարող ենք տարբերակել դրանք։

Պարզ պատասխանը՝ տրամադրել համատեքստ։ Կարող ենք պարզապես տվյալների հատվածում հղում ավելացնել դրա մայր փաստաթղթին. օրինակ՝ այս դեպքում {”character”: “X”}։ Այդպես կկարողանանք ճշգրիտ առբերել X կերպարի ճիշտ տվյալները, նույնիսկ եթե ունենք նույն տվյալները նաև Y և Z կերպարների համար։

Սակայն ավելի լավ և ընդհանրացվող մոտեցում կլինի հատվածի համատեքստային ամփոփում ստեղծելը։ Այսինքն՝ միայն տվյալների հատվածն ամփոփելու փոխարեն կարող ենք մուտքում տրամադրել թե՛ դրա մայր փաստաթուղթը, թե՛ հենց հատվածը և ստեղծել ընդհանուր համատեքստային ամփոփում։ Դրանում կնշենք, թե ինչպես է այս հատվածը տեղավորվում մայր փաստաթղթում, օրինակ՝

  1. Այս հատվածը մանրամասն վիճակագրություն է ներկայացնում X կերպարի … մասին։ Հատվածն ամբողջական փաստաթղթի մաս է կազմում՝ ցույց տալով X-ի ուժեղ կողմը հարձակման արագության հարցում…

  2. Այս հատվածը մանրամասն վիճակագրություն է ներկայացնում Y կերպարի … մասին։ Հատվածն ամբողջական փաստաթղթի մաս է կազմում՝ ցույց տալով Y-ի հատուկ ունակությամբ ուժեղացված ցուցանիշները…

  3. Այս հատվածը մանրամասն վիճակագրություն է ներկայացնում Z կերպարի … մասին։ Հատվածն ամբողջական փաստաթղթի մաս է կազմում՝ ցույց տալով Z-ի այն ցուցանիշները, որոնց շնորհիվ նա հարմար է թիմային մարտերում տանկի դերի համար…

Այս մեթոդը (որը մասամբ ներշնչված է Anthropic-ի մոտեցմամբ) վերոնշյալ օրինակի համար կարող է չափազանցված թվալ, սակայն այն շատ արդյունավետ է այն հատվածների դեպքում, որոնք «համատեքստից դուրս» կարող են սխալ մեկնաբանվել։ Բացի այդ, այն ապահովում է բոլոր հատվածների համար կիրառելի միասնական մոտեցում և պահպանում է ինժեներական կոկիկ աշխատանքային շղթա։

Գծապատկեր, որը ցույց է տալիս, թե երբ է ամեն ինչ բարդանում։

  1. Երբ պետք է ~~ամեն ինչ վերահսկել~~ խիստ հետևողական լինել

Սովորաբար տվյալները ստանում ենք ամբողջական միավորներով և RAG համակարգի համար բաժանում հատվածների։ Այս օրինակում ցույց ենք տալիս մի փոքր այլ դեպք. տվյալները բաժանված են հատվածների, սակայն վատ ձևով։ Դրանք տրամաբանական հատվածի պատահական մասեր են, որոնք իրականում պետք է կրկին միավորել։ Տրամաբանական հատվածը բովանդակության մի մաս է, որը բնականորեն պետք է միասնական լինի, օրինակ՝ փաստաթղթի ենթաբաժին կամ իմաստային ամբողջություն կազմող պարբերություն։

Գծապատկեր, որը ցույց է տալիս, թե երբ է ամեն ինչ բարդանում։

Այս տվյալների հետ մեր առաջին փորձը ամեն ինչ LLM կանչին փոխանցելն էր՝ խնդրելով ըստ նպատակահարմարության խմբավորել և վերադարձնել խմբավորված բովանդակությունը։ LLM-ը պետք է բավական լավ կատարի դա, չէ՞։ Ե՛վ այո, և՛ ոչ։

Այս և մի շարք այլ դեպքերում պարզել ենք, որ LLM-ները հակված են ծուլանալու և անվստահելի են, երբ անհրաժեշտ է ամբողջական ու ճշգրիտ բովանդակություն, հատկապես երկար համատեքստի դեպքում։ Եվ դա միանգամայն հասկանալի է։ Սակայն այս կոնկրետ կիրառության դեպքում դա անընդունելի էր, քանի որ մեզ անհրաժեշտ էր բառ առ բառ ճշգրիտ բովանդակությունը՝ առանց ամփոփումների և սկզբնական բովանդակության որևէ մաս բաց թողնելու։ Չենք կարող բաց թողնել ոչ մի մանրամասն։

Իսկ պատասխանի «այո» մասն, իհարկե, այն էր, որ LLM-ը հրաշալի էր հասկանում մասնատված հատվածների իմաստն ու կառուցվածքը։ Միայն թե երբեմն հրաժարվում էր ճշգրիտ վերարտադրել բովանդակությունը։ Գրողը տանի :/

Այդ դեպքում ինչպե՞ս օգտվենք LLM-ի ուժեղ կողմերից՝ խուսափելով այն խնդիրներից, որոնցում այն անվստահելի է։ Դիմեցինք մեր հին ու վստահելի ընկերոջը՝ կոդին (կարդացեք՝ հատուկ Python ֆունկցիային)։ Եվ «ավելի պարզ լինել չէր կարող» Pydantic մոդելին։ Ահա լուծումը՝

  • Հերթով անցնել բաժիններով՝ միաժամանակ պահպանելով ընթացիկ տրամաբանական հատվածը

  • Յուրաքանչյուր բաժնի դեպքում LLM-ին հարցնել՝ արդյո՞ք այս բաժինը պատկանում է ընթացիկ տրամաբանական հատվածին, և պահանջել «այո» կամ «ոչ» պատասխան՝ Pydantic մոդելին համապատասխան։

  • Եթե այո, բաժինը կցել հատվածին։ Եթե ոչ, ավարտված ընթացիկ տրամաբանական հատվածը դուրս բերել և այդ բաժնով սկսել նորը։

Գծապատկեր, որը ցույց է տալիս, թե երբ է ամեն ինչ բարդանում։

Իհարկե, այստեղ մի փոքր ավելի շատ թոքեն ենք օգտագործում, քան ամբողջ բովանդակությունը մեկ անցումով մշակելիս։ Բայց այս կոնկրետ կիրառության դեպքում, երբ ճշգրիտ բովանդակության պահպանումն առաջնահերթ էր, լրացուցիչ՝ աննշան ծախսը լիովին արդարացված էր։

Սա շատ պարզ լուծում է, բայց հետևում է մի կարևոր սկզբունքի. երբ անհրաժեշտ է խստություն, չպետք է ապավինել միայն LLM-ներին, քանի որ դրանք, ի վերջո, հավանականային են։

Հատուկ կոդը կամ ֆունկցիաները և Pydantic մոդելները կարելի է կիրառել կանխատեսելի ու հուսալի արդյունք ստանալու համար՝ միաժամանակ լիարժեք օգտագործելով LLM-ների հնարավորությունները։

Ամփոփում

Գեներատիվ ԱԲ լուծման ստեղծումը նույնքան ինժեներական խնդիր է, որքան ԱԲ-ի խնդիր։ Հուսով ենք՝ այս օրինակները ոգեշնչել են ձեզ լուծելու ձեր սեփական յուրահատուկ խնդիրները։ Ինժեներական մոտեցման վրա հիմնված գեներատիվ ԱԲ լուծումների մասին ավելին կարդալու համար ծանոթացեք երթուղիչների վրա հիմնված գործակալային համակարգերի նախագծման վերաբերյալ մեր բլոգային գրառմանը։

Հեղինակ

Cynthia Yu