paint-brush
Ενίσχυση RAG με Γραφήματα Γνώσης: Ενσωμάτωση Llama 3.1, NVIDIA NIM και LangChain για Dynamic AIμε@neo4j
Νέα ιστορία

Ενίσχυση RAG με Γραφήματα Γνώσης: Ενσωμάτωση Llama 3.1, NVIDIA NIM και LangChain για Dynamic AI

με Neo4j9m2024/10/22
Read on Terminal Reader

Πολύ μακρύ; Να διαβασω

Αυτό το άρθρο δείχνει τη χρήση των Llama 3.1, NVIDIA NIM και LangChain για τη δημιουργία ενός πράκτορα βασισμένου σε γραφήματα γνώσης για επαυξημένη παραγωγή ανάκτησης (RAG), αξιοποιώντας δομημένα δεδομένα και δυναμική δημιουργία ερωτημάτων για τη βελτίωση της ανάκτησης πληροφοριών και της ακρίβειας απόκρισης.
featured image - Ενίσχυση RAG με Γραφήματα Γνώσης: Ενσωμάτωση Llama 3.1, NVIDIA NIM και LangChain για Dynamic AI
Neo4j HackerNoon profile picture
0-item
1-item



Ενώ οι περισσότεροι άνθρωποι επικεντρώνονται στην επαυξημένη παραγωγή ανάκτησης (RAG) πάνω από μη δομημένο κείμενο, όπως έγγραφα ή τεκμηρίωση εταιρείας, είμαι αρκετά θετικός στα συστήματα ανάκτησης έναντι δομημένων πληροφοριών, ιδιαίτερα γραφημάτων γνώσης . Υπήρξε πολύς ενθουσιασμός για το GraphRAG, ειδικά για την υλοποίηση της Microsoft. Ωστόσο, κατά την υλοποίησή τους, τα δεδομένα εισόδου είναι αδόμητο κείμενο με τη μορφή εγγράφων, το οποίο μετατρέπεται σε γράφημα γνώσης χρησιμοποιώντας ένα μεγάλο γλωσσικό μοντέλο (LLM).


Σε αυτήν την ανάρτηση ιστολογίου, θα δείξουμε πώς να εφαρμόσετε ένα retriever σε ένα γράφημα γνώσεων που περιέχει δομημένες πληροφορίες από το Σύστημα Αναφοράς Ανεπιθύμητων Συμβάντων (FAERS) της FDA , το οποίο προσφέρει πληροφορίες σχετικά με ανεπιθύμητες ενέργειες φαρμάκων. Εάν έχετε ασχοληθεί ποτέ με γραφήματα γνώσης και ανάκτηση, η πρώτη σας σκέψη μπορεί να είναι να χρησιμοποιήσετε ένα LLM για να δημιουργήσετε ερωτήματα βάσης δεδομένων για να ανακτήσετε σχετικές πληροφορίες από ένα γράφημα γνώσης για να απαντήσετε σε μια δεδομένη ερώτηση. Ωστόσο, η δημιουργία ερωτημάτων βάσης δεδομένων με χρήση LLM εξακολουθεί να εξελίσσεται και ενδέχεται να μην προσφέρει ακόμη την πιο συνεπή ή ισχυρή λύση. Λοιπόν, ποιες είναι οι βιώσιμες εναλλακτικές λύσεις αυτή τη στιγμή;


Κατά τη γνώμη μου, η καλύτερη τρέχουσα λύση είναι η δυναμική δημιουργία ερωτημάτων. Αντί να βασίζεται εξ ολοκλήρου σε ένα LLM για τη δημιουργία του πλήρους ερωτήματος, αυτή η μέθοδος χρησιμοποιεί ένα λογικό επίπεδο που δημιουργεί ντετερμινιστικά ένα ερώτημα βάσης δεδομένων από προκαθορισμένες παραμέτρους εισόδου. Αυτή η λύση μπορεί να υλοποιηθεί χρησιμοποιώντας ένα LLM με υποστήριξη κλήσης συναρτήσεων. Το πλεονέκτημα της χρήσης μιας δυνατότητας κλήσης συνάρτησης έγκειται στην ικανότητα να ορίζει σε ένα LLM πώς πρέπει να προετοιμάζει μια δομημένη είσοδο σε μια συνάρτηση. Αυτή η προσέγγιση διασφαλίζει ότι η διαδικασία δημιουργίας ερωτημάτων είναι ελεγχόμενη και συνεπής, ενώ επιτρέπει την ευελιξία των εισαγωγών από τον χρήστη.


