Steam ライブラリを検索するよりスマートで高速な方法 あなたはその感覚を知っています。 あなたは、戦略的な、コオペ、たぶん、サイエンスフィーのテーマでゲームを探している. あなたは、その種のマッチのタイトルの壁を得る. あなたが望んだのは、本当にあなたの言葉の背後にある雰囲気をキャプチャするショートリストでした. このガイドでは、私たちは、LlamaIndexとスーパーリンクを組み合わせることによって、それを正確に構築する方法を示します. その結果は、ジャンルと説明とタグを理解し、ミリ秒で答えを提供するカスタマイズされたSteamゲームリレーバーです。 ♪♪♪ 実際のクエリと遅延番号でデータにこれを見たいですか? 連絡を取ってください。 . Get in touch タッチにしろ タッチにしろ トップ > DR Custom retrievers は、ドメイン コンテキスト、メタデータ、ランキング ロジックの制御を提供します。They outperform generic similarity search when queries are messy or jargon heavy.Custom retrievers give you control over domain context, metadata, and ranking logic. They outperform generic similarity search when queries are messy or jargon heavy. Superlinked は複数のテキスト フィールドを 1 つのセマンティック スペースに統合し、メモリでクエリを実行し、素晴らしい結果を得ます。 LlamaIndex はクリーン リトリバー インターフェイスを提供し、クエリ エンジンと応答合成に直接接続します。 あなたが輸入して使用できるLlamaIndexの公式のSuperlinked Retriever統合があります。 オフィシャル Superlinked Retriever for LlamaIndex Superlinkedは公式のLlamaIndexを介して統合します。 LlamaHub にリストされているので、簡単なインストールと、既存の LlamaIndex スタックに Superlinked を追加できます。 その後、Aに組み込む。 . Learn more on the クラスとコンビニターのパラメータは、LlamaIndex API 参照に記述されています。 SuperlinkedRetriever from llama_index.retrievers.superlinked import SuperlinkedRetriever RetrieverQueryEngine . 統合公式ページ . 統合公式ページ 統合公式ページ pip install llama-index-retrievers-superlinked from llama_index.retrievers.superlinked import SuperlinkedRetriever # sl_app: a running Superlinked App # query_descriptor: a Superlinked QueryDescriptor that describes your query plan retriever = SuperlinkedRetriever( sl_client=sl_app, sl_query=query_descriptor, page_content_field="text", query_text_param="query_text", metadata_fields=None, top_k=10, ) nodes = retriever.retrieve("strategic co-op sci fi game") 手で作るか、論理をさらにカスタマイズするか? 続きを読む Why Superlinked + LlamaIndex? なぜ Superlinked + LlamaIndex? 目標は単純です: Superlinked の強みを複数のフィールドの検索に取り込んで、開発者がリアルな RAG システムで採用し、拡張できるようにパッケージします。 Superlinked は、名前、説明、ジャンルなどのフィールドを単一のセマンティックなビューに組み合わせる表現的なベクトルスペースとクエリを定義するのに役立ちます。 You can also follow along in 同様に、 Superlinked ノートブックから同じビルドブロックを使用します。 Googleの コラボ Googleの コラボ コラボ なぜカスタムリトリバーは重要なのか あなたのドメインのために調節 - 一般的なリトリバーは一般的な用途に適していますが、それらは微妙なものを逃す傾向があります。ジャーゴン、短編、またはドメイン特有のフラッシュについて考えてみてください、あなたのリトリバーが何を見つけるべきかを知らない限り、これらは通常取り上げられません。 テキスト以外の機能 - ほとんどの現実世界のデータは単純なテキストではありません。あなたはしばしばメタデータとタグを持っています。 たとえば、ゲーム勧告システムでは、我々はゲームの説明だけを気にしません。 我々はまた、ジャンル、タグ、ユーザーの評価、その他に要因を考慮したいです。 この論理について考える:誰かが「Sci-fi要素を持つ戦略コオペゲーム」を探していると、テキストのみのマッチングは遠くにないでしょう。 カスタマイズされたフィルタリングとランキングの論理 - 時には、物事が得点またはフィルタリングされる方法に独自のルールを適用したい場合があります。 もしかしたら、新しいコンテンツを優先するか、特定の品質の限界を満たさない結果を罰したいかもしれません。 パフォーマンスの向上 – 現実のものにしよう:一般的なソリューションは、すべての人にとって「OK」で動作するように構築されていますが、あなたにとっては素晴らしいものではありません。 データとアクセスパターンを知っていると、リトリバーをより速く実行し、より良いランクアップを実行し、結果の不必要な騒音を減らすことができます。 実施分断 第1部:コア依存と輸入 import time import logging import pandas as pd from typing import List from llama_index.core.retrievers import BaseRetriever from llama_index.core.schema import NodeWithScore, QueryBundle, TextNode from llama_index.core.query_engine import RetrieverQueryEngine from llama_index.core.response_synthesizers import get_response_synthesizer from llama_index.core import Settings from llama_index.llms.openai import OpenAI import superlinked.framework as sl 輸入構造は、私たちのハイブリッドアプローチを明らかにします: LlamaIndex Core: Retrieval Abstraction Layer を提供します。 Superlinked Framework: Handles vector computation and semantic search (スーパーリンクされたフレームワーク) Pandas: Manages data preprocessing and manipulation データの前処理と操作 Part 2: LlamaIndex Custom Retrieversの理解 Superlinked を実装する前に、LlamaIndex のカスタマイズされたリトリバーアーキテクチャがどのように機能するか、なぜドメイン特定の RAG アプリケーションを構築するのに強力であるかを理解することが重要です。 BaseRetriever 抽象化 LlamaIndex は、抽象を提供します。 このデザインの美しさはそのシンプルさにあり、すべてのカスタマイズされたリトリバーは1つのコアメソッドのみを実装する必要があります。 BaseRetriever from abc import abstractmethod from llama_index.core.retrievers import BaseRetriever from llama_index.core.schema import NodeWithScore, QueryBundle class BaseRetriever: @abstractmethod def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]: """Retrieve nodes given query.""" pass ここで重要なのは、LlamaIndexからのリトリバル・プロトコルの存在です。この「リトリバル・プロトコル」は、システムの残りの部分に触れることなく、さまざまなバックエンドや戦略を簡単に接続できます。 Input: QueryBundle This is the query object passed into your retriever. At minimum, it contains the user's raw query string (e.g., "sci-fi strategy games"). But it can also include extra metadata like filters, embeddings, or user preferences. Basically, anything that might help shape a more relevant response. Output: List[NodeWithScore] The retriever returns a list of nodes—these are your chunks of content, documents, or data entries—each paired with a relevance score. The higher the score, the more relevant the node is to the query. This list is what gets passed downstream to the LLM or other post-processing steps. As in our case, we are plugging on the Processing: Backend-Agnostic Here’s the cool part: how you get from query to result is totally up to you. You can use a vector database, a traditional search engine, a REST API, or even something handcrafted for your specific use case. This decouples logic and gives you full control over the retrieval stack. なぜこれが重要なのか。 この抽象化は清潔で、 それはあなたができることを意味します: パワフル 複数の戦略を組み合わせる - 必要に応じて密集ベクター検索とキーワードフィルタリングを使用します。 A / B テストを簡単に実行する - 異なるリトリバーを比較して、ユーザーのためのより良い結果を提供するものを見てください。 任意のエージェントやツールに接続 - チャットボット、検索UI、または完全なエージェントシステムを構築しているかどうかにかかわらず、このリトリバーインターフェイスは簡単に利用できます。 リハビリプロトコルを「リハビリ脳」とその他のすべてのものとの間のAPI契約として考えてみてください. Once you follow it, you are free to innovate whatever you want behind the scenes. Plugging Superlinked into LlamaIndex について まあ、それで、その クラスは基本的に、スマートで意味のあるゲーム勧告を提供するための私たちのツールです. We will start with a quick look at how it is put together, and then later dive deeper into each part to really see what makes this thing tic. 僕たちは、それをどのように組み合わせたかをすぐに見つけることから始めます。 SuperlinkedSteamGamesRetriever まず上は、The , think of it as the foundation. It’s what keeps everything organized and reliable. Superlinked’s’s , we lay out key details like で、 で、 そして、 これにより、すべてのゲーム情報は清潔で一貫して保たれ、スーパーリンクのパイプラインに直接接続され、すべてがスムーズに流れていきます。 schema definition GameSchema game_number name desc_snippet genre class GameSchema(sl.Schema): game_number: sl.IdField name: sl.String desc_snippet: sl.String game_details: sl.String languages: sl.String genre: sl.String game_description: sl.String original_price: sl.Float discount_price: sl.Float combined_text: sl.String # New field for combined text self.game = GameSchema() 次に上がるのは、 これが、セマンティック検索の魔法が起こる場所です。 モデルは、ゲーム情報(名前、記述、ジャンルなど)を密集したベクトル表示に変換します。基本的に、モデルが理解できるものにそのテキストをすべて溶解します。 だから、誰かが「オープンワールド・アドベンチャー」のようなものを探していると、その雰囲気に実際に合うゲームを見つけることができます。 text similarity space sentence-transformers/all-mpnet-base-v2 意味 self.text_space = sl.TextSimilaritySpace( text=self.game.combined_text, model="sentence-transformers/all-mpnet-base-v2" ) THE それは、ゲームの名前、説明、ジャンル、およびそれ以上のような異なる情報のビットを取って、テキストの1つの大きな部分にそれらを打ち砕きます。これは、ベクトルに変換すると、モデルにそれぞれのゲームのより完全なイメージを与えます。結果は? より良い推奨方法は、単独で単一のものを見る代わりに、すべての異なる詳細を一斉に集めているからです。 combined text field self.df['combined_text'] = ( self.df['name'].astype(str) + " " + self.df['desc_snippet'].astype(str) + " " + self.df['genre'].astype(str) + " " + self.df['game_details'].astype(str) + " " + self.df['game_description'].astype(str) ) そして最後に、 スーパーリンクのおかげで、スーパーリンクのおかげです。 リトリバーは、リクエストをリアルタイムで処理することができ、遅延はなく、即座の結果だけです。それは、誰かが特定のジャンルのための狩りをしているか、または遊ぶために新しい何かを探しているかどうか、彼らは待つことなく迅速かつ正確な推奨を得ることを意味します。 in-memory execution InMemoryExecutor # Set up in-memory source and executor source = sl.InMemorySource(self.game, parser=parser) self.executor = sl.InMemoryExecutor(sources=[source], indices=[self.index]) self.app = self.executor.run() # Load data source.put([self.df]) All these pieces together, and you have the — ユーザーにとって実際には意味のあるゲームの推奨を提供するための堅固なセットアップ. それは速く、スマートで、個人的です. Here's what the whole thing looks like in action... SuperlinkedSteamGamesRetriever class SuperlinkedSteamGamesRetriever(BaseRetriever): """A custom LlamaIndex retriever using Superlinked for Steam games data.""" def __init__(self, csv_file: str, top_k: int = 10): """ Initialize the retriever with a CSV file path and top_k parameter. Args: csv_file (str): Path to games_data.csv top_k (int): Number of results to return (default: 10) """ self.top_k = top_k # Load the dataset and ensure all required columns are present self.df = pd.read_csv(csv_file) print(f"Loaded dataset with {len(self.df)} games") print("DataFrame Columns:", list(self.df.columns)) required_columns = [ 'game_number', 'name', 'desc_snippet', 'game_details', 'languages', 'genre', 'game_description', 'original_price', 'discount_price' ] for col in required_columns: if col not in self.df.columns: raise ValueError(f"Missing required column: {col}") # Combine relevant columns into a single field for text similarity self.df['combined_text'] = ( self.df['name'].astype(str) + " " + self.df['desc_snippet'].astype(str) + " " + self.df['genre'].astype(str) + " " + self.df['game_details'].astype(str) + " " + self.df['game_description'].astype(str) ) self._setup_superlinked() def _setup_superlinked(self): """Set up Superlinked schema, space, index, and executor.""" # Define schema class GameSchema(sl.Schema): game_number: sl.IdField name: sl.String desc_snippet: sl.String game_details: sl.String languages: sl.String genre: sl.String game_description: sl.String original_price: sl.Float discount_price: sl.Float combined_text: sl.String # New field for combined text self.game = GameSchema() # Create text similarity space using the combined_text field self.text_space = sl.TextSimilaritySpace( text=self.game.combined_text, model="sentence-transformers/all-mpnet-base-v2" ) # Create index self.index = sl.Index([self.text_space]) # Map DataFrame columns to schema parser = sl.DataFrameParser( self.game, mapping={ self.game.game_number: "game_number", self.game.name: "name", self.game.desc_snippet: "desc_snippet", self.game.game_details: "game_details", self.game.languages: "languages", self.game.genre: "genre", self.game.game_description: "game_description", self.game.original_price: "original_price", self.game.discount_price: "discount_price", self.game.combined_text: "combined_text" } ) # Set up in-memory source and executor source = sl.InMemorySource(self.game, parser=parser) self.executor = sl.InMemoryExecutor(sources=[source], indices=[self.index]) self.app = self.executor.run() # Load data source.put([self.df]) print(f"Initialized Superlinked retriever with {len(self.df)} games") def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]: """ Retrieve top-k games based on the query string. Args: query_bundle (QueryBundle): Contains the query string Returns: List[NodeWithScore]: List of retrieved games with scores """ query_text = query_bundle.query_str # Define Superlinked query with explicit field selection query = ( sl.Query(self.index) .find(self.game) .similar(self.text_space, query_text) .select([ self.game.game_number, self.game.name, self.game.desc_snippet, self.game.game_details, self.game.languages, self.game.genre, self.game.game_description, self.game.original_price, self.game.discount_price ]) .limit(self.top_k) ) # Execute query result = self.app.query(query) df_result = sl.PandasConverter.to_pandas(result) # Convert results to NodeWithScore objects nodes_with_scores = [] for i, row in df_result.iterrows(): text = f"{row['name']}: {row['desc_snippet']}" metadata = { "game_number": row["id"], "name": row["name"], "desc_snippet": row["desc_snippet"], "game_details": row["game_details"], "languages": row["languages"], "genre": row["genre"], "game_description": row["game_description"], "original_price": row["original_price"], "discount_price": row["discount_price"] } score = 1.0 - (i / self.top_k) node = TextNode(text=text, metadata=metadata) nodes_with_scores.append(NodeWithScore(node=node, score=score)) return nodes_with_scores print("✅ SuperlinkedSteamGamesRetriever class defined successfully!") Integration Architecture Deep Dive Part 3: Superlinked Schema 定義と設定 スケジュールの設計から始まり、スーパーリンクでは、スケジュールはデータの種類を定義するだけでなく、データとベクトル計算エンジンの間の公式な定義のようなものです。 わたしたちの スケジュールは、このように定義されています: SuperlinkedSteamGamesRetriever class GameSchema(sl.Schema): game_number: sl.IdField name: sl.String desc_snippet: sl.String game_details: sl.String languages: sl.String genre: sl.String game_description: sl.String original_price: sl.Float discount_price: sl.Float combined_text: sl.String # New field for combined text self.game = GameSchema() これらの要素のいくつかを実際に分解してみましょう。 : やる (→ ) Think of this as our primary key. It gives each game a unique identity and allows Superlinked to index and retrieve items efficiently, I mean basically it’s about how we are telling the Superlinked to segregate the unique identify of the games, and btw it’s especially important when you're dealing with thousands of records. sl.IdField game_number and Now these aren't just type hints—they enable Superlinked to optimize operations differently depending on the field. For instance, fields can be embedded and compared semantically, while fields can support numeric filtering or sorting. sl.String sl.Float sl.String sl.Float This is the of our retriever. It’s a synthetic field where we concatenate the game name, description, genre, and other relevant attributes into a single block of text. This lets us build a single using sentence-transformer embeddings: combined_text semantic anchor text similarity space self.text_space = sl.TextSimilaritySpace( text=self.game.combined_text, model="sentence-transformers/all-mpnet-base-v2" ) なぜそうするのか? なぜなら、ユーザーはジャンルや名前で検索するだけでなく、自分が何者なのかを説明しているからです。 すべての重要な信号を組み込むことで、 , we can better match fuzzy, natural-language queries with the most relevant games. , we can better match fuzzy, natural-language queries with the most relevant games. , we can better match fuzzy, natural-language queries with the most relevant games. 探し方 combined_text Part 4: Vector Space Configuration(ベクトル空間構成) # Create text similarity space using the combined_text field self.text_space = sl.TextSimilaritySpace( text=self.game.combined_text, model="sentence-transformers/all-mpnet-base-v2" ) # Create index self.index = sl.Index([self.text_space]) Steam ゲームデータセットのセマンティックな検索を強化するために、私はパフォーマンス、シンプルさ、柔軟性のバランスをとる2つの意図的なデザイン選択を行いました。 まず、入力モデルとして、私は選択しました。 このモデルは、堅固な中間地盤を打つ768次元の埋め込みを生成します:それらは豊富なセマンティックな意味をキャプチャするのに十分に表現力がありますが、生産に迅速であるのに十分に軽量です。私は、それは信頼性の高い一般用途モデルであり、さまざまなテキストタイプでうまく機能することを知っています - あなたのデータが短いジャンルタグから長い形態のゲーム記述に至るまで非常に重要です。 きれいに取り扱った。 all-mpnet-base-v2 all-mpnet-base-v2 次に、スーパーリンクは複数のスペースのインデックスをサポートしますが、複数のフィールドや方法(テキスト + 画像など)を組み合わせることができます。 I would have included the ここにも入っていますが、私はゲームのリリース日に関する情報を持っていません。しかし、ここにこれを置くために、もし私たちがリリース日付の情報を持っているなら、私はここでRecencySpaceを接続することができます。 ゲームの最新情報と共に、カッコイイ。 TextSimilaritySpace RecencySpace TextSimilaritySpace Part 5: Data Pipeline and Executor Setup # Map DataFrame columns to schema - Critical for data integrity parser = sl.DataFrameParser( self.game, mapping={ self.game.game_number: "game_number", self.game.name: "name", self.game.desc_snippet: "desc_snippet", self.game.game_details: "game_details", self.game.languages: "languages", self.game.genre: "genre", self.game.game_description: "game_description", self.game.original_price: "original_price", self.game.discount_price: "discount_price", self.game.combined_text: "combined_text" } ) # Set up in-memory source and executor source = sl.InMemorySource(self.game, parser=parser) self.executor = sl.InMemoryExecutor(sources=[source], indices=[self.index]) self.app = self.executor.run() # Load data source.put([self.df]) print(f"Initialized Superlinked retriever with {len(self.df)} games") 私たちの回収システムの中心は、明確さとスピードの両方のために構築された簡素化されたパイプラインです。 それは、データセットの各フィールドが正しく入力され、一貫して当社のスケジュールにマッピングされていることを保証します; 基本的に、私たちの原始CSVデータとスーパーリンクされたインデックスレイヤーの間の契約として機能します。 DataFrameParser データが構造化されると、私はそれを一つの このアプローチは、ストレージオーバーヘッドやネットワーク遅延を導入することなく、すべてを閃光速で保持します。 これは、スーパーリンクをインタラクティブな推奨システムなどのリアルタイムアプリケーションに適したもので、スピードが直接ユーザー体験に影響を与える。 InMemorySource InMemoryExecutor Part 6: The Retrieval Engine(リトリバル・エンジン) def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]: """ Retrieve top-k games based on the query string. Args: query_bundle (QueryBundle): Contains the query string Returns: List[NodeWithScore]: List of retrieved games with scores """ query_text = query_bundle.query_str # Define Superlinked query with explicit field selection query = ( sl.Query(self.index) .find(self.game) .similar(self.text_space, query_text) .select([ self.game.game_number, self.game.name, self.game.desc_snippet, self.game.game_details, self.game.languages, self.game.genre, self.game.game_description, self.game.original_price, self.game.discount_price ]) .limit(self.top_k) ) # Execute query result = self.app.query(query) df_result = sl.PandasConverter.to_pandas(result) Superlinked が本当に楽しめるものの一つは、その流暢なスタイルのクエリ ビルダーです。 SQLAlchemy や Django ORM のようなライブラリを使用している場合は、パターンがよく知られています。チェーンの各メソッドは混乱の代わりに明確性を追加します。 この方法は、埋め込みスペースにおけるコシン類似性を計算するもので、ユーザーの自然言語のクエリに基づいてセマンティックに近いゲームを取得することを可能にします。 .similar() もう一つ考えたデザインの決断は、 I care about in the result set, rather than doing something like これは小さく聞こえるかもしれませんが、データのレイアウトを維持し、処理のオーバーヘッドを減らし、後処理中に不要なパイロードを回避しないようにします。 explicitly select the fields SELECT * 第7部:結果処理とノード作成 # Convert to LlamaIndex NodeWithScore format nodes_with_scores = [] for i, row in df_result.iterrows(): text = f"{row['name']}: {row['desc_snippet']}" metadata = { "game_number": row["id"], "name": row["name"], "desc_snippet": row["desc_snippet"], "game_details": row["game_details"], "languages": row["languages"], "genre": row["genre"], "game_description": row["game_description"], "original_price": row["original_price"], "discount_price": row["discount_price"] } # Simple ranking score based on result position score = 1.0 - (i / self.top_k) node = TextNode(text=text, metadata=metadata) nodes_with_scores.append(NodeWithScore(node=node, score=score)) return nodes_with_scores Superlinked から結果が届いたら、LlamaIndex でよく使える形式に変換しました。 これは、各ノードのコンテンツになり、言語モデルが推論しやすくなります。これは小さな触れですが、LLMに転送されたときに収集されたデータがどれほど関連性と理解性を向上させます。 human-readable text 次に、そのことを確実に ジャンル、価格、ゲームの詳細を含むデータセットのメタデータがメタデータに保存されているため、下流プロセスは、この情報に基づいて結果をフィルタリング、表示、またはランクアップしたいかもしれません。 all original fields 最後に、軽量化を行います。 戦略. 純粋な類似性のスコアに依存する代わりに、我々はランクされたリストの結果の位置に基づいてスコアを割り当てます. これは物事を単純かつ一貫しているようにします. トップの結果は常に最高のスコアを持っており、残りは下方順に従います. それは幻想的ではありませんが、それは異なるクエリでうまく機能する安定した解釈可能なスコアシステムを私たちに与えます. score normalisation Show Time: パイプラインの実行 すべてのコンポーネントが用意されている今、私たちのRetrieval-Augmented Generation(RAG)システムを生かす時が来ました。 # Initialize the RAG pipeline print("Setting up complete Retrieval pipeline...") # Create response synthesizer and query engine response_synthesizer = get_response_synthesizer() query_engine = RetrieverQueryEngine( retriever=retriever, response_synthesizer=response_synthesizer ) print("✅ RAG pipeline configured successfully!") print("\n" + "="*60) print("FULL RAG PIPELINE DEMONSTRATION") print("="*60) # Test queries with full RAG responses test_queries = [ "I want to find a magic game with spells and wizards", "Recommend a fun party game for friends", "I'm looking for a strategic sci-fi game", "What's a good cooperative game for teamwork?" ] for i, query in enumerate(test_queries, 1): print(f"\nQuery {i}: '{query}'") print("-" * 50) response = query_engine.query(query) print(f"Response: {response}") print("\n" + "="*50) このセットアップは、私たちのカスタマイズされたセマンティック・レトリバーとLLM駆動の応答ジェネレーターを組み合わせています。クエリはパイプラインを通じてスムーズに移動し、単に原始データを吐き出す代わりに、ユーザーが求めているものに基づいて実際にプレイしたいゲームの種類について考えた提案を返します。 Takeaways Takeaways Custom retrievers let you bake domain rules and jargon into the system. Combining multiple text fields into one index improves query understanding. In LlamaIndex you only need to implement _retrieve for a custom backend. Superlinked InMemoryExecutor gives real time latency on moderate datasets. Schema choice matters for clean parsing and mapping. Simple position based scoring is a stable default when you want predictable ranks.\ If you want a quick chat about where mixture of encoders or multi-field retrieval fits in your pipeline, ! talk to one of our engineers エンコーダーの組み合わせやマルチフィールドの検索があなたのパイプラインに合う場所について、すぐにチャットしたい場合は、 ! 私たちのエンジニアの1人と話す 私たちのエンジニアの1人と話す 私たちのエンジニアの1人と話す 統合参照: Superlinked retriever package on PyPI and LlamaIndex docs for custom retrievers. 貢献者 Vipul Maheshwari 著者 フィリップ・マクラドゥリ、編集者