Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# OpenAI Configuration
OPENAI__API_KEY=your_openai_api_key_here
OPENAI__MODEL=gpt-4o-mini
OPENAI__TEMPERATURE=0.0
OPENAI__MAX_TOKENS=1500

# Neo4j Configuration
NEO4J__URI=your_neo4j_uri_here
Expand All @@ -11,23 +14,16 @@ NEO4J__DATABASE=neo4j
QDRANT__URL=your_qdrant_url_here
QDRANT__API_KEY=your_qdrant_api_key_here
QDRANT__COLLECTION_NAME=biomedical_papers
QDRANT__EMBEDDING_MODEL=text-embedding-3-large
QDRANT__EMBEDDING_DIMENSION=1536
QDRANT__RERANKER_EMBEDDING_DIMENSION=3072
QDRANT__EMBEDDING_MODEL=text-embedding-3-large
QDRANT__ESTIMATE_BM25_AVG_LEN_ON_X_DOCS=500
QDRANT__ESTIMATE_BM25_AVG_LEN_ON_X_DOCS=300
QDRANT__CLOUD_INFERENCE=true
JSON__DATA_PATH=data/pubmed_sample.json

# PubMed Configuration
PUBMED__API_KEY=your_pubmed_api_key_here
PUBMED__EMAIL=your_email@example.com

# OpenAI Settings
OPENAI__API_KEY=your_openai_api_key_here
OPENAI__MODEL=gpt-4o-mini
OPENAI__TEMPERATURE=0.0
OPENAI__MAX_TOKENS=1500

# JSON Paths
JSON__PUBMED_JSON_PATH=data/pubmed_dataset.json
JSON__GENE_JSON_PATH=data/gene_dataset.json
# JSON Data Paths (optional — defaults are data/pubmed_dataset.json and data/gene_dataset.json)
JSON_DATA__PUBMED_JSON_PATH=data/pubmed_dataset.json
JSON_DATA__GENE_JSON_PATH=data/gene_dataset.json
22 changes: 6 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,39 +48,29 @@ delete-graph: ## Delete all nodes and relationships in the Neo4j graph
uv run src/biomedical_graphrag/infrastructure/neo4j_db/delete_graph.py
@echo "Neo4j graph deletion complete."

example-graph-query: ## Run example queries on the Neo4j graph using GraphRAG
@echo "Running example queries on the Neo4j graph..."
uv run src/biomedical_graphrag/application/cli/fusion_query.py --examples
@echo "Example queries complete."

custom-graph-query: ## Run a custom natural language query using Neo4j GraphRAG (use QUESTION="your question")
@echo "Running custom query on the Neo4j graph with GraphRAG..."
uv run src/biomedical_graphrag/application/cli/fusion_query.py $(if $(QUESTION),--ask "$(QUESTION)")
custom-graph-query: ## Run a custom natural language query (use QUESTION="your question")
@echo "Running custom query..."
uv run src/biomedical_graphrag/application/cli/fusion_query.py "$(QUESTION)"
@echo "Custom query complete."

#################################################################################
## Qdrant Commands
#################################################################################
create-qdrant-collection: ## Create the Qdrant collection for embeddings
@echo "Creating Qdrant collection for embeddings..."
uv run src/biomedical_graphrag/infrastructure/qdrant_db/create_collection.py
uv run src/biomedical_graphrag/infrastructure/qdrant_engine/create_collection.py
@echo "Qdrant collection creation complete."

delete-qdrant-collection: ## Delete the Qdrant collection for embeddings
@echo "Deleting Qdrant collection for embeddings..."
uv run src/biomedical_graphrag/infrastructure/qdrant_db/delete_collection.py
uv run src/biomedical_graphrag/infrastructure/qdrant_engine/delete_collection.py
@echo "Qdrant collection deletion complete."

ingest-qdrant-data: ## Ingest embeddings into the Qdrant collection
@echo "Ingesting embeddings into the Qdrant collection..."
uv run src/biomedical_graphrag/infrastructure/qdrant_db/qdrant_ingestion.py
uv run src/biomedical_graphrag/infrastructure/qdrant_engine/qdrant_ingestion.py
@echo "Embeddings ingestion complete."