Ροή δημιουργίας δυναμικού ερωτήματος — Εικόνα από συγγραφέα


Η εικόνα απεικονίζει μια διαδικασία κατανόησης της ερώτησης ενός χρήστη για την ανάκτηση συγκεκριμένων πληροφοριών. Η ροή περιλαμβάνει τρία βασικά βήματα:


  1. Ένας χρήστης κάνει μια ερώτηση σχετικά με τις συχνές παρενέργειες του φαρμάκου Lyrica για άτομα κάτω των 35 ετών.


  2. Το LLM αποφασίζει ποια συνάρτηση θα καλέσει και τις παραμέτρους που απαιτούνται. Σε αυτό το παράδειγμα, επέλεξε μια συνάρτηση με το όνομα side_effects με παραμέτρους που περιλαμβάνουν το φάρμακο Lyrica και μέγιστη ηλικία 35 ετών.


  3. Η ταυτοποιημένη συνάρτηση και οι παράμετροι χρησιμοποιούνται για την ντετερμινιστική και δυναμική δημιουργία μιας δήλωσης ερωτήματος βάσης δεδομένων (Cypher) για την ανάκτηση σχετικών πληροφοριών.


Η υποστήριξη κλήσης λειτουργιών είναι ζωτικής σημασίας για προηγμένες περιπτώσεις χρήσης LLM, όπως να επιτρέπεται στα LLM να χρησιμοποιούν πολλαπλά retriever με βάση την πρόθεση του χρήστη ή να δημιουργούν ροές πολλαπλών παραγόντων. Έχω γράψει μερικά άρθρα χρησιμοποιώντας εμπορικά LLM με εγγενή υποστήριξη κλήσης λειτουργιών. Ωστόσο, θα χρησιμοποιήσουμε το Llama-3.1 που κυκλοφόρησε πρόσφατα, ένα ανώτερο LLM ανοιχτού κώδικα με εγγενή υποστήριξη κλήσης λειτουργιών.


Ο κωδικός είναι διαθέσιμος στο GitHub .

Ρύθμιση του γραφήματος γνώσης

Θα χρησιμοποιήσουμε το Neo4j, το οποίο είναι μια εγγενής βάση δεδομένων γραφημάτων, για να αποθηκεύσουμε τις πληροφορίες ανεπιθύμητων συμβάντων. Μπορείτε να ρυθμίσετε ένα δωρεάν έργο cloud Sandbox που συνοδεύεται από προσυμπληρωμένα FAERS ακολουθώντας αυτόν τον σύνδεσμο .


Το στιγμιότυπο της βάσης δεδομένων έχει ένα γράφημα με το ακόλουθο σχήμα.


Σχήμα γραφήματος ανεπιθύμητων συμβάντων — Εικόνα από συγγραφέα


Το σχήμα επικεντρώνεται στον κόμβο Case, ο οποίος συνδέει διάφορες πτυχές μιας αναφοράς ασφάλειας φαρμάκων, συμπεριλαμβανομένων των φαρμάκων που εμπλέκονται, των αντιδράσεων που βιώθηκαν, των αποτελεσμάτων και των συνταγογραφούμενων θεραπειών. Κάθε φάρμακο χαρακτηρίζεται από το αν είναι πρωτογενές, δευτερογενές, συγχορηγούμενο ή αλληλεπιδρά. Τα κρούσματα σχετίζονται επίσης με πληροφορίες σχετικά με τον κατασκευαστή, την ηλικιακή ομάδα του ασθενούς και την πηγή της αναφοράς. Αυτό το σχήμα επιτρέπει την παρακολούθηση και την ανάλυση των σχέσεων μεταξύ των φαρμάκων, των αντιδράσεων και των αποτελεσμάτων τους με δομημένο τρόπο.


Θα ξεκινήσουμε δημιουργώντας μια σύνδεση με τη βάση δεδομένων δημιουργώντας ένα αντικείμενο Neo4jGraph:


 os.environ["NEO4J_URI"] = "bolt://18.206.157.187:7687" os.environ["NEO4J_USERNAME"] = "neo4j" os.environ["NEO4J_PASSWORD"] = "elevation-reservist-thousands" graph = Neo4jGraph(refresh_schema=False)


Ρύθμιση του περιβάλλοντος LLM

Υπάρχουν πολλές επιλογές για τη φιλοξενία LLM ανοιχτού κώδικα όπως το Llama-3.1. Θα χρησιμοποιήσουμε τον κατάλογο NVIDIA API , ο οποίος παρέχει μικροϋπηρεσίες συμπερασμάτων NVIDIA NIM και υποστηρίζει την κλήση συναρτήσεων για μοντέλα Llama 3.1. Όταν δημιουργείτε έναν λογαριασμό, λαμβάνετε 1.000 διακριτικά, τα οποία είναι υπεραρκετά για να ακολουθήσετε. Θα χρειαστεί να δημιουργήσετε ένα κλειδί API και να το αντιγράψετε στο σημειωματάριο:


 os.environ["NVIDIA_API_KEY"] = "nvapi-" llm = ChatNVIDIA(model="meta/llama-3.1-70b-instruct")


Θα χρησιμοποιήσουμε το λάμα-3.1–70b επειδή η έκδοση 8b έχει κάποια προβλήματα με προαιρετικές παραμέτρους στους ορισμούς συναρτήσεων.


Το ωραίο με τις μικροϋπηρεσίες NVIDIA NIM είναι ότι μπορείτε εύκολα να τις φιλοξενήσετε τοπικά εάν έχετε προβλήματα ασφάλειας ή άλλες ανησυχίες, επομένως είναι εύκολα εναλλάξιμα και χρειάζεται μόνο να προσθέσετε μια παράμετρο URL στη διαμόρφωση του LLM:


 # connect to an local NIM running at localhost:8000, # specifying a specific model llm = ChatNVIDIA( base_url="http://localhost:8000/v1", model="meta/llama-3.1-70b-instruct" )

Ορισμός εργαλείου

Θα διαμορφώσουμε ένα μόνο εργαλείο με τέσσερις προαιρετικές παραμέτρους. Θα δημιουργήσουμε μια αντίστοιχη δήλωση Cypher με βάση αυτές τις παραμέτρους για να ανακτήσουμε τις σχετικές πληροφορίες από το γράφημα γνώσης. Το εργαλείο μας θα μπορεί να εντοπίσει τις πιο συχνές παρενέργειες με βάση το φάρμακο εισαγωγής, την ηλικία και τον κατασκευαστή του φαρμάκου.


 @tool def get_side_effects( drug: Optional[str] = Field( description="disease mentioned in the question. Return None if no mentioned." ), min_age: Optional[int] = Field( description="Minimum age of the patient. Return None if no mentioned." ), max_age: Optional[int] = Field( description="Maximum age of the patient. Return None if no mentioned." ), manufacturer: Optional[str] = Field( description="manufacturer of the drug. Return None if no mentioned." ), ): """Useful for when you need to find common side effects.""" params = {} filters = [] side_effects_base_query = """ MATCH (c:Case)-[:HAS_REACTION]->(r:Reaction), (c)-[:IS_PRIMARY_SUSPECT]->(d:Drug) """ if drug and isinstance(drug, str): candidate_drugs = [el["candidate"] for el in get_candidates(drug, "drug")] if not candidate_drugs: return "The mentioned drug was not found" filters.append("d.name IN $drugs") params["drugs"] = candidate_drugs if min_age and isinstance(min_age, int): filters.append("c.age > $min_age ") params["min_age"] = min_age if max_age and isinstance(max_age, int): filters.append("c.age < $max_age ") params["max_age"] = max_age if manufacturer and isinstance(manufacturer, str): candidate_manufacturers = [ el["candidate"] for el in get_candidates(manufacturer, "manufacturer") ] if not candidate_manufacturers: return "The mentioned manufacturer was not found" filters.append( "EXISTS {(c)<-[:REGISTERED]-(:Manufacturer {manufacturerName: $manufacturer})}" ) params["manufacturer"] = candidate_manufacturers[0] if filters: side_effects_base_query += " WHERE " side_effects_base_query += " AND ".join(filters) side_effects_base_query += """ RETURN d.name AS drug, r.description AS side_effect, count(*) AS count ORDER BY count DESC LIMIT 10 """ print(f"Using parameters: {params}") data = graph.query(side_effects_base_query, params=params) return data


Η συνάρτηση get_side_effects έχει σχεδιαστεί για να ανακτά κοινές παρενέργειες φαρμάκων από ένα γράφημα γνώσης χρησιμοποιώντας συγκεκριμένα κριτήρια αναζήτησης. Δέχεται προαιρετικές παραμέτρους για το όνομα του φαρμάκου, το εύρος ηλικίας του ασθενούς και τον κατασκευαστή του φαρμάκου για την προσαρμογή της αναζήτησης. Κάθε παράμετρος έχει μια περιγραφή που μεταβιβάζεται σε ένα LLM μαζί με την περιγραφή της συνάρτησης, επιτρέποντας στο LLM να κατανοήσει πώς να τα χρησιμοποιήσει. Στη συνέχεια, η συνάρτηση κατασκευάζει ένα δυναμικό ερώτημα Cypher με βάση τις παρεχόμενες εισόδους, εκτελεί αυτό το ερώτημα σε σχέση με το γράφημα γνώσης και επιστρέφει τα δεδομένα παρενεργειών που προκύπτουν.


Ας δοκιμάσουμε τη συνάρτηση:


 get_side_effects("lyrica") # Using parameters: {'drugs': ['LYRICA', 'LYRICA CR']} # [{'drug': 'LYRICA', 'side_effect': 'Pain', 'count': 32}, # {'drug': 'LYRICA', 'side_effect': 'Fall', 'count': 21}, # {'drug': 'LYRICA', 'side_effect': 'Intentional product use issue', 'count': 20}, # {'drug': 'LYRICA', 'side_effect': 'Insomnia', 'count': 19}, # ...


Το εργαλείο μας αντιστοίχισε αρχικά το φάρμακο Lyrica που αναφέρεται στην ερώτηση στις τιμές "['LYRICA", "LYRICA CR"]" στο γράφημα γνώσεων και, στη συνέχεια, εκτέλεσε μια αντίστοιχη δήλωση Cypher για να βρει τις πιο συχνές παρενέργειες.

Πράκτορας LLM που βασίζεται σε γραφήματα

Το μόνο που μένει να κάνετε είναι να διαμορφώσετε έναν παράγοντα LLM που μπορεί να χρησιμοποιήσει το καθορισμένο εργαλείο για να απαντήσει σε ερωτήσεις σχετικά με τις παρενέργειες του φαρμάκου.


Ροή δεδομένων αντιπροσώπου — Εικόνα από συγγραφέα


Η εικόνα απεικονίζει έναν χρήστη να αλληλεπιδρά με έναν παράγοντα Llama 3.1 για να ρωτήσει σχετικά με τις παρενέργειες του φαρμάκου. Ο πράκτορας έχει πρόσβαση σε ένα εργαλείο παρενεργειών που ανακτά πληροφορίες από ένα γράφημα γνώσης για να παρέχει στον χρήστη τα σχετικά δεδομένα.


Θα ξεκινήσουμε ορίζοντας το πρότυπο προτροπής:


 prompt = ChatPromptTemplate.from_messages( [ ( "system", "You are a helpful assistant that finds information about common side effects. " "If tools require follow up questions, " "make sure to ask the user for clarification. Make sure to include any " "available options that need to be clarified in the follow up questions " "Do only the things the user specifically requested. ", ), MessagesPlaceholder(variable_name="chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ] )


Το πρότυπο προτροπής περιλαμβάνει το μήνυμα συστήματος, το προαιρετικό ιστορικό συνομιλιών και την είσοδο χρήστη. Το agent_scratchpad είναι δεσμευμένο για το LLM, καθώς μερικές φορές χρειάζεται πολλά βήματα για να απαντήσει στην ερώτηση, όπως η εκτέλεση και η ανάκτηση πληροφοριών από εργαλεία.


Η βιβλιοθήκη LangChain καθιστά εύκολη την προσθήκη εργαλείων στο LLM χρησιμοποιώντας τη μέθοδο bind_tools:


 tools = [get_side_effects] llm_with_tools = llm.bind_tools(tools=tools) agent = ( { "input": lambda x: x["input"], "chat_history": lambda x: _format_chat_history(x["chat_history"]) if x.get("chat_history") else [], "agent_scratchpad": lambda x: format_to_openai_function_messages( x["intermediate_steps"] ), } | prompt | llm_with_tools | OpenAIFunctionsAgentOutputParser() ) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True).with_types( input_type=AgentInput, output_type=Output )


Ο πράκτορας επεξεργάζεται την είσοδο μέσω μετασχηματισμών και χειριστών που μορφοποιούν το ιστορικό συνομιλιών, εφαρμόζουν το LLM με τα δεσμευμένα εργαλεία και αναλύουν την έξοδο. Τέλος, ο πράκτορας έχει ρυθμιστεί με έναν εκτελεστή που διαχειρίζεται τη ροή εκτέλεσης, καθορίζει τους τύπους εισόδου και εξόδου και περιλαμβάνει ρυθμίσεις πολυγλωσσίας για λεπτομερή καταγραφή κατά την εκτέλεση.


Ας δοκιμάσουμε τον πράκτορα:


 agent_executor.invoke( { "input": "What are the most common side effects when using lyrica for people below 35 years old?" } )


Αποτελέσματα:


Εκτέλεση πράκτορα — Εικόνα από συγγραφέα


Το LLM εντόπισε ότι πρέπει να χρησιμοποιήσει τη συνάρτηση get_side_effects με κατάλληλα ορίσματα. Στη συνέχεια, η συνάρτηση δημιουργεί δυναμικά μια δήλωση Cypher, ανακτά τις σχετικές πληροφορίες και τις επιστρέφει στο LLM για να δημιουργήσει την τελική απάντηση.

Περίληψη

Οι δυνατότητες κλήσης συναρτήσεων είναι μια ισχυρή προσθήκη σε μοντέλα ανοιχτού κώδικα όπως το Llama 3.1, επιτρέποντας πιο δομημένες και ελεγχόμενες αλληλεπιδράσεις με εξωτερικές πηγές δεδομένων και εργαλεία. Πέρα από την απλή αναζήτηση μη δομημένων εγγράφων, οι πράκτορες που βασίζονται σε γραφήματα προσφέρουν συναρπαστικές δυνατότητες αλληλεπίδρασης με γραφήματα γνώσης και δομημένα δεδομένα. Η ευκολία φιλοξενίας αυτών των μοντέλων χρησιμοποιώντας πλατφόρμες όπως οι μικροϋπηρεσίες NVIDIA NIM τα καθιστά ολοένα και πιο προσβάσιμα.


Όπως πάντα, ο κώδικας είναι διαθέσιμος στο GitHub .


Για να μάθετε περισσότερα σχετικά με αυτό το θέμα, ελάτε μαζί μας στο NODES 2024 στις 7 Νοεμβρίου, το δωρεάν εικονικό συνέδριο προγραμματιστών μας για έξυπνες εφαρμογές, γραφήματα γνώσης και τεχνητή νοημοσύνη. Εγγραφείτε τώρα!