Të gjithëve u pëlqejnë modelet e futjes së tekstit dhe për arsye të mirë: ato shkëlqejnë në kodimin e tekstit të pastrukturuar, duke e bërë më të lehtë zbulimin e përmbajtjeve të ngjashme semantike. Nuk është për t'u habitur që ato përbëjnë shtyllën kurrizore të shumicës së aplikacioneve RAG, veçanërisht me theksin aktual në kodimin dhe marrjen e informacionit përkatës nga dokumentet dhe burimet e tjera tekstuale. Megjithatë, ka shembuj të qartë të pyetjeve që mund të shtrohen kur qasja e ngulitjes së tekstit në aplikacionet RAG është e shkurtër dhe jep informacion të pasaktë.
Siç u përmend, futjet e tekstit janë të shkëlqyera në kodimin e tekstit të pastrukturuar. Nga ana tjetër, ata nuk janë aq të mirë në trajtimin e informacionit të strukturuar dhe operacioneve të tilla si filtrimi , renditja ose grumbullimet . Imagjinoni një pyetje të thjeshtë si:
Cili është filmi më i vlerësuar i publikuar në 2024?
Për t'iu përgjigjur kësaj pyetjeje, fillimisht duhet të filtrojmë sipas vitit të lëshimit, e më pas të renditim sipas vlerësimit. Ne do të shqyrtojmë se si funksionon një qasje naive me ngulitje teksti dhe më pas do të demonstrojmë se si të trajtojmë pyetje të tilla. Ky postim në blog tregon se kur keni të bëni me operacione të strukturuara të të dhënave si filtrimi, renditja ose grumbullimi, duhet të përdorni mjete të tjera që ofrojnë strukturë si grafikët e njohurive.
Kodi është i disponueshëm në GitHub .
Për këtë postim në blog, ne do të përdorim projektin e rekomandimeve në Neo4j Sandbox . Projekti i rekomandimeve përdor grupin e të dhënave MovieLens , i cili përmban filma, aktorë, vlerësime dhe më shumë informacion.
Kodi i mëposhtëm do të instantojë një mbështjellës LangChain për t'u lidhur me bazën e të dhënave Neo4j:
os.environ["NEO4J_URI"] = "bolt://44.204.178.84:7687" os.environ["NEO4J_USERNAME"] = "neo4j" os.environ["NEO4J_PASSWORD"] = "minimums-triangle-saving" graph = Neo4jGraph(refresh_schema=False)
Për më tepër, do t'ju duhet një çelës OpenAI API që të kaloni në kodin e mëposhtëm:
os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")
Baza e të dhënave përmban 10,000 filma, por futjet e tekstit nuk janë ruajtur ende. Për të shmangur llogaritjen e futjeve për të gjithë ata, ne do t'i etiketojmë 1000 filmat më të vlerësuar me një etiketë dytësore të quajtur Target :
graph.query(""" MATCH (m:Movie) WHERE m.imdbRating IS NOT NULL WITH m ORDER BY m.imdbRating DESC LIMIT 1000 SET m:Target """)
Të vendosësh se çfarë të futësh është një konsideratë e rëndësishme. Meqenëse do të demonstrojmë filtrimin sipas vitit dhe renditjen sipas vlerësimit, nuk do të ishte e drejtë të përjashtoheshin ato detaje nga teksti i integruar. Kjo është arsyeja pse zgjodha të kap vitin e publikimit, vlerësimin, titullin dhe përshkrimin e secilit film.
Këtu është një shembull i tekstit që do të vendosim për filmin The Wolf of Wall Street :
plot: Based on the true story of Jordan Belfort, from his rise to a wealthy stock-broker living the high life to his fall involving crime, corruption and the federal government. title: Wolf of Wall Street, The year: 2013 imdbRating: 8.2
Ju mund të thoni se kjo nuk është një qasje e mirë për të futur të dhëna të strukturuara, dhe unë nuk do të argumentoja pasi nuk e di qasjen më të mirë. Ndoshta në vend të artikujve me vlerë kyçe, ne duhet t'i konvertojmë ato në tekst ose diçka tjetër. Më tregoni nëse keni disa ide se çfarë mund të funksionojë më mirë.
Objekti Neo4j Vector në LangChain ka një metodë të përshtatshme nga_existing_graph ku mund të zgjidhni cilat veti teksti duhet të kodohen:
embedding = OpenAIEmbeddings(model="text-embedding-3-small") neo4j_vector = Neo4jVector.from_existing_graph( embedding=embedding, index_name="movies", node_label="Target", text_node_properties=["plot", "title", "year", "imdbRating"], embedding_node_property="embedding", )
Në këtë shembull, ne përdorim modelin e tekstit-embedding-3-small të OpenAI për gjenerimin e ngulitjes. Ne inicializojmë objektin Neo4jVector duke përdorur metodën from_existing_graph. Parametri node_label filtron nyjet që do të kodohen, veçanërisht ato të etiketuara Target . Parametri text_node_properties përcakton vetitë e nyjës që do të futen, duke përfshirë grafikun , titullin , vitin dhe imdbRating . Së fundi, embedding_node_property përcakton veçorinë ku do të ruhen embedding-et e krijuara, të përcaktuara si embedding .
Le të fillojmë duke u përpjekur të gjejmë një film bazuar në komplotin ose përshkrimin e tij:
pretty_print( neo4j_vector.similarity_search( "What is a movie where a little boy meets his hero?" ) )
Rezultatet:
plot: A young boy befriends a giant robot from outer space that a paranoid government agent wants to destroy. title: Iron Giant, The year: 1999 imdbRating: 8.0 plot: After the death of a friend, a writer recounts a boyhood journey to find the body of a missing boy. title: Stand by Me year: 1986 imdbRating: 8.1 plot: A young, naive boy sets out alone on the road to find his wayward mother. Soon he finds an unlikely protector in a crotchety man and the two have a series of unexpected adventures along the way. title: Kikujiro (Kikujirô no natsu) year: 1999 imdbRating: 7.9 plot: While home sick in bed, a young boy's grandfather reads him a story called The Princess Bride. title: Princess Bride, The year: 1987 imdbRating: 8.1
Rezultatet duken mjaft solide në përgjithësi. Është i përfshirë vazhdimisht një djalë i vogël, megjithëse nuk jam i sigurt nëse ai takohet gjithmonë me heroin e tij. Pastaj përsëri, grupi i të dhënave përfshin vetëm 1000 filma, kështu që opsionet janë disi të kufizuara.
Tani le të provojmë një pyetje që kërkon disa filtrim bazë:
pretty_print( neo4j_vector.similarity_search( "Which movies are from year 2016?" ) )
Rezultatet:
plot: Six short stories that explore the extremities of human behavior involving people in distress. title: Wild Tales year: 2014 imdbRating: 8.1 plot: A young man who survives a disaster at sea is hurtled into an epic journey of adventure and discovery. While cast away, he forms an unexpected connection with another survivor: a fearsome Bengal tiger. title: Life of Pi year: 2012 imdbRating: 8.0 plot: Based on the true story of Jordan Belfort, from his rise to a wealthy stock-broker living the high life to his fall involving crime, corruption and the federal government. title: Wolf of Wall Street, The year: 2013 imdbRating: 8.2 plot: After young Riley is uprooted from her Midwest life and moved to San Francisco, her emotions - Joy, Fear, Anger, Disgust and Sadness - conflict on how best to navigate a new city, house, and school. title: Inside Out year: 2015 imdbRating: 8.3
Është qesharake, por nuk u zgjodh asnjë film i vitit 2016. Ndoshta mund të arrijmë rezultate më të mira me përgatitjen e teksteve të ndryshme për kodim. Megjithatë, futjet e tekstit nuk janë të zbatueshme këtu pasi kemi të bëjmë me një operacion të thjeshtë të strukturuar të të dhënave ku duhet të filtrojmë dokumente ose, në këtë shembull, filma të bazuar në një veçori meta të dhënash. Filtrimi i meta të dhënave është një teknikë e mirëpërcaktuar që shpesh përdoret për të rritur saktësinë e sistemeve RAG.
Pyetja tjetër që do të provojmë kërkon pak renditje:
pretty_print( neo4j_vector.similarity_search("Which movie has the highest imdb score?") )
Rezultatet:
plot: A silent film production company and cast make a difficult transition to sound. title: Singin' in the Rain year: 1952 imdbRating: 8.3 plot: A film about the greatest pre-Woodstock rock music festival. title: Monterey Pop year: 1968 imdbRating: 8.1 plot: This movie documents the Apollo missions perhaps the most definitively of any movie under two hours. Al Reinert watched all the footage shot during the missions--over 6,000,000 feet of it, ... title: For All Mankind year: 1989 imdbRating: 8.2 plot: An unscrupulous movie producer uses an actress, a director and a writer to achieve success. title: Bad and the Beautiful, The year: 1952 imdbRating: 7.9
Nëse jeni të njohur me vlerësimet në IMDb, e dini se ka shumë filma me rezultate mbi 8.3. Titulli më i vlerësuar në bazën tonë të të dhënave është në fakt një seri — Band of Brothers — me një vlerësim mbresëlënës 9.6. Edhe një herë, futjet e tekstit performojnë dobët kur bëhet fjalë për renditjen e rezultateve.
Le të vlerësojmë gjithashtu një pyetje që kërkon një lloj grumbullimi:
pretty_print(neo4j_vector.similarity_search("How many movies are there?"))
Rezultatet:
plot: Ten television drama films, each one based on one of the Ten Commandments. title: Decalogue, The (Dekalog) year: 1989 imdbRating: 9.2 plot: A documentary which challenges former Indonesian death-squad leaders to reenact their mass-killings in whichever cinematic genres they wish, including classic Hollywood crime scenarios and lavish musical numbers. title: Act of Killing, The year: 2012 imdbRating: 8.2 plot: A meek Hobbit and eight companions set out on a journey to destroy the One Ring and the Dark Lord Sauron. title: Lord of the Rings: The Fellowship of the Ring, The year: 2001 imdbRating: 8.8 plot: While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard. title: Lord of the Rings: The Two Towers, The year: 2002 imdbRating: 8.7
Rezultatet definitivisht nuk janë të dobishme këtu sepse kemi kthyer katër filma të rastësishëm. Është praktikisht e pamundur të arrish nga këto katër filma të rastësishëm një përfundim se ka gjithsej 1000 filma që kemi etiketuar dhe ngulitur për këtë shembull.
Pra, cila është zgjidhja? Është e drejtpërdrejtë: Pyetjet që përfshijnë operacione të strukturuara si filtrimi, renditja dhe grumbullimi kanë nevojë për mjete të dizajnuara për të funksionuar me të dhëna të strukturuara.
Për momentin, duket se shumica e njerëzve mendojnë për qasjen text2query, ku një LLM gjeneron një pyetje të bazës së të dhënave për të bashkëvepruar me një bazë të dhënash bazuar në pyetjen dhe skemën e dhënë. Për Neo4j, ky është text2cypher, por ekziston edhe text2sql për bazat e të dhënave SQL. Megjithatë, në praktikë rezulton se nuk është i besueshëm dhe jo mjaftueshëm i fortë për përdorim në prodhim.
Vlerësimi i gjenerimit të deklaratave cypher. Marrë nga postimi im në blog rreth vlerësimit të Cypher .
Ju mund të përdorni teknika si zinxhiri i mendimit, shembujt me pak të shtëna ose rregullimi i imët, por arritja e saktësisë së lartë mbetet pothuajse e pamundur në këtë fazë. Qasja text2query funksionon mirë për pyetje të thjeshta mbi skemat e drejtpërdrejta të bazës së të dhënave, por ky nuk është realiteti i mjediseve të prodhimit. Për të adresuar këtë, ne e zhvendosim kompleksitetin e gjenerimit të pyetjeve të bazës së të dhënave nga një LLM dhe e trajtojmë atë si një problem kodi ku gjenerojmë pyetjet e bazës së të dhënave në mënyrë deterministe bazuar në hyrjet e funksionit. Avantazhi është qëndrueshmëria e përmirësuar ndjeshëm, megjithëse vjen me koston e reduktimit të fleksibilitetit. Është më mirë të ngushtoni qëllimin e aplikacionit RAG dhe t'u përgjigjeni atyre pyetjeve me saktësi, në vend që të përpiqeni t'i përgjigjeni gjithçkaje, por ta bëni këtë në mënyrë të pasaktë.
Meqenëse ne po gjenerojmë pyetje të bazës së të dhënave - në këtë rast, deklarata Cypher - bazuar në inputet e funksionit, ne mund të shfrytëzojmë aftësitë e veglave të LLM-ve. Në këtë proces, LLM plotëson parametrat përkatës bazuar në hyrjen e përdoruesit, ndërsa funksioni merret me marrjen e informacionit të nevojshëm. Për këtë demonstrim, ne fillimisht do të zbatojmë dy mjete: një për numërimin e filmave dhe një tjetër për renditjen e tyre, dhe më pas do të krijojmë një agjent LLM duke përdorur LangGraph.
Fillojmë duke zbatuar një mjet për numërimin e filmave bazuar në filtra të paracaktuar. Së pari, ne duhet të përcaktojmë se cilat janë ato filtra dhe t'i përshkruajmë një LLM kur dhe si t'i përdorim ato:
class MovieCountInput(BaseModel): min_year: Optional[int] = Field( description="Minimum release year of the movies" ) max_year: Optional[int] = Field( description="Maximum release year of the movies" ) min_rating: Optional[float] = Field(description="Minimum imdb rating") grouping_key: Optional[str] = Field( description="The key to group by the aggregation", enum=["year"] )
LangChain ofron disa mënyra për të përcaktuar hyrjet e funksionit, por unë preferoj qasjen Pydantic. Në këtë shembull, ne kemi tre filtra të disponueshëm për të përmirësuar rezultatet e filmit: min_year, max_year dhe min_rating. Këta filtra bazohen në të dhëna të strukturuara dhe janë opsionale, pasi përdoruesi mund të zgjedhë të përfshijë ndonjë, të gjithë ose asnjë prej tyre. Për më tepër, ne kemi prezantuar një hyrje grupimi_kyç që i tregon funksionit nëse duhet të grupojë numërimin sipas një vetie specifike. Në këtë rast, i vetmi grupim i mbështetur është sipas vitit, siç përcaktohet në përmbledhje.
Tani le të përcaktojmë funksionin aktual:
@tool("movie-count", args_schema=MovieCountInput) def movie_count( min_year: Optional[int], max_year: Optional[int], min_rating: Optional[float], grouping_key: Optional[str], ) -> List[Dict]: """Calculate the count of movies based on particular filters""" filters = [ ("t.year >= $min_year", min_year), ("t.year <= $max_year", max_year), ("t.imdbRating >= $min_rating", min_rating), ] # Create the parameters dynamically from function inputs params = { extract_param_name(condition): value for condition, value in filters if value is not None } where_clause = " AND ".join( [condition for condition, value in filters if value is not None] ) cypher_statement = "MATCH (t:Target) " if where_clause: cypher_statement += f"WHERE {where_clause} " return_clause = ( f"t.`{grouping_key}`, count(t) AS movie_count" if grouping_key else "count(t) AS movie_count" ) cypher_statement += f"RETURN {return_clause}" print(cypher_statement) # Debugging output return graph.query(cypher_statement, params=params)
Funksioni movie_count gjeneron një pyetje Cypher për të numëruar filmat bazuar në filtrat opsionalë dhe çelësin e grupimit. Fillon duke përcaktuar një listë filtrash me vlerat përkatëse të dhëna si argumente. Filtrat përdoren për të ndërtuar në mënyrë dinamike klauzolën WHERE, e cila është përgjegjëse për zbatimin e kushteve të specifikuara të filtrimit në deklaratën Cypher, duke përfshirë vetëm ato kushte ku vlerat nuk janë Asnjë.
Më pas ndërtohet klauzola RETURN i pyetjes Cypher, ose duke u grupuar sipas grupit_çelës të dhënë ose thjesht duke numëruar numrin total të filmave. Së fundi, funksioni ekzekuton pyetjen dhe kthen rezultatet.
Funksioni mund të zgjerohet me më shumë argumente dhe logjikë më të përfshirë sipas nevojës, por është e rëndësishme të siguroheni që ai të mbetet i qartë në mënyrë që një LLM ta thërrasë saktë dhe saktë.
Përsëri, duhet të fillojmë duke përcaktuar argumentet e funksionit:
class MovieListInput(BaseModel): sort_by: str = Field( description="How to sort movies, can be one of either latest, rating", enum=["latest", "rating"], ) k: Optional[int] = Field(description="Number of movies to return") description: Optional[str] = Field(description="Description of the movies") min_year: Optional[int] = Field( description="Minimum release year of the movies" ) max_year: Optional[int] = Field( description="Maximum release year of the movies" ) min_rating: Optional[float] = Field(description="Minimum imdb rating")
Ne mbajmë të njëjtat tre filtra si në funksionin e numërimit të filmave, por shtojmë argumentin e përshkrimit. Ky argument na lejon të kërkojmë dhe listojmë filma bazuar në komplotin e tyre duke përdorur kërkimin e ngjashmërisë vektoriale. Vetëm për shkak se ne përdorim mjete dhe filtra të strukturuar, nuk do të thotë se nuk mund të përfshijmë metodat e ngulitjes së tekstit dhe kërkimit vektor. Meqenëse nuk duam t'i kthejmë të gjithë filmat shumicën e kohës, ne përfshijmë një hyrje opsionale k me një vlerë të paracaktuar. Për më tepër, për listim, ne duam të renditim filmat për të kthyer vetëm ato më të rëndësishmet. Në këtë rast, ne mund t'i renditim ato sipas vlerësimit ose vitit të lëshimit.
Le të zbatojmë funksionin:
@tool("movie-list", args_schema=MovieListInput) def movie_list( sort_by: str = "rating", k : int = 4, description: Optional[str] = None, min_year: Optional[int] = None, max_year: Optional[int] = None, min_rating: Optional[float] = None, ) -> List[Dict]: """List movies based on particular filters""" # Handle vector-only search when no prefiltering is applied if description and not min_year and not max_year and not min_rating: return neo4j_vector.similarity_search(description, k=k) filters = [ ("t.year >= $min_year", min_year), ("t.year <= $max_year", max_year), ("t.imdbRating >= $min_rating", min_rating), ] # Create parameters dynamically from function arguments params = { key.split("$")[1]: value for key, value in filters if value is not None } where_clause = " AND ".join( [condition for condition, value in filters if value is not None] ) cypher_statement = "MATCH (t:Target) " if where_clause: cypher_statement += f"WHERE {where_clause} " # Add the return clause with sorting cypher_statement += " RETURN t.title AS title, t.year AS year, t.imdbRating AS rating ORDER BY " # Handle sorting logic based on description or other criteria if description: cypher_statement += ( "vector.similarity.cosine(t.embedding, $embedding) DESC " ) params["embedding"] = embedding.embed_query(description) elif sort_by == "rating": cypher_statement += "t.imdbRating DESC " else: # sort by latest year cypher_statement += "t.year DESC " cypher_statement += " LIMIT toInteger($limit)" params["limit"] = k or 4 print(cypher_statement) # Debugging output data = graph.query(cypher_statement, params=params) return data
Ky funksion rimerr një listë filmash bazuar në filtra të shumtë opsionalë: përshkrimi, diapazoni i vitit, vlerësimi minimal dhe preferencat e renditjes. Nëse jepet vetëm një përshkrim pa filtra të tjerë, ai kryen një kërkim të ngjashmërisë së indeksit të vektorit për të gjetur filma përkatës. Kur aplikohen filtra shtesë, funksioni ndërton një pyetje Cypher për të përputhur filmat bazuar në kriteret e specifikuara, si viti i publikimit dhe vlerësimi IMDb, duke i kombinuar ato me një ngjashmëri opsionale të bazuar në përshkrim. Rezultatet më pas renditen sipas rezultatit të ngjashmërisë, vlerësimit në IMDb ose vitit, dhe kufizohen në k filma.
Ne do të zbatojmë një agjent të drejtpërdrejtë ReAct duke përdorur LangGraph.
Agjenti përbëhet nga një hap LLM dhe mjete. Ndërsa ndërveprojmë me agjentin, fillimisht do të telefonojmë LLM-në për të vendosur nëse duhet të përdorim mjete. Pastaj do të ekzekutojmë një lak:
Zbatimi i kodit është aq i drejtpërdrejtë sa mund të bëhet. Së pari ne lidhim mjetet me LLM dhe përcaktojmë hapin e ndihmës:
llm = ChatOpenAI(model='gpt-4-turbo') tools = [movie_count, movie_list] llm_with_tools = llm.bind_tools(tools) # System message sys_msg = SystemMessage(content="You are a helpful assistant tasked with finding and explaining relevant information about movies.") # Node def assistant(state: MessagesState): return {"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])]}
Më pas ne përcaktojmë rrjedhën LangGraph:
# Graph builder = StateGraph(MessagesState) # Define nodes: these do the work builder.add_node("assistant", assistant) builder.add_node("tools", ToolNode(tools)) # Define edges: these determine how the control flow moves builder.add_edge(START, "assistant") builder.add_conditional_edges( "assistant", # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END tools_condition, ) builder.add_edge("tools", "assistant") react_graph = builder.compile()
Ne përcaktojmë dy nyje në LangGraph dhe i lidhim ato me një skaj të kushtëzuar. Nëse thirret një mjet, rrjedha drejtohet te veglat; përndryshe, rezultatet i kthehen përdoruesit.
Le të testojmë tani agjentin tonë:
messages = [ HumanMessage( content="What are the some movies about a girl meeting her hero?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
Rezultatet:
Në hapin e parë, agjenti zgjedh të përdorë mjetin e listës së filmave me parametrin e duhur të përshkrimit. Është e paqartë pse zgjedh një kvalue prej 5, por duket se favorizon këtë numër. Mjeti kthen pesë filmat më të rëndësishëm bazuar në komplot dhe LLM thjesht i përmbledh ato për përdoruesit në fund.
Nëse pyesim ChatGPT pse i pëlqen vlera k prej 5, marrim përgjigjen e mëposhtme.
Më pas, le të bëjmë një pyetje pak më komplekse që kërkon filtrim të meta të dhënave:
messages = [ HumanMessage( content="What are the movies from the 90s about a girl meeting her hero?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
Rezultatet:
Këtë herë, argumente shtesë u përdorën për të filtruar filmat vetëm nga vitet 1990. Ky shembull do të ishte një shembull tipik i filtrimit të meta të dhënave duke përdorur qasjen e para-filtrimit. Deklarata e krijuar Cypher fillimisht ngushton filmat duke filtruar në vitin e tyre të publikimit. Në pjesën tjetër, deklarata Cypher përdor ngulitje teksti dhe kërkim të ngjashmërisë vektoriale për të gjetur filma rreth një vajze të vogël që takohet me heroin e saj.
Le të përpiqemi të numërojmë filma bazuar në kushte të ndryshme:
messages = [ HumanMessage( content="How many movies are from the 90s have the rating higher than 9.1?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
Rezultatet:
Me një mjet të dedikuar për numërim, kompleksiteti kalon nga LLM në mjet, duke e lënë LLM përgjegjës vetëm për plotësimin e parametrave përkatës të funksionit. Kjo ndarje e detyrave e bën sistemin më efikas dhe më të fortë dhe redukton kompleksitetin e hyrjes LLM.
Meqenëse agjenti mund të thërrasë shumë mjete në mënyrë sekuenciale ose paralele, le ta testojmë atë me diçka edhe më komplekse:
messages = [ HumanMessage( content="How many were movies released per year made after the highest rated movie?" ) ] messages = react_graph.invoke({"messages": messages}) for m in messages["messages"]: m.pretty_print()
Rezultatet
Siç u përmend, agjenti mund të thërrasë mjete të shumta për të mbledhur të gjithë informacionin e nevojshëm për t'iu përgjigjur pyetjes. Në këtë shembull, ai fillon duke renditur filmat me vlerësimin më të lartë për të identifikuar se kur u publikua filmi më i vlerësuar. Pasi të ketë ato të dhëna, ai thërret mjetin e numërimit të filmave për të mbledhur numrin e filmave të lëshuar pas vitit të caktuar, duke përdorur një çelës grupimi siç përcaktohet në pyetje.
Ndërsa futjet e tekstit janë të shkëlqyera për të kërkuar nëpër të dhëna të pastrukturuara, ato nuk janë të shkurtra kur bëhet fjalë për operacione të strukturuara si filtrimi , renditja dhe grumbullimi . Këto detyra kërkojnë mjete të dizajnuara për të dhëna të strukturuara, të cilat ofrojnë saktësinë dhe fleksibilitetin e nevojshëm për të trajtuar këto operacione. Gjëja kryesore është se zgjerimi i grupit të mjeteve në sistemin tuaj ju lejon të adresoni një gamë më të gjerë të pyetjeve të përdoruesve, duke i bërë aplikacionet tuaja më të fuqishme dhe të gjithanshme. Kombinimi i qasjeve të të dhënave të strukturuara dhe teknikave të kërkimit të tekstit të pastrukturuar mund të japë përgjigje më të sakta dhe më të rëndësishme, duke përmirësuar përfundimisht përvojën e përdoruesit në aplikacionet RAG.
Si gjithmonë, kodi është i disponueshëm në GitHub .
Për të mësuar më shumë rreth kësaj teme, bashkohuni me ne në NODES 2024 më 7 nëntor, konferencën tonë falas të zhvilluesve virtualë mbi aplikacionet inteligjente, grafikët e njohurive dhe AI. Regjistrohu Tani!