Add Neo4j GraphRAG samples (#4994)

* Add Neo4j GraphRAG samples

* Fix sample CI issues

* Address sample review feedback

* Move Neo4j Python sample to end-to-end

* Make Neo4j GraphRAG sample self-contained

* Remove unused central package versions
This commit is contained in:
Christian Glessner
2026-04-01 12:23:04 +02:00
committed by GitHub
Unverified
parent 2a8c3e2dcf
commit 34329840e1
7 changed files with 320 additions and 0 deletions
+1
View File
@@ -171,6 +171,7 @@
<Project Path="samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/AgentWithRAG_Step02_CustomVectorStoreRAG.csproj" />
<Project Path="samples/02-agents/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/AgentWithRAG_Step03_CustomRAGDataSource.csproj" />
<Project Path="samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj" />
<Project Path="samples/02-agents/AgentWithRAG/AgentWithRAG_Step05_Neo4jGraphRAG/AgentWithRAG_Step05_Neo4jGraphRAG.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/ModelContextProtocol/">
<File Path="samples/02-agents/ModelContextProtocol/README.md" />
@@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageReference Remove="Microsoft.CodeAnalysis.NetAnalyzers" />
<PackageReference Remove="Microsoft.VisualStudio.Threading.Analyzers" />
<PackageReference Remove="xunit.analyzers" />
<PackageReference Remove="Moq.Analyzers" />
<PackageReference Remove="Roslynator.Analyzers" />
<PackageReference Remove="Roslynator.CodeAnalysis.Analyzers" />
<PackageReference Remove="Roslynator.Formatting.Analyzers" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
<PackageReference Include="Azure.Identity" Version="1.19.0" />
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-rc4" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="10.4.0" />
<PackageReference Include="Neo4j.AgentFramework.GraphRAG" Version="0.1.0-preview.2" />
<PackageReference Include="Neo4j.Driver" Version="5.28.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.100">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="4.14.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" Version="4.14.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.14.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft. All rights reserved.
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Neo4j.AgentFramework.GraphRAG;
using Neo4j.Driver;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
var neo4jUri = Environment.GetEnvironmentVariable("NEO4J_URI") ?? throw new InvalidOperationException("NEO4J_URI is not set.");
var neo4jUsername = Environment.GetEnvironmentVariable("NEO4J_USERNAME") ?? "neo4j";
var neo4jPassword = Environment.GetEnvironmentVariable("NEO4J_PASSWORD") ?? throw new InvalidOperationException("NEO4J_PASSWORD is not set.");
var fulltextIndex = Environment.GetEnvironmentVariable("NEO4J_FULLTEXT_INDEX_NAME") ?? "search_chunks";
const string RetrievalQuery = """
MATCH (node)-[:FROM_DOCUMENT]->(doc:Document)<-[:FILED]-(company:Company)
OPTIONAL MATCH (company)-[:FACES_RISK]->(risk:RiskFactor)
WITH node, score, company, doc, collect(DISTINCT risk.name)[0..5] AS risks
OPTIONAL MATCH (company)-[:MENTIONS]->(product:Product)
WITH node, score, company, doc, risks, collect(DISTINCT product.name)[0..5] AS products
RETURN
node.text AS text,
score,
company.name AS company,
company.ticker AS ticker,
doc.title AS title,
risks,
products
ORDER BY score DESC
""";
await using var driver = GraphDatabase.Driver(new Uri(neo4jUri), AuthTokens.Basic(neo4jUsername, neo4jPassword));
await driver.VerifyConnectivityAsync();
await using var provider = new Neo4jContextProvider(
driver,
new Neo4jContextProviderOptions
{
IndexName = fulltextIndex,
IndexType = IndexType.Fulltext,
RetrievalQuery = RetrievalQuery,
TopK = 5,
ContextPrompt = "Use the retrieved Neo4j graph context to answer accurately and call out when context is missing."
});
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(
new Uri(endpoint),
new DefaultAzureCredential())
.GetChatClient(deploymentName)
.AsIChatClient()
.AsAIAgent(new ChatClientAgentOptions
{
ChatOptions = new()
{
Instructions = "You are a helpful assistant that answers questions using Neo4j graph context."
},
AIContextProviders = [provider]
});
AgentSession session = await agent.CreateSessionAsync();
foreach (var question in new[]
{
"What products does Microsoft offer?",
"What risks does Apple face?",
"Tell me about NVIDIA's AI business and risk factors."
})
{
Console.WriteLine($">> {question}\n");
Console.WriteLine(await agent.RunAsync(question, session));
Console.WriteLine();
}
@@ -0,0 +1,32 @@
# Agent Framework Retrieval Augmented Generation (RAG) with Neo4j GraphRAG
This sample demonstrates how to create and run an agent that uses the [Neo4j GraphRAG context provider](https://github.com/neo4j-labs/neo4j-maf-provider) with Microsoft Agent Framework for .NET.
The sample uses a Neo4j fulltext index for retrieval and a Cypher `RetrievalQuery` to enrich results with related companies, products, and risk factors.
## Prerequisites
- .NET 10 SDK or later
- Azure OpenAI endpoint and chat deployment
- Azure CLI installed and authenticated
- A Neo4j database with chunked documents and a fulltext index such as `search_chunks`
## Environment variables
```powershell
$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
$env:NEO4J_URI="neo4j+s://your-instance.databases.neo4j.io"
$env:NEO4J_USERNAME="neo4j"
$env:NEO4J_PASSWORD="your-password"
$env:NEO4J_FULLTEXT_INDEX_NAME="search_chunks"
```
## Build and run
```powershell
dotnet build
dotnet run --framework net10.0 --no-build
```
The sample issues a few questions against the graph-backed retrieval provider and prints the responses to the console.
@@ -8,3 +8,4 @@ These samples show how to create an agent with the Agent Framework that uses Ret
|[RAG with Vector Store and custom schema](./AgentWithRAG_Step02_CustomVectorStoreRAG/)|This sample demonstrates how to create and run an agent that uses Retrieval Augmented Generation (RAG) with a vector store. It also uses a custom schema for the documents stored in the vector store.|
|[RAG with custom RAG data source](./AgentWithRAG_Step03_CustomRAGDataSource/)|This sample demonstrates how to create and run an agent that uses Retrieval Augmented Generation (RAG) with a custom RAG data source.|
|[RAG with Foundry VectorStore service](./AgentWithRAG_Step04_FoundryServiceRAG/)|This sample demonstrates how to create and run an agent that uses Retrieval Augmented Generation (RAG) with the Foundry VectorStore service.|
|[RAG with Neo4j GraphRAG](./AgentWithRAG_Step05_Neo4jGraphRAG/)|This sample demonstrates how to create and run an agent that uses a Neo4j-backed GraphRAG context provider with graph-enriched retrieval.|
@@ -0,0 +1,43 @@
# Neo4j GraphRAG Context Provider
The [Neo4j GraphRAG context provider](https://github.com/neo4j-labs/neo4j-maf-provider) adds read-only retrieval from a Neo4j knowledge graph to an Agent Framework agent. It supports vector, fulltext, and hybrid retrieval, and can enrich search results by traversing graph relationships with a Cypher `retrieval_query`.
This sample keeps setup lightweight by using a pre-built Neo4j fulltext index plus a graph-enrichment query.
## Example
| File | Description |
|---|---|
| [`main.py`](main.py) | Runnable GraphRAG sample using a Neo4j fulltext index and a Cypher enrichment query to surface related companies, products, and risk factors. |
## Prerequisites
1. A Neo4j database with document chunks already loaded
2. A Neo4j fulltext index over chunk text, such as `search_chunks`
3. An Azure AI Foundry project endpoint and chat deployment
4. Azure CLI authentication via `az login`
## Environment variables
This sample expects:
- `FOUNDRY_PROJECT_ENDPOINT`
- `FOUNDRY_MODEL`
- `NEO4J_URI`
- `NEO4J_USERNAME`
- `NEO4J_PASSWORD`
- `NEO4J_FULLTEXT_INDEX_NAME` (optional, defaults to `search_chunks`)
## Run with uv
From the `python/` directory:
```bash
uv run samples/05-end-to-end/neo4j_graphrag/main.py
```
## Notes
- This sample uses the published `agent-framework-neo4j` package rather than code from this repository.
- The package also supports vector and hybrid retrieval when you configure embeddings and indexes in Neo4j.
- For memory-oriented scenarios, the Neo4j project also maintains companion examples in the external provider repository.
@@ -0,0 +1,112 @@
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "agent-framework-foundry",
# "agent-framework-neo4j",
# ]
# ///
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import os
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_neo4j import Neo4jContextProvider, Neo4jSettings
from azure.identity.aio import AzureCliCredential
from dotenv import load_dotenv
load_dotenv()
"""
This sample demonstrates how to use the Neo4j GraphRAG context provider with
Agent Framework and Azure AI Foundry.
Environment variables:
FOUNDRY_PROJECT_ENDPOINT — Azure AI Foundry project endpoint
FOUNDRY_MODEL — Model deployment name (e.g. gpt-4o)
NEO4J_URI — Neo4j connection URI
NEO4J_USERNAME — Neo4j username
NEO4J_PASSWORD — Neo4j password
NEO4J_FULLTEXT_INDEX_NAME — Optional fulltext index name (defaults to search_chunks)
"""
USER_INPUTS = [
"What products does Microsoft offer?",
"What risks does Apple face?",
"Tell me about NVIDIA's AI business and risk factors.",
]
# Optional graph-enrichment query: retrieval works without this, but supplying
# a query lets the sample attach related company, product, and risk metadata to
# each retrieved chunk.
RETRIEVAL_QUERY = """
MATCH (node)-[:FROM_DOCUMENT]->(doc:Document)<-[:FILED]-(company:Company)
OPTIONAL MATCH (company)-[:FACES_RISK]->(risk:RiskFactor)
WITH node, score, company, doc, collect(DISTINCT risk.name)[0..5] AS risks
OPTIONAL MATCH (company)-[:MENTIONS]->(product:Product)
WITH node, score, company, doc, risks, collect(DISTINCT product.name)[0..5] AS products
RETURN
node.text AS text,
score,
company.name AS company,
company.ticker AS ticker,
doc.title AS title,
risks,
products
ORDER BY score DESC
"""
async def main() -> None:
# 1. Load and validate the Neo4j connection settings.
settings = Neo4jSettings()
if not settings.is_configured:
raise RuntimeError("Set NEO4J_URI, NEO4J_USERNAME, and NEO4J_PASSWORD before running this sample.")
# 2. Read the Azure AI Foundry project endpoint and model configuration.
project_endpoint = os.environ.get("FOUNDRY_PROJECT_ENDPOINT")
if not project_endpoint:
raise RuntimeError("Set FOUNDRY_PROJECT_ENDPOINT before running this sample.")
model = os.environ.get("FOUNDRY_MODEL") or "gpt-4o"
# 3. Create the Neo4j context provider and Foundry-backed agent, then ask sample questions.
async with (
AzureCliCredential() as credential,
Neo4jContextProvider(
source_id="neo4j_graphrag",
uri=settings.uri,
username=settings.username,
password=settings.get_password(),
index_name=settings.fulltext_index_name,
index_type="fulltext",
retrieval_query=RETRIEVAL_QUERY,
top_k=5,
) as provider,
Agent(
client=FoundryChatClient(
project_endpoint=project_endpoint,
model=model,
credential=credential,
),
name="Neo4jGraphRAGAgent",
instructions=(
"You are a helpful assistant. Use the Neo4j context provider results to answer accurately. "
"If the retrieved context is insufficient, say so plainly."
),
context_providers=[provider],
) as agent,
):
session = agent.create_session()
print("=== Neo4j GraphRAG Context Provider ===\n")
for user_input in USER_INPUTS:
print(f"User: {user_input}")
result = await agent.run(user_input, session=session)
print(f"Agent: {getattr(result, 'text', result)}\n")
if __name__ == "__main__":
asyncio.run(main())