Apprenez à construire un agent d'IA pour la récupération, la recherche et la résumation de documents de recherche Apprenez à construire un agent d'IA pour la récupération, la recherche et la résumation de documents de recherche Pour les chercheurs, rester à jour avec les dernières découvertes est semblable à trouver une aiguille dans un pansement. Imaginez un assistant alimenté par l'IA qui non seulement récupère les documents les plus pertinents, mais résume également les informations clés et répond à vos questions spécifiques, tout cela en temps réel. Cet article se penche sur la construction d'un tel agent de recherche sur l'IA en utilisant les capacités d'emballage de documents complexes de Superlinked.En intégrant la pertinence sémantique et temporelle, nous éliminons la nécessité d'un réarrangement complexe, assurant une récupération efficace et précise des informations. Cet article se penche sur la construction d'un tel agent de recherche sur l'IA en utilisant les capacités d'emballage de documents complexes de Superlinked.En intégrant la pertinence sémantique et temporelle, nous éliminons la nécessité d'un réarrangement complexe, assurant une récupération efficace et précise des informations. TL;DR: Construisez un agent de recherche AI en temps réel à l'aide de la recherche vectorielle de Superlinked.Il saute les pipelines RAG complexes en intégrant et en interrogant directement les documents - rendant la recherche plus rapide, plus simple et plus intelligente. (Voulez-vous sauter directement sur le code?Regardez le code open source sur GitHub ici.Prêt à essayer la recherche sémantique pour votre propre cas d'utilisation d'agent?Nous sommes là pour vous aider.) Découvrez l’open source sur GitHub . ici ici ici We’re here to . help aide aide Cet article montre comment construire un système d'agent à l'aide d'un agent du noyau pour gérer les requêtes. Si vous souhaitez suivre et exécuter le code dans le navigateur, here’s the . collab . collab collab Où commencer à construire un système d'assistance à la recherche? Traditionnellement, la construction d'un tel système implique une complexité et un investissement considérable de ressources. Les systèmes de recherche récupèrent généralement un ensemble initial large de documents en fonction de la pertinence et appliquent ensuite un processus de réarrangement secondaire pour affiner et réorganiser les résultats. Alors que le réarrangement améliore la précision, il augmente considérablement la complexité computationnelle, la latence et l'excès en raison de la récupération de données étendue initialement requise. Superlinked aborde cette complexité en combinant des intégrations numériques et catégoriques structurées avec des intégrations de texte sémantique, fournissant des vecteurs multimodaux complets. Cette méthode améliore considérablement la précision de la recherche en préservant des informations Créer un système d’agent avec Superlinked Cet agent AI peut faire trois choses principales: Recherchez des documents de recherche par thème (par exemple, « informatique quantique ») et classez-les par pertinence et récente. Résumez les papiers : condensez les papiers récupérés en informations de taille de morceau. Questions de réponse : extraire les réponses directement des documents de recherche spécifiques basés sur des requêtes ciblées de l'utilisateur. Superlinked élimine la nécessité de méthodes de re-ranger en améliorant la pertinence de la recherche vectorielle. Superlinked's RecencySpace sera utilisé qui codera spécifiquement les métadonnées temporaires, priorisant les documents récents lors de la recherche et éliminant la nécessité de re-ranger coûteux. Par exemple, si deux papiers ont la même pertinence - celui qui est le plus récent se classera plus haut. Étape 1 : Configurer la boîte à outils %pip install superlinked Pour rendre les choses plus faciles et plus modulaires, j'ai créé une classe d'outils abstraits. import pandas as pd import superlinked.framework as sl from datetime import timedelta from sentence_transformers import SentenceTransformer from openai import OpenAI import os from abc import ABC, abstractmethod from typing import Any, Optional, Dict from tqdm import tqdm from google.colab import userdata # Abstract Tool Class class Tool(ABC): @abstractmethod def name(self) -> str: pass @abstractmethod def description(self) -> str: pass @abstractmethod def use(self, *args, **kwargs) -> Any: pass # Get API key from Google Colab secrets try: api_key = userdata.get('OPENAI_API_KEY') except KeyError: raise ValueError("OPENAI_API_KEY not found in user secrets. Please add it using Tools > User secrets.") # Initialize OpenAI Client api_key = os.environ.get("OPENAI_API_KEY", "your-openai-key") # Replace with your OpenAI API key if not api_key: raise ValueError("Please set the OPENAI_API_KEY environment variable.") client = OpenAI(api_key=api_key) model = "gpt-4" Étape 2 : Comprendre le dataset Cet exemple utilise un ensemble de données contenant environ 10 000 articles de recherche sur l'IA disponibles sur Pour le rendre plus facile, il suffit d'exécuter la cellule ci-dessous, et il téléchargera automatiquement le ensemble de données dans votre répertoire de travail. Vous pouvez également utiliser vos propres sources de données, telles que des documents de recherche ou d'autres contenus académiques. Kagué import pandas as pd !wget --no-check-certificate 'https://drive.google.com/uc?export=download&id=1FCR3TW5yLjGhEmm-Uclw0_5PWVEaLk1j' -O arxiv_ai_data.csv Pour l'instant, pour rendre les choses un peu plus rapides, nous allons utiliser un sous-ensemble plus petit des papiers juste pour accélérer les choses, mais ne manquez pas d'essayer l'exemple en utilisant l'ensemble complet des données. Un détail technique important ici est que les timestamps de l'ensemble des données seront convertis des timestamps de chaîne (comme '1993-08-01 00:00:00+00:00') en objets datetime pandas. Cette conversion est nécessaire car elle nous permet d'effectuer des opérations date/heure. df = pd.read_csv('arxiv_ai_data.csv').head(100) # Convert to datetime but keep it as datetime (more readable and usable) df['published'] = pd.to_datetime(df['published']) # Ensure summary is a string df['summary'] = df['summary'].astype(str) # Add 'text' column for similarity search df['text'] = df['title'] + " " + df['summary'] Debug: Columns in original DataFrame: ['authors', 'categories', 'comment', 'doi', 'entry_id', 'journal_ref' 'pdf_url', 'primary_category', 'published', 'summary', 'title', 'updated'] Comprendre les colonnes de dataset Voici un bref aperçu des colonnes clés de notre ensemble de données, qui seront importantes dans les prochaines étapes: Date de publication : date de publication du document de recherche. Résumé : Le résumé de l’article, fournissant un aperçu concis. entry_id : l'identifiant unique pour chaque papier d'arXiv. Pour cette démonstration, nous nous concentrons spécifiquement sur quatre colonnes : , à , à et Pour optimiser la qualité de la recherche, le titre et le résumé sont combinés en une seule colonne de texte complète, qui forme le noyau de notre processus d'intégration et de recherche. entry_id published title summary Note sur l'indicateur In-Memory de Superlinked : l'indexation in-memory de Superlinked stocke nos ensembles de données directement dans la mémoire RAM, ce qui rend la récupération exceptionnellement rapide, ce qui est idéal pour les recherches en temps réel et le prototypage rapide. Étape 3 : Définir le schéma superliqué Pour avancer, il faut un schéma pour cartographier nos données. Avec des champs clés : PaperSchema lass PaperSchema(sl.Schema): text: sl.String published: sl.Timestamp # This will handle datetime objects properly entry_id: sl.IdField title: sl.String summary: sl.String paper = PaperSchema() Définir des espaces superliés pour une récupération efficace Une étape essentielle pour organiser et interroger efficacement notre ensemble de données consiste à définir deux espaces vectoriels spécialisés : TextSimilaritySpace et RecencySpace. Espace textile Le est conçu pour coder des informations textuelles, telles que les titres et abstracts de documents de recherche en vecteurs.En convertissant le texte en embeddings, cet espace améliore considérablement la facilité et la précision des recherches sémantiques.Il est spécifiquement optimisé pour gérer efficacement les séquences de texte plus longues, permettant des comparaisons précises de similitude entre documents. TextSimilaritySpace text_space = sl.TextSimilaritySpace( text=sl.chunk(paper.text, chunk_size=200, chunk_overlap=50), model="sentence-transformers/all-mpnet-base-v2" ) Recentissage Le capture des métadonnées temporaires, mettant l'accent sur la réalité des publications de recherche. En codant les timestamps, cet espace attribue une plus grande signification aux documents plus récents. En conséquence, les résultats de recherche équilibrent naturellement la pertinence du contenu avec les dates de publication, favorisant les informations récentes. RecencySpace recency_space = sl.RecencySpace( timestamp=paper.published, period_time_list=[ sl.PeriodTime(timedelta(days=365)), # papers within 1 year sl.PeriodTime(timedelta(days=2*365)), # papers within 2 years sl.PeriodTime(timedelta(days=3*365)), # papers within 3 years ], negative_filter=-0.25 ) Pensez à RecencySpace comme un filtre basé sur le temps, semblable à trier vos e-mails par date ou à regarder les messages Instagram avec les derniers en premier. Des délais plus petits (comme 365 jours) permettent des classements plus granulaires et annuels basés sur le temps. Des délais plus grands (comme 1095 jours) créent des périodes de temps plus larges. Le Pour l'expliquer plus clairement, considérez l'exemple suivant où deux articles ont la même pertinence de contenu, mais leur classement dépendra de leur date de publication. negative_filter Paper A: Published in 1996 Paper B: Published in 1993 Scoring example: - Text similarity score: Both papers get 0.8 - Recency score: - Paper A: Receives the full recency boost (1.0) - Paper B: Gets penalized (-0.25 due to negative_filter) Final combined scores: - Paper A: Higher final rank - Paper B: Lower final rank Ces espaces sont essentiels pour rendre le groupe de données plus accessible et efficace. Ils permettent à la fois des recherches basées sur le contenu et sur le temps, et sont vraiment utiles pour comprendre la pertinence et la récente des travaux de recherche. Cela fournit un moyen puissant d'organiser et de rechercher à travers le groupe de données en fonction du contenu et du temps de publication. Étape 4 : Créer l’index Ensuite, les espaces sont fusionnés dans un index qui est le noyau du moteur de recherche: paper_index = sl.Index([text_space, recency_space]) Ensuite, le DataFrame est cartographié dans le schéma et chargé en lots (10 papiers à la fois) dans un entrepôt en mémoire : # Parser to map DataFrame columns to schema fields parser = sl.DataFrameParser( paper, mapping={ paper.entry_id: "entry_id", paper.published: "published", paper.text: "text", paper.title: "title", paper.summary: "summary", } ) # Set up in-memory source and executor source = sl.InMemorySource(paper, parser=parser) executor = sl.InMemoryExecutor(sources=[source], indices=[paper_index]) app = executor.run() # Load the DataFrame with a progress bar using batches batch_size = 10 data_batches = [df[i:i + batch_size] for i in range(0, len(df), batch_size)] for batch in tqdm(data_batches, total=len(data_batches), desc="Loading Data into Source"): source.put([batch]) L'exécuteur en mémoire est la raison pour laquelle Superlinked brille ici - 1 000 papiers s'intègrent facilement dans la RAM, et les requêtes volent sans barrières d'entrée/sortie du disque. Étape 5 : Créer la requête Ensuite, la création de la requête. C'est là que le modèle pour la création de requêtes est créé. Pour gérer cela, nous avons besoin d'un modèle de requête qui peut équilibrer la pertinence et la récente. Voici à quoi cela ressemblerait: # Define the query knowledgebase_query = ( sl.Query( paper_index, weights={ text_space: sl.Param("relevance_weight"), recency_space: sl.Param("recency_weight"), } ) .find(paper) .similar(text_space, sl.Param("search_query")) .select(paper.entry_id, paper.published, paper.text, paper.title, paper.summary) .limit(sl.Param("limit")) ) Cela nous permet de choisir si nous devons privilégier le contenu (relevance_weight) ou le récent (recency_weight) - un combo très utile pour les besoins de notre agent. Étape 6 : Construire des outils Maintenant vient la partie d’outillage. Nous allons créer trois outils... Outil de récupération : Cet outil est créé en connectant l’index de Superlinked, lui permettant de tirer les 5 papiers les plus importants en fonction d’une requête. Il équilibre la pertinence (1.0 poids) et la récente (0,5 poids) pour atteindre l’objectif de « trouver des papiers ». Ce que nous voulons est de trouver les papiers qui sont pertinents pour la requête. Donc, si la requête est : « Quels papiers de calcul quantique ont été publiés entre 1993 et 1994 ? », alors l’outil de récupération récupérera ces papiers, les résumera un par un, et retournera les résultats. class RetrievalTool(Tool): def __init__(self, df, app, knowledgebase_query, client, model): self.df = df self.app = app self.knowledgebase_query = knowledgebase_query self.client = client self.model = model def name(self) -> str: return "RetrievalTool" def description(self) -> str: return "Retrieves a list of relevant papers based on a query using Superlinked." def use(self, query: str) -> pd.DataFrame: result = self.app.query( self.knowledgebase_query, relevance_weight=1.0, recency_weight=0.5, search_query=query, limit=5 ) df_result = sl.PandasConverter.to_pandas(result) # Ensure summary is a string if 'summary' in df_result.columns: df_result['summary'] = df_result['summary'].astype(str) else: print("Warning: 'summary' column not found in retrieved DataFrame.") return df_result Le prochain est le Cet outil est conçu pour les cas où un résumé concis d'un papier est nécessaire. , qui est l'identifiant du papier qui doit être résumé. n'est pas fourni, l'outil ne fonctionnera pas car ces ID sont une exigence pour trouver les papiers correspondants dans le groupe de données. Summarization Tool paper_id paper_id class SummarizationTool(Tool): def __init__(self, df, client, model): self.df = df self.client = client self.model = model def name(self) -> str: return "SummarizationTool" def description(self) -> str: return "Generates a concise summary of specified papers using an LLM." def use(self, query: str, paper_ids: list) -> str: papers = self.df[self.df['entry_id'].isin(paper_ids)] if papers.empty: return "No papers found with the given IDs." summaries = papers['summary'].tolist() summary_str = "\n\n".join(summaries) prompt = f""" Summarize the following paper summaries:\n\n{summary_str}\n\nProvide a concise summary. """ response = self.client.chat.completions.create( model=self.model, messages=[{"role": "user", "content": prompt}], temperature=0.7, max_tokens=500 ) return response.choices[0].message.content.strip() Enfin, nous avons le Cet outil chaîne les pour récupérer les documents pertinents, puis les utiliser pour répondre aux questions.Si aucun document pertinent n'est trouvé pour répondre aux questions, il fournira une réponse basée sur les connaissances générales QuestionAnsweringTool RetrievalTool class QuestionAnsweringTool(Tool): def __init__(self, retrieval_tool, client, model): self.retrieval_tool = retrieval_tool self.client = client self.model = model def name(self) -> str: return "QuestionAnsweringTool" def description(self) -> str: return "Answers questions about research topics using retrieved paper summaries or general knowledge if no specific context is available." def use(self, query: str) -> str: df_result = self.retrieval_tool.use(query) if 'summary' not in df_result.columns: # Tag as a general question if summary is missing prompt = f""" You are a knowledgeable research assistant. This is a general question tagged as [GENERAL]. Answer based on your broad knowledge, not limited to specific paper summaries. If you don't know the answer, provide a brief explanation of why. User's question: {query} """ else: # Use paper summaries for specific context contexts = df_result['summary'].tolist() context_str = "\n\n".join(contexts) prompt = f""" You are a research assistant. Use the following paper summaries to answer the user's question. If you don't know the answer based on the summaries, say 'I don't know.' Paper summaries: {context_str} User's question: {query} """ response = self.client.chat.completions.create( model=self.model, messages=[{"role": "user", "content": prompt}], temperature=0.7, max_tokens=500 ) return response.choices[0].message.content.strip() Étape 7 : Construire l’agent du noyau Ensuite, c'est l'agent du noyau. Il fonctionne comme le contrôleur central, assurant un fonctionnement fluide et efficace. Agissant comme le composant central du système, l'agent du noyau coordonne la communication en routageant les requêtes selon leur intention lorsque plusieurs agents opèrent simultanément. Dans les systèmes d'agent unique, comme celui-ci, l'agent du noyau utilise directement les outils pertinents pour gérer efficacement les tâches. class KernelAgent: def __init__(self, retrieval_tool: RetrievalTool, summarization_tool: SummarizationTool, question_answering_tool: QuestionAnsweringTool, client, model): self.retrieval_tool = retrieval_tool self.summarization_tool = summarization_tool self.question_answering_tool = question_answering_tool self.client = client self.model = model def classify_query(self, query: str) -> str: prompt = f""" Classify the following user prompt into one of the three categories: - retrieval: The user wants to find a list of papers based on some criteria (e.g., 'Find papers on AI ethics from 2020'). - summarization: The user wants to summarize a list of papers (e.g., 'Summarize papers with entry_id 123, 456, 789'). - question_answering: The user wants to ask a question about research topics and get an answer (e.g., 'What is the latest development in AI ethics?'). User prompt: {query} Respond with only the category name (retrieval, summarization, question_answering). If unsure, respond with 'unknown'. """ response = self.client.chat.completions.create( model=self.model, messages=[{"role": "user", "content": prompt}], temperature=0.7, max_tokens=10 ) classification = response.choices[0].message.content.strip().lower() print(f"Query type: {classification}") return classification def process_query(self, query: str, params: Optional[Dict] = None) -> str: query_type = self.classify_query(query) if query_type == 'retrieval': df_result = self.retrieval_tool.use(query) response = "Here are the top papers:\n" for i, row in df_result.iterrows(): # Ensure summary is a string and handle empty cases summary = str(row['summary']) if pd.notna(row['summary']) else "" response += f"{i+1}. {row['title']} \nSummary: {summary[:200]}...\n\n" return response elif query_type == 'summarization': if not params or 'paper_ids' not in params: return "Error: Summarization query requires a 'paper_ids' parameter with a list of entry_ids." return self.summarization_tool.use(query, params['paper_ids']) elif query_type == 'question_answering': return self.question_answering_tool.use(query) else: return "Error: Unable to classify query as 'retrieval', 'summarization', or 'question_answering'." À ce stade, tous les composants du système d'agent de recherche ont été configurés.Le système peut désormais être initialisé en fournissant à l'agent du noyau les outils appropriés, après quoi le système d'agent de recherche sera pleinement opérationnel. retrieval_tool = RetrievalTool(df, app, knowledgebase_query, client, model) summarization_tool = SummarizationTool(df, client, model) question_answering_tool = QuestionAnsweringTool(retrieval_tool, client, model) # Initialize KernelAgent kernel_agent = KernelAgent(retrieval_tool, summarization_tool, question_answering_tool, client, model) Maintenant, testons le système... # Test query print(kernel_agent.process_query("Find papers on quantum computing in last 10 years")) Cette fonctionnalité active le Il récupérera les documents pertinents en fonction de la pertinence et de la récente, et renverra les colonnes pertinentes.Si le résultat retourné inclut la colonne de résumé (indicant que les documents ont été récupérés à partir de l'ensemble de données), il utilisera ces résumés et nous les renverra. RetrievalTool Query type: retrieval Here are the top papers: 1. Quantum Computing and Phase Transitions in Combinatorial Search Summary: We introduce an algorithm for combinatorial search on quantum computers that is capable of significantly concentrating amplitude into solutions for some NP search problems, on average. This is done by... 1. The Road to Quantum Artificial Intelligence Summary: This paper overviews the basic principles and recent advances in the emerging field of Quantum Computation (QC), highlighting its potential application to Artificial Intelligence (AI). The paper provi... 1. Solving Highly Constrained Search Problems with Quantum Computers Summary: A previously developed quantum search algorithm for solving 1-SAT problems in a single step is generalized to apply to a range of highly constrained k-SAT problems. We identify a bound on the number o... 1. The model of quantum evolution Summary: This paper has been withdrawn by the author due to extremely unscientific errors.... 1. Artificial and Biological Intelligence Summary: This article considers evidence from physical and biological sciences to show machines are deficient compared to biological systems at incorporating intelligence. Machines fall short on two counts: fi... Essayons une autre requête, cette fois, faisons un résumé. print(kernel_agent.process_query("Summarize this paper", params={"paper_ids": ["http://arxiv.org/abs/cs/9311101v1"]})) Query type: summarization This paper discusses the challenges of learning logic programs that contain the cut predicate (!). Traditional learning methods cannot handle clauses with cut because it has a procedural meaning. The proposed approach is to first generate a candidate base program that covers positive examples, and then make it consistent by inserting cut where needed. Learning programs with cut is difficult due to the need for intensional evaluation, and current induction techniques may need to be limited to purely declarative logic languages. J'espère que cet exemple a été utile pour le développement d'agents d'IA et de systèmes basés sur des agents. Une grande partie de la fonctionnalité de récupération démontrée ici a été rendue possible par Superlinked, alors veuillez envisager de jouer le rôle principal pour une référence future lorsque des capacités de récupération précises sont nécessaires pour vos agents d’IA ! Réservatoire Les Takeaways Notebook du code La combinaison de la pertinence sémantique et temporelle élimine le redémarrage complexe tout en conservant la précision de la recherche pour les documents de recherche. Les pénalités basées sur le temps (negative_filter=-0.25) donnent la priorité aux recherches récentes lorsque les articles ont une pertinence de contenu similaire. L'architecture modulaire basée sur des outils permet aux composants spécialisés de gérer des tâches distinctes (récupération, résumation, réponse aux questions) tout en maintenant la cohésion du système. Le chargement des données en petits lots (batch_size=10) avec le suivi des progrès améliore la stabilité du système lors du traitement de grands ensembles de données de recherche. Les poids de requête ajustables permettent aux utilisateurs d'équilibrer la pertinence (1.0) et la récente (0.5) en fonction des besoins de recherche spécifiques. La composante de réponse aux questions se dégrade gracieusement en connaissances générales lorsque le contexte spécifique au papier n’est pas disponible, empêchant ainsi les expériences d’utilisateur inédites. Rester à jour avec le grand nombre d'articles de recherche publiés régulièrement peut être difficile et coûteux. Un flux de travail d'assistant de l'IA agent capable de localiser efficacement la recherche pertinente, de résumer les informations clés et de répondre à des questions spécifiques de ces articles pourrait considérablement rationaliser ce processus. contributeurs Vipul Maheshwari, auteur Filip Makraduli, rédacteur