mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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:
committed by
GitHub
Unverified
parent
2a8c3e2dcf
commit
34329840e1
@@ -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" />
|
||||
|
||||
+54
@@ -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())
|
||||
Reference in New Issue
Block a user