Cognita: オープンソースでモジュール式のRAGアプリケーションを本番環境向けに構築する

Built for Speed: ~10ms Latency, Even Under Load
Blazingly fast way to build, track and deploy your models!
- Handles 350+ RPS on just 1 vCPU — no tuning needed
- Production-ready with full enterprise support
Assume there is a team A assigned to develop RAG application for use-case-1, then there is team B that is developing RAG application for use-case-2, and then there is team C, that is just planning out for their upcoming RAG application use case. Have you wished that building RAG pipelines across multiple teams should have been easy? Each team need not start from scratch but a modular way where each team can use the same base functionality and effectively develop their own apps on top of it without any interference?
Worry not!! This is why we created Cognita. While RAG is undeniably impressive, the process of creating a functional application with it can be daunting. There's a significant amount to grasp regarding implementation and development practices, ranging from selecting the appropriate AI models for the specific use case to organizing data effectively to obtain the desired insights. While tools like LangChain and LlamaIndex exist to simplify the prototype design process, there has yet to be an accessible, ready-to-use open-source RAG template that incorporates best practices and offers modular support, allowing anyone to quickly and easily utilize it.
Advantages of Cognita:
- A central reusable repository of parsers, loaders, embedders and retrievers.
- Ability for non-technical users to play with UI - Upload documents and perform QnA using modules built by the development team.
- Fully API driven - which allows integration with other systems.
Overview

Delving into the inner workings of Cognita, our goal was to strike a balance between full customisation and adaptability while ensuring user-friendliness right out of the box. Given the rapid pace of advancements in RAG and AI, it was imperative for us to engineer Cognita with scalability in mind, enabling seamless integration of new breakthroughs and diverse use cases. This led us to break down the RAG process into distinct modular steps (as shown in the above diagram, to be discussed in subsequent sections), facilitating easier system maintenance, the addition of new functionalities such as interoperability with other AI libraries, and enabling users to tailor the platform to their specific requirements. Our focus remains on providing users with a robust tool that not only meets their current needs but also evolves alongside technology, including broader architectural shifts such as MCP vs RAG, ensuring long-term value.
Components
Cognita is designed around seven different modules, each customisable and controllable to suit different needs:
- Data Loaders
- Parsers
- Embedders
- Rerankers
- Vector DBs
- Metadata Store
- Query Controllers
Data Loaders

These load the data from different sources like local directories, S3 buckets, databases, Truefoundry artifacts, etc. Cognita currently supports data loading from local directory, web url, Github repository and Truefoundry artifacts. More data loaders can be easily added under backend/modules/dataloaders/ . Once a dataloader is added you need to register it so that it can be used by the RAG application under backend/modules/dataloaders/__init__.py To register a dataloader add the following:
Parsers
In this step, we deal with different kinds of data, like regular text files, PDFs, and even Markdown files. The aim is to turn all these different types into one common format so we can work with them more easily later on. This part, called parsing, usually takes the longest and is difficult to implement when we're setting up a system like this. But using Cognita can help because it already handles the hard work of managing data pipelines for us.
Post this, we split the parsed data into uniform chunks. But why do we need this? The text we get from the files can be different lengths. If we use these long texts directly, we'll end up adding a bunch of unnecessary information. Plus, since all LLMs can only handle a certain amount of text at once, we won't be able to include all the important context needed for the question. So instead, we're going to break down the text into smaller parts for each section. Intuitively, smaller chunks will contain relevant concepts and will be less noisy compared to larger chunks.

Currently we support, parsing for Markdown, PDF and Text files. More data parsers can be easily added under backend/modules/parsers/ . Once a parser is added you need to register it so that it can be used by the RAG application under backend/modules/parsers/__init__.py To register a parser add the following:
Embedders
Once we've split the data into smaller pieces, we want to find the most important chunks for a specific question. One fast and effective way to do this is by using a pre-trained model (embedding model) to convert our data and the question into special codes called embeddings. Then, we compare the embeddings of each chunk of data to the one for the question. By measuring the cosine similarity between these embeddings, we can figure out which chunks are most closely related to the question, helping us find the best ones to use.

There are many pre-trained models available to embed the data such as models from OpenAI, Cohere, etc. The popular popular ones can be discovered through HuggingFace's Massive Text Embedding Benchmark (MTEB) leaderboard. We provide support for OpenAI Embeddings, TrueFoundry Embeddings and also current SOTA embeddings (as of April, 2024) from mixedbread-ai.
More embedders can be easily added under backend/modules/embedder/ . Once a embedder is added you need to register it so that it can be used by the RAG application under backend/modules/embedders/__init__.py To register a parser add the following:
Note: Remember, embeddings aren't the only method for finding important chunks. We could also use an LLM for this task! However, LLMs are much bigger than embedding models and have a limit on the amount of text they can handle at once. That's why it's smarter to use embeddings to pick the top k chunks first. Then, we can use LLMs on these fewer chunks to figure out the best ones to use as the context to answer our question.
Rerankers
Once embedding step finds some potential matches, which can be a lot, a reranking step is applied. Reranking to makes sure the best results are at the top. As a result, we can choose the top x documents making our context more concise and prompt query shorter. We provide the support for SOTA reranker (as of April, 2024) from mixedbread-ai which is implemented under backend/modules/reranker/
VectorDB
Once we create vectors for texts, we store them in something called a vector database. This database keeps track of these vectors so we can quickly find them later using different methods. Regular databases organize data in tables, like rows and columns, but vector databases are special because they store and find data based on these vectors. This is super useful for things like recognizing images, understanding language, or recommending stuff. For example, in a recommendation system, each item you might want to recommend (like a movie or a product) is turned into a vector, with different parts of the vector representing different features of the item, like its genre or price. Similarly, in language stuff, each word or document is turned into a vector, with parts of the vector representing features of the word or document, like how often the word is used or what it means. These vector databases are designed to handle these efficiently. Using different ways to measure how close vectors are to each other, like how similar they are or how far apart they are, we find vectors that are closest to the given user query. The most common ways to measure this are Euclidean Distance, Cosine Similarity, and Dot Product.
There are various available vector databases in the market, like Qdrant, SingleStore, Weaviate, etc. We currently support Qdrant and SingleStore. Qdrant vector db class is defined under /backend/modules/vector_db/qdrant.py, while SingleStore vector db class is defined under /backend/modules/vector_db/singlestore.py
Other vector dbs can be added too in the vector_db folder and can be registered under /backend/modules/vector_db/__init__.py
To add any vector DB support in Cognita, user needs to do the following:
- Create the class that inherits from
BaseVectorDB(from backend.modules.vector_db.base import BaseVectorDB) and initialize it withVectorDBConfig(from backend.types import VectorDBConfig) - Implement the following methods:
create_collection: To intialize the collection/project/table in vector db.upsert_documents: To insert the documents in the db.get_collections: Get all the collections present in the db.delete_collection: To delete the collection from the db.get_vector_store: To get the vector store for the given collection.get_vector_client: To get the vector client for the given collection if any.list_data_point_vectors: To list already present vectors in the db that are similar to the documents being inserted.delete_data_point_vectors: To delete the vectors from the db, used to remove old vectors of the updated document.
We now show how we can add a new vector db to the RAG system. We take example of both Qdrant and SingleStore vector dbs.
Qdrant Integration
Qdrant is an Open-Source Vector Database and Vector Search Engine written in Rust. It provides fast and scalable vector similarity search service with convenient API. To add Qdrant vector db to the RAG system, follow the below steps:
In .env file you can add the following
VECTOR_DB_CONFIG = '{"url": "<url_here>", "provider": "qdrant"}' # Qdrant URL for deployed instanceVECTOR_DB_CONFIG='{"provider":"qdrant","local":"true"}'# For local file based Qdrant instance without docker
- Create a new class
QdrantVectorDBinbackend/modules/vector_db/qdrant.pythat inherits fromBaseVectorDBand initialize it withVectorDBConfig
- Override the
create_collectionmethod to create a collection in Qdrant
- Override the
upsert_documentsmethod to insert the documents in the db
- Override the
get_collectionsmethod to get all the collections present in the db
- Override the
delete_collectionmethod to delete the collection from the db
- Override the
get_vector_storemethod to get the vector store for the given collection
- Override the
get_vector_clientmethod to get the vector client for the given collection if any
- Override the
list_data_point_vectorsmethod to list already present vectors in the db that are similar to the documents being inserted
- Override the
delete_data_point_vectorsmethod to delete the vectors from the db, used to remove old vectors of the updated document
SingleStore Integration
SingleStore offers powerful vector database functionality perfectly suited for AI-based applications, chatbots, image recognition and more, eliminating the need for you to run a speciality vector database solely for your vector workloads. Unlike traditional vector databases, SingleStore stores vector data in relational tables alongside other types of data. Co-locating vector data with related data allows you to easily query extended metadata and other attributes of your vector data with the full power of SQL.
SingleStore provides a free tier for developers to get started with their vector database. You can sign up for a free account here. Upon signup, go to Cloud -> workspace -> Create User. Use the credentials to connect to the SingleStore instance.
In .env file you can add the following
VECTOR_DB_CONFIG = '{"url": "<url_here>", "provider": "singlestore"}' # url: mysql://{user}:{password}@{host}:{port}/{db}
To add SingleStore vector db to the RAG system, follow the below steps:
- We want to add extra columns to table to store vector id hence, we override SingleStoreDB.
- 新しいクラス
SingleStoreVectorDBをbackend/modules/vector_db/singlestore.pyに作成し、BaseVectorDBを継承してVectorDBConfig で初期化します。
- をオーバーライドし、
create_collectionメソッドでSingleStoreにコレクションを作成します。
- をオーバーライドし、
upsert_documentsメソッドでドキュメントをDBに挿入します。
- をオーバーライドし、
get_collectionsメソッドでDB内のすべてのコレクションを取得します。
- をオーバーライドし、
delete_collectionメソッドでDBからコレクションを削除します。
- をオーバーライドする
get_vector_storeメソッドで、指定されたコレクションのベクトルストアを取得します
- をオーバーライドする
get_vector_clientメソッドで、指定されたコレクションのベクトルクライアントがあれば取得します
- をオーバーライドする
list_data_point_vectorsメソッドで、挿入されるドキュメントに類似した、DBに既に存在するベクトルをリストします
- をオーバーライドする
delete_data_point_vectorsメソッドで、DBからベクトルを削除します。これは、更新されたドキュメントの古いベクトルを削除するために使用されます。
メタデータストア
これには、プロジェクトまたはRAGアプリを一意に定義するために必要な設定が含まれています。RAGアプリは、1つ以上のデータソースからのドキュメントのセットを組み合わせたものを含む場合があり、これを私たちは コレクションと呼びます。これらのデータソースからのドキュメントは、データローディング + パース + 埋め込みメソッドを使用してベクトルDBにインデックス化されます。各RAGユースケースについて、メタデータストアには以下が含まれます。
- コレクション名
- 使用される関連ベクトルDBの名前
- リンクされたデータソース
- 各データソースのパース設定
- 埋め込みモデルとその使用設定
現在、このデータを保存する方法を2つ定義しており、1つは ローカルに 、もう1つは Truefoudryを使用します。これらのストアは、以下の場所に定義されています。 backend/modules/metada_store/
クエリコントローラー
データがインデックス化され、ベクトルDBに保存されたら、アプリを使用するためにすべての要素を組み合わせる時が来ました。クエリコントローラーはまさにその役割を果たします!これらは、対応するユーザーのクエリに対する回答を取得するのに役立ちます。標準的なクエリコントローラーのステップは以下の通りです。
- ユーザーは、クエリ、コレクション名、LLM設定、プロンプト、リトリーバー、およびその設定を含むリクエストペイロードを送信します。
- に基づいて
コレクション名関連するベクトルDBが、使用されるエンベッダー、ベクトルDBの種類などの設定とともに取得されます - に基づいて
クエリ、関連するドキュメントがリトリーバーを使用してベクトルDBから取得されます。 - 取得されたドキュメントは、
コンテキストとなり、クエリ(別名質問)とともにLLMに与えられ、回答が生成されます。このステップにはプロンプトチューニングも含まれる場合があります。 - 必要に応じて、生成された回答とともに、関連するドキュメントのチャンクもレスポンスで返されます。
注記: エージェントの場合、中間ステップもストリーミングできます。これは特定のアプリケーションが決定します。
クエリコントローラーのメソッドは、対応する関数にHTTPデコレーターを追加することで、APIとして直接公開できます。
独自のクエリコントローラーを追加するには、以下の手順を実行します。
- 各RAGユースケースには通常、クエリコントローラーを含む個別のフォルダーが必要です。ここでは、フォルダーが
app-2であると仮定します。したがって、コントローラーは/backend/modules/query_controller/app-2/controller.py - 追加します。
query_controllerデコレーターをクラスに追加し、カスタムコントローラーの名前を引数として渡します。 - このコントローラーに必要に応じてメソッドを追加し、当社のHTTPデコレーター(例:
POST、GET、DELETEを使い、メソッドをAPIとして機能させるため
- カスタムコントローラークラスを以下にインポートしてください。
backend/modules/query_controllers/__init__.py
サンプルクエリコントローラーは以下に記述されています。 /backend/modules/query_controller/example/controller.py より理解を深めるためにご参照ください。
Cognita - プロセスフロー
一般的なCognitaのプロセスは、2つのフェーズで構成されます。
- データインデックス作成
- レスポンス生成
データインデックス作成

このフェーズでは、ソースからデータをロードし、それらのソースに存在するドキュメントを解析し、ベクトルDBにインデックスを作成します。本番環境で発生する大量のドキュメントを処理するため、Cognitaはさらに一歩進んだアプローチを取ります。
- Cognitaは、すべてのドキュメントをまとめてインデックス作成するのではなく、バッチでグループ化します。
- Cognitaはドキュメントのハッシュを計算し追跡することで、インデックス作成のために新しいドキュメントがデータソースに追加されるたびに、データソース全体をインデックス作成するのではなく、それらのドキュメントのみがインデックス作成されるようにします。これにより、時間と計算リソースを大幅に節約できます。
- このインデックス作成モードは、
インクリメンタルインデックス作成の他に、Cognitaでサポートされている別のモードとしてフルインデックス作成があります。フルインデックス作成では、指定されたコレクションに既存のベクトルデータがあるかどうかにかかわらず、データをベクトルDBに再取り込みします。
応答生成

応答生成フェーズでは、 /answer で定義された QueryController のエンドポイントを呼び出し、要求されたクエリに対する応答を生成します。
Cognita UIの使用
以下の手順では、Cognita UIを使用してドキュメントをクエリする方法を示します。
1. データソースの作成
- 「
Data Sources」タブをクリックします。

- 「
+ New Datasource - データソースの種類は、ローカルディレクトリのファイル、Web URL、GitHub URL、またはTruefoundryアーティファクトFQNの提供のいずれかです。
- 例: もし
ローカルディレクトリが選択されている場合、お使いのコンピューターからファイルをアップロードし、送信をクリックしてください。
- 例: もし
- 作成されたデータソースのリストは、「データソース」タブで利用可能になります。

2. コレクションを作成
- 「
コレクション」タブをクリックします。 - 「
+ 新しいコレクション

- コレクション名を入力
- 埋め込みモデルを選択
- 以前に作成したデータソースと必要な設定を追加します。
- 「
処理」をクリックして、コレクションを作成し、データをインデックス化します。

3. コレクションを作成するとすぐにデータの取り込みが開始され、コレクションタブでコレクションを選択するとそのステータスを確認できます。後から追加のデータソースを追加し、コレクションにインデックスを作成することも可能です。

4. 応答生成

- コレクションを選択
- LLMとその設定を選択
- ドキュメントリトリーバーを選択
- プロンプトを記述するか、デフォルトのプロンプトを使用
- クエリを送信
今すぐ始めましょう!
TrueFoundry AI Gateway delivers ~3–4 ms latency, handles 350+ RPS on 1 vCPU, scales horizontally with ease, and is production-ready, while LiteLLM suffers from high latency, struggles beyond moderate RPS, lacks built-in scaling, and is best for light or prototype workloads.


















.webp)
.webp)





.png)

.png)