custom-qdrant-query: ## Run a custom query on the Qdrant collection (modify the --ask parameter as needed)
@echo "Running custom query on the Qdrant collection..."
uv run src/biomedical_graphrag/application/cli/query_vectorstore.py $(if $(QUESTION),--ask "$(QUESTION)")
@echo "Custom query complete."

#################################################################################
## API Server Commands
#################################################################################
Expand Down
104 changes: 39 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@
- [Data Collection](#data-collection)
- [Infrastructure Setup](#infrastructure-setup)
- [Neo4j Graph Database](#neo4j-graph-database)
- [Qdrant Vector Database](#qdrant-vector-database)
- [Qdrant Vector Search Engine](#qdrant-vector-search-engine)
- [Query Commands](#query-commands)
- [Qdrant Vector Search](#qdrant-vector-search)
- [Hybrid Neo4j + Qdrant Queries](#hybrid-neo4j--qdrant-queries)
- [Available Query Types](#available-query-types)
- [Sample Queries](#sample-queries)
- [API Server](#api-server)
- [Frontend](#frontend)
Expand All @@ -46,15 +44,20 @@

## Overview

A comprehensive GraphRAG (Graph Retrieval-Augmented Generation) system designed for biomedical research. It combines knowledge graphs with vector search engine to provide intelligent querying and analysis of biomedical literature and genomic data.
A biomedical context engineering system. An agent uses Qdrant vector search engine tools (hybrid retrieval, recommendations) and Neo4j graph database tools (graph enrichment) to gather context, then fuses it into a single biomedical answer.

Article: [Building a Biomedical GraphRAG: When Knowledge Graphs Meet Vector Search](https://aiechoes.substack.com/p/building-a-biomedical-graphrag-when)
> Originally forked from [benitomartin/biomedical-graphrag](https://github.com/benitomartin/biomedical-graphrag).

**References:**
- Video: [PubMed Navigator](https://www.youtube.com/watch?v=3NWTi90i6C4)
- Article: [Building a Biomedical GraphRAG: When Knowledge Graphs Meet Vector Search](https://aiechoes.substack.com/p/building-a-biomedical-graphrag-when)

**Key Features:**

- **Hybrid Query System**: Combines Neo4j graph database with Qdrant vector search engine for comprehensive biomedical insights
- **Context Engineering**: Agent orchestrates Qdrant and Neo4j tools, fusing results into a single answer
- **Qdrant Vector Search Engine**: Hybrid retrieval (dense + BM25 with reranking) and constraint-based recommendations
- **Neo4j Graph Database**: Graph enrichment via ontology-based tools (collaborator networks, MeSH relations, gene co-mentions)
- **Data Integration**: Processes PubMed papers, gene data, and research citations
- **Intelligent Querying**: Uses LLM-powered tool selection for graph enrichment and hybrid (semantic + lexical) search
- **Biomedical Schema**: Specialized graph schema for papers, authors, institutions, genes, and MeSH terms
- **Async Processing**: High-performance async data collection and processing

Expand All @@ -67,7 +70,7 @@ biomedical-graphrag/
├── src/
│ └── biomedical_graphrag/
│ ├── api/ # FastAPI server
│ │ └── server.py # GraphRAG API endpoints
│ │ └── server.py # PubMed Navigator API endpoints
│ ├── application/ # Application layer
│ │ ├── cli/ # Command-line interfaces
│ │ └── services/ # Business logic services
Expand Down Expand Up @@ -102,7 +105,7 @@ biomedical-graphrag/
1. Clone the repository:

```bash
git clone git@github.com:benitomartin/biomedical-graphrag.git
git clone git@github.com:thierrypdamiba/biomedical-graphrag.git
cd biomedical-graphrag
```

Expand All @@ -127,7 +130,7 @@ biomedical-graphrag/
1. Create a `.env` file in the root directory:

```bash
cp env.example .env
cp .env.example .env
```

## Usage
Expand Down Expand Up @@ -221,7 +224,7 @@ make delete-qdrant-collection
Notes:

- Embeddings are built from **PubMed paper abstracts**.
- This project uses OpenAI eembeddings **Matryoshka Representation Learning (MRL)** feature:
- This project uses OpenAI embeddings **Matryoshka Representation Learning (MRL)** feature:
- `QDRANT__EMBEDDING_DIMENSION` is the prefix dimension used for **retrieval** (stored in Qdrant as the `Dense` vector).
- `QDRANT__RERANKER_EMBEDDING_DIMENSION` is the (larger) prefix dimension used for **reranking** (stored in Qdrant as the `Reranker` vector).
- `make ingest-qdrant-data` currently recreates the collection each run (see `qdrant_ingestion.py`).
Expand All @@ -233,48 +236,27 @@ Notes:

### Query Commands

#### Qdrant Vector Search

```bash
# Run a custom query on the Qdrant vector store
make custom-qdrant-query QUESTION="Which institutions have collaborated most frequently on papers about 'Gene Editing' and 'Immunotherapy'?"

# Or run directly with the CLI
uv run src/biomedical_graphrag/application/cli/query_vectorstore.py --ask "Which institutions have collaborated most frequently on papers about 'Gene Editing' and 'Immunotherapy'?"
```

#### Hybrid Neo4j + Qdrant Queries

```bash
# Run example queries on the Neo4j graph using GraphRAG
make example-graph-query

# Run a custom natural language query using hybrid GraphRAG
# Run a custom natural language query
make custom-graph-query QUESTION="What are the latest research trends in cancer immunotherapy?"

# Or run directly with the CLI (positional args)
uv run src/biomedical_graphrag/application/cli/fusion_query.py "What are the latest research trends in cancer immunotherapy?"
```

#### Available Query Types

**Qdrant Queries:**

- Semantic search across paper abstracts and content
- Similarity-based retrieval using embeddings and BM25 fusion
The hybrid query system combines vector search engine (Qdrant) with graph enrichment (Neo4j):
- Author collaboration networks
- Citation analysis and paper relationships
- Gene-paper associations
- MeSH term relationships
- Institution affiliations

**Hybrid Queries:**

- Combines vector search engine (Qdrant) with graph enrichment (Neo4j):
- Author collaboration networks
- Citation analysis and paper relationships
- Gene-paper associations
- MeSH term relationships
- Institution affiliations
- LLM-powered tool selection & fusion:
- Runs one Qdrant tool: hybrid retrieval (BM25 + dense & reranking) or recommendations with contraints, - to fetch relevant papers.
- Calls Neo4j enrichment tools for graph evidence.
- Produces one fused answer from both sources.
LLM-powered tool selection & fusion:
- Runs one Qdrant tool: hybrid retrieval (BM25 + dense & reranking) or recommendations with constraints — to fetch relevant papers.
- Calls Neo4j enrichment tools for graph evidence.
- Produces one fused answer from both sources.

Output:

Expand All @@ -283,27 +265,12 @@ Output:
#### Sample Queries

- Who collaborates with Jennifer Doudna on CRISPR research?
Which researchers work with Emmanuelle Charpentier on gene editing or genome engineering papers?

- Who are George Church’s collaborators publishing on synthetic biology and genome sequencing?

- List scientists collaborating with Feng Zhang on neuroscience studies

- Which papers are related to PMID 31295471 based on shared MeSH terms?

- Find papers similar to the CRISPR-Cas9 genome editing study with PMID 31295471

- Show other studies linked by MeSH terms to PMID 27562951

- Which genes are mentioned in the same papers as gag?

- What genes appear together with HIF1A in cancer research?

- Which genes are frequently co-mentioned with TP53?

### API Server

The project includes a FastAPI server that exposes the GraphRAG functionality via HTTP endpoints:
The project includes a FastAPI server (PubMed Navigator) that exposes the context engineering pipeline via HTTP endpoints:

```bash
# Start the API server (runs on port 8765)
Expand All @@ -316,21 +283,28 @@ make run-api
|--------|----------|-------------|
| GET | `/health` | Health check |
| GET | `/api/neo4j/stats` | Neo4j graph statistics (node/relationship counts) |
| POST | `/api/search` | Hybrid GraphRAG search |
| POST | `/api/graphrag-query` | Context engineering search (Qdrant + Neo4j) |

**Search Request Example:**

```bash
curl -X POST http://localhost:8765/api/search \
curl -X POST http://localhost:8765/api/graphrag-query \
-H "Content-Type: application/json" \
-d '{"query": "What genes are associated with breast cancer?", "limit": 10}'
-d '{"query": "What genes are associated with breast cancer?", "limit": 5}'
```

### Frontend

The frontend is maintained in a separate repository:
The frontend is maintained in a separate repository: **[biomedical-graphrag-frontend](https://github.com/thierrypdamiba/biomedical-graphrag-frontend)**

The quickest way to run it locally:

```bash
# Auto-clones the frontend repo and starts it (requires pnpm)
make run-frontend
```

**[biomedical-graphrag-frontend](https://github.com/thierrypdamiba/biomedical-graphrag-frontend)**
Or manually:

```bash
git clone https://github.com/thierrypdamiba/biomedical-graphrag-frontend.git
Expand Down Expand Up @@ -359,7 +333,7 @@ curl http://localhost:8765/health
### Troubleshooting

- **Make fails immediately with ".env file is missing"**
- Create it with `cp env.example .env` and fill in required values.
- Create it with `cp .env.example .env` and fill in required values.

- **Qdrant ingestion/query fails**
- Confirm Qdrant is running and `QDRANT__URL` points to it.
Expand Down
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "biomedical-graphrag"
version = "0.1.0"
description = "GraphRAG system for biomedical research, combining knowledge graphs and vector search"
description = "Context engineering for biomedical research, combining Qdrant vector search engine and Neo4j graph database"
readme = "README.md"
authors = [
{name = "Benito Martin"}
Expand All @@ -14,8 +14,6 @@ dependencies = [
"fastapi>=0.115.0",
"loguru>=0.7.3",
"neo4j>=5.28.2",
"neo4j-graphrag>=1.10.0",
"nest-asyncio>=1.5.8",
"openai>=1.0.0",
"pydantic>=2.12.0",
"pydantic-settings>=2.11.0",
Expand Down
10 changes: 5 additions & 5 deletions src/biomedical_graphrag/api/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async def _preload_services() -> None:

app = FastAPI(
title="PubMed Navigator API",
description="Hybrid search API combining Qdrant vector search with Neo4j knowledge graph",
description="Context engineering API combining Qdrant vector search engine with Neo4j graph database",
version="0.1.0",
lifespan=lifespan,
)
Expand All @@ -84,8 +84,8 @@ class SearchRequest(BaseModel):
"""Search request body."""

query: str = Field(..., description="The search query")
limit: int = Field(default=5, ge=1, le=5, description="Maximum number of results")
mode: str = Field(default="graphrag", description="Search mode: graphrag, dense, sparse, hybrid")
limit: int = Field(default=5, ge=1, le=5, description="Maximum number of results (vector search)")
mode: str = Field(default="graphrag", description="Search mode: graphrag (Qdrant + Neo4j context engineering)")


class TraceStep(BaseModel):
Expand Down Expand Up @@ -188,9 +188,9 @@ async def get_neo4j_stats() -> Neo4jStatsResponse:
@app.post("/api/graphrag-query", response_model=SearchResponse)
async def search(request: SearchRequest) -> SearchResponse:
"""
Perform hybrid GraphRAG search.
Run context engineering pipeline.

Combines Qdrant vector search with Neo4j knowledge graph enrichment.
Combines Qdrant vector search engine with Neo4j graph enrichment and fuses the results.
"""
try:
_load_services()
Expand Down
8 changes: 4 additions & 4 deletions src/biomedical_graphrag/application/cli/fusion_query.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
CLI for hybrid GraphRAG querying:
1. Retrieve relevant papers from Qdrant based on the tool selected.
2. Use LLM to select and run Neo4j enrichment tools.
CLI for context engineering pipeline:
1. Retrieve relevant papers from Qdrant (hybrid retrieval or recommendations).
2. Use LLM to select and run Neo4j graph enrichment tools.
3. Fuse both sources into one concise biomedical summary.
"""

Expand Down Expand Up @@ -36,7 +36,7 @@ async def main() -> None:
answer = await run_tools_sequence_and_summarize(question)

print("\n=== Unified Biomedical Answer ===\n")
print(answer)
print(answer.summary)
except Exception as e:
logger.error(f"Error during query processing: {e}")
raise
Expand Down
Loading