Folosirea graficelor de cunoștințe pentru a îmbunătăți rezultatele aplicațiilor de generare augmentată de recuperare (RAG) a devenit un subiect fierbinte. Cele mai multe exemple demonstrează cum să construiți un grafic de cunoștințe folosind un număr relativ mic de documente. Acest lucru s-ar putea datora faptului că abordarea tipică - extragerea de informații detaliate, centrate pe entitate - pur și simplu nu se extind. Rularea fiecărui document printr-un model pentru a extrage entitățile (nodurile) și relațiile (marginile) durează prea mult (și costă prea mult) pentru a rula pe seturi de date mari.
Noi am argumentat asta
Încărcarea documentelor într-un magazin de grafice centrat pe entitate, cum ar fi Neo4j, a fost realizată folosind LLMGraphTransformer de la LangChain. Codul se bazează pe LangChain
from langchain_core.documents import Document from langchain_experimental.graph_transformers import LLMGraphTransformer from langchain_openai import ChatOpenAI llm = ChatOpenAI(temperature=0, model_name="gpt-4-turbo") llm_transformer = LLMGraphTransformer(llm=llm) from time import perf_counter start = perf_counter() documents_to_load = [Document(page_content=line) for line in lines_to_load] graph_documents = llm_transformer.convert_to_graph_documents(documents_to_load) end = perf_counter() print(f"Loaded (but NOT written) {NUM_LINES_TO_LOAD} in {end - start:0.2f}s")
Încărcarea datelor în GraphVectorStore este aproximativ aceeași cu încărcarea lor într-un magazin de vectori. Singura adăugare este că calculăm metadate care indică modul în care fiecare pagină face legătura cu alte pagini.
import json from langchain_core.graph_vectorstores.links import METADATA_LINKS_KEY, Link def parse_document(line: str) -> Document: para = json.loads(line) id = para["id"] links = { Link.outgoing(kind="href", tag=id) for m in para["mentions"] if m["ref_ids"] is not None for id in m["ref_ids"] } links.add(Link.incoming(kind="href", tag=id)) return Document( id = id, page_content = " ".join(para["sentences"]), metadata = { "content_id": para["id"], METADATA_LINKS_KEY: list(links) }, )
Acesta este, de asemenea, un exemplu bun despre cum vă puteți adăuga propriile legături între noduri.
from langchain_openai import OpenAIEmbeddings from langchain_community.graph_vectorstores.cassandra import CassandraGraphVectorStore import cassio cassio.init(auto=True) TABLE_NAME = "wiki_load" store = CassandraGraphVectorStore( embedding = OpenAIEmbeddings(), node_table=TABLE_NAME, insert_timeout = 1000.0, ) from time import perf_counter start = perf_counter() from datasets.wikimultihop.load import parse_document kg_documents = [parse_document(line) for line in lines_to_load] store.add_documents(kg_documents) end = perf_counter() print(f"Loaded (and written) {NUM_LINES_TO_LOAD} in {end - start:0.2f}s")
Rulând pe 100 de rânduri, abordarea centrată pe entitate folosind GPT-4o a luat 405,93 s pentru a extrage documentele Graph și 10,99 s pentru a le scrie pe Neo4j, în timp ce abordarea centrată pe conținut a durat 1,43 s. Extrapolând, ar dura 41 de săptămâni pentru a încărca toate cele 5.989.847 de pagini folosind abordarea centrată pe entitate și aproximativ 24 de ore folosind abordarea centrată pe conținut. Dar datorită paralelismului, abordarea centrată pe conținut rulează în doar 2,5 ore! Presupunând aceleași beneficii de paralelism, ar dura încă patru săptămâni pentru a încărca totul folosind abordarea centrată pe entitate. Nu l-am încercat, deoarece costul estimat ar fi de 58.700 USD - presupunând că totul a funcționat prima dată!
Concluzie: abordarea centrată pe entitate de extragere a graficelor de cunoștințe din conținut folosind un LLM a fost atât timp cât și cost prohibitivă la scară. Pe de altă parte, utilizarea GraphVectorStore a fost rapidă și ieftină.
În această secțiune, sunt adresate câteva întrebări, extrase din subsetul de documente încărcate, pentru a aborda calitatea răspunsurilor.
Entity-centric a folosit 7324 de jetoane prompt și a costat 0,03 USD pentru a produce răspunsuri practic inutile, în timp ce centrat pe conținut a folosit 450 de jetoane prompt și a costat 0,002 USD pentru a produce răspunsuri concise care răspund direct la întrebări.
Poate fi surprinzător că graficul Neo4j cu granulație fină returnează răspunsuri inutile. Privind înregistrarea din lanț, vedem câteva dintre motivele pentru care se întâmplă acest lucru:
> Entering new GraphCypherQAChain chain... Generated Cypher: cypher MATCH (a:Album {id: 'The Circle'})-[:RELEASED_BY]->(r:Record_label) RETURN a.id, r.id Full Context: [{'a.id': 'The Circle', 'r.id': 'Restless'}] > Finished chain. {'query': "When was 'The Circle' released?", 'result': "I don't know the answer."}
Deci, schema cu granulație fină a returnat doar informații despre casa de discuri. Este logic că LLM nu a putut să răspundă la întrebare pe baza informațiilor preluate.
Extragerea graficelor de cunoștințe detaliate, specifice entității, este prohibitivă în timp și costuri la scară. Când au fost puse întrebări despre subsetul de date care a fost încărcat, granularitatea suplimentară (și costul suplimentar de încărcare a graficului cu granulație fină) a returnat mai multe jetoane pentru a include promptul, dar a generat răspunsuri inutile!
GraphVectorStore adoptă o abordare grosieră, centrată pe conținut, care face rapidă și ușoară construirea unui grafic de cunoștințe. Puteți începe cu codul existent pentru popularea unui VectorStore folosind LangChain și puteți adăuga legături (margini) între bucăți pentru a îmbunătăți procesul de recuperare.
Graph RAG este un instrument util pentru a permite aplicațiilor generative AI RAG să recupereze contexte mai profund relevante. Dar utilizarea unei abordări precise, centrată pe entitate, nu se adaptează la nevoile de producție. Dacă doriți să adăugați capabilități grafice de cunoștințe aplicației dvs. RAG, încercați
De Ben Chambers , DataStax