LlamaIndex AgensGraph
This plugin integrates AgensGraph
with LlamaIndex, persisting graphs and vectors
directly in AgensGraph. It powers PropertyGraphIndex and VectorStoreIndex,
so you can store and query property graphs and embeddings in one database.
- Property Graph Store:
AgensPropertyGraphStore - Vector Store:
AgensgraphVectorStore - Connection pool:
AgensEngine(optional, shared across stores)
Demos & guides
Start here: the examples/demos/ suite — runnable,
end-to-end demos on real datasets (arXiv, Wikipedia, CC-News) that show how to
build with this integration at realistic scale. Its
README has a quickstart and copy-paste building
blocks.
| Demo | What you build |
|---|---|
| 01 · arXiv | a property graph + vector search + GraphRAG in one store, with get/get_triplets and an upsert→delete lifecycle |
| 02 · Wikipedia | an LLM-built knowledge graph + natural-language (Text2Cypher) Q&A |
| 03 · News | vector RAG: semantic, metadata-filtered (full operator set), hybrid, and cited, plus the store-mutation lifecycle |
| 04 · Router | one AgensEngine routing questions to the graph or the vector store — router and FunctionAgent |
Each demo folder also ships a pre-executed notebook — a narrated, end-to-end tour with real embedded outputs.
Short, single-feature notebooks:
Requirements
- Python 3.10+
- AgensGraph with the
vectorextension (for vector / HNSW search). Themetaextension is used for schema introspection when present, with a catalog fallback otherwise.
What's new in 0.2.0
- Fixed
AgensPropertyGraphStore.vector_query. It previously hard-coded a 3-dimension embedding cast and ordered by a fixed literal vector, so results ignored the query embedding and errored at any other dimension. It now uses the configuredvector_dimensionconsistently and ranks by the actual query embedding, backed by the HNSW index. - Metadata-filtered vector search. Both
AgensPropertyGraphStore.vector_queryandAgensgraphVectorStore.queryhonorMetadataFilters, translated into a fully parameterized (injection-safe) CypherWHERE. All 14FilterOperatorvalues are supported —EQ,NE,GT,GTE,LT,LTE,IN,NIN,CONTAINS,TEXT_MATCH,TEXT_MATCH_INSENSITIVE,ANY,ALL,IS_EMPTY— along withAND/OR/NOTconditions and nested filter groups. - Hybrid search.
AgensgraphVectorStore(hybrid_search=True)fuses HNSW semantic search with full-text keyword search by reciprocal rank fusion — each modality is queried against its own index (so both stay index-backed) and the two rankings are merged. - AgensGraph-dialect Text2Cypher. The property graph store sets a default
text_to_cypher_templatethat knows the storage model (every node on a single"__Node__"label, entity type held in alabelslist) and avoids Neo4j-only syntax, soTextToCypherRetrievergenerates runnable Cypher out of the box. - Lazy schema introspection.
AgensPropertyGraphStore(refresh_schema=False)defers the (O(N)) schema scan to the firstget_schema()/get_schema_str()call, so opening a large existing graph is instant. - Indexed type filter. Each node also stores its primary type in a
btree-indexed
__type__scalar, so a type-scoped query (WHERE n.__type__ = 'X') is an index scan rather than a'X' IN n.labelsjsonb membership scan. - Correctness fixes. Entity embeddings are persisted on
upsert_nodeseven when the entity has no source chunk;get(ids=[])returns nothing (instead of the whole graph); and depth-1get_rel_mapuses a fixed pattern (AgensGraph's variable-length edges are far slower). - Modern vector-store node management.
AgensgraphVectorStoreimplementsget_nodes(node_ids, filters),delete_nodes(node_ids, filters)andclear()(plus asyncaget_nodes/adelete_nodes/aclear). - Richer enhanced schema. With
enhanced_schema=True, numeric properties getmin/max/distinct_count, list properties getmin_size/max_size, and other properties get example values +distinct_count(computed exhaustively under a row threshold, sampled above it). - Performance. Ingest is index-backed and near-linear (a btree index on the
idMERGE key; bulkadd/upsertbatched). Id-keyed lookups (get,get_nodes,get_triplets,get_rel_map,delete_nodes) and the vector store'sdelete(ref_doc_id)are index-backed rather than sequential scans, relation upserts are UNWIND-batched per type, and schema introspection no longer materializes every distinct property value. Metadata-filter keys can be indexed withcreate_property_index(...). See Performance & indexing. - True async + connection pooling. See Async & connection pooling.
- Breaking change. The deprecated triplet
AgensGraphStore(Knowledge Graph Store) has been removed. UseAgensPropertyGraphStorewithPropertyGraphIndex.
Installation
pip install llama-index llama-index-agensgraph
Usage
Property Graph Store
import os
import urllib.request
import nest_asyncio
from llama_index.core import SimpleDirectoryReader, PropertyGraphIndex
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
from llama_index.core.indices.property_graph import SchemaLLMPathExtractor
from llama_index_agensgraph.graph_stores.agensgraph import AgensPropertyGraphStore
os.environ[
"OPENAI_API_KEY"
] = "<YOUR_API_KEY>" # Replace with your OpenAI API key
os.makedirs("data/paul_graham/", exist_ok=True)
url = "https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt"
output_path = "data/paul_graham/paul_graham_essay.txt"
urllib.request.urlretrieve(url, output_path)
nest_asyncio.apply()
with open(output_path, "r", encoding="utf-8") as file:
content = file.read()
modified_content = content.replace("'", "\\'")
with open(output_path, "w", encoding="utf-8") as file:
file.write(modified_content)
documents = SimpleDirectoryReader("./data/paul_graham/").load_data()
# Setup AgensGraph connection (ensure AgensGraph is running)
conf = {
"dbname": "",
"user": "",
"password": "",
"host": "",
"port": 5432,
}
# Pass vector_dimension to enable the HNSW vector index (match your embedding
# model's dimension, e.g. 1536 for text-embedding-3-small). Without it, vector
# search still works but is unindexed.
graph_store = AgensPropertyGraphStore(
graph_name="graph",
conf=conf,
vector_dimension=1536,
)
index = PropertyGraphIndex.from_documents(
documents,
embed_model=OpenAIEmbedding(model_name="text-embedding-3-small"),
kg_extractors=[
SchemaLLMPathExtractor(
llm=OpenAI(model="gpt-4o-mini", temperature=0.0),
# strict=True can yield zero triplets with some models; strict=False is more forgiving
strict=False,
)
],
property_graph_store=graph_store,
show_progress=True,
)
query_engine = index.as_query_engine(include_text=True)
response = query_engine.query("What happened at Interleaf and Viaweb?")
print("\nDetailed Query Response:")
print(str(response))
Natural-language queries (Text2Cypher)
TextToCypherRetriever turns a question into AgensGraph Cypher using the store's
built-in dialect prompt — no custom prompt needed:
from llama_index.core.indices.property_graph import TextToCypherRetriever
retriever = TextToCypherRetriever(
graph_store=graph_store, llm=OpenAI(model="gpt-4o-mini")
)
nodes = retriever.retrieve("How many entities of each type are there?")
print(nodes[0].node.text) # the generated Cypher and its result
Vector Store
import os
import urllib.request
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, StorageContext
from llama_index_agensgraph.vector_stores.agensgraph import AgensgraphVectorStore
# Set your OpenAI API key
os.environ["OPENAI_API_KEY"] = "<YOUR_API_KEY>" # Replace with your key
# Download example data
os.makedirs("data/paul_graham/", exist_ok=True)
url = "https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt"
output_path = "data/paul_graham/paul_graham_essay.txt"
urllib.request.urlretrieve(url, output_path)
# Load documents
documents = SimpleDirectoryReader("./data/paul_graham").load_data()
# Setup AgensGraph connection (ensure AgensGraph is running)
url = "postgresql://username:password@host:port/database_name"
embed_dim = 1536
# Initialize vector store
vector_store = AgensgraphVectorStore(url=url, embedding_dimension=embed_dim)
# Build index
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
# Query
query_engine = index.as_query_engine()
response = query_engine.query("What happened at Interleaf?")
print("\nQuery Response:")
print(str(response))
For hybrid (vector + keyword) search, build the store with hybrid_search=True
and query with a query_str:
hybrid_store = AgensgraphVectorStore(
url=url, embedding_dimension=embed_dim, hybrid_search=True
)
index = VectorStoreIndex.from_vector_store(hybrid_store)
index.as_retriever(vector_store_query_mode="hybrid").retrieve(
"What happened at Interleaf?"
)
Async & connection pooling
By default each store opens a single dedicated connection. For concurrent
workloads, share an AgensEngine (a psycopg connection pool) across stores so
each request checks out its own connection instead of serializing on one:
from llama_index_agensgraph.engine import AgensEngine
from llama_index_agensgraph.graph_stores.agensgraph import AgensPropertyGraphStore
from llama_index_agensgraph.vector_stores.agensgraph import AgensgraphVectorStore
engine = AgensEngine.from_url(
"postgresql://user:pwd@host:5432/db", min_size=2, max_size=20
)
graph_store = AgensPropertyGraphStore(graph_name="graph", conf=conf, engine=engine)
vector_store = AgensgraphVectorStore(
url="postgresql://user:pwd@host:5432/db",
embedding_dimension=1536,
engine=engine,
)
# ... use the stores ...
engine.close() # await engine.aclose() if you used the async pool
The stores also provide true-async hot paths backed by psycopg.AsyncConnection
(no thread-pool wrapping):
- Vector store:
async_add,aquery,adelete - Property graph store:
aupsert_nodes,aupsert_relations,aget,avector_query,astructured_query
These work with or without an AgensEngine; with one, they draw from the async
pool.
Performance & indexing
The stores are indexed for their hot paths out of the box:
- Ingest (
MERGE-by-id) is backed by a btree index onid, so bulkupsert/addstays near-linear rather than O(N²). - Vector search uses the HNSW index on the embedding.
- Lookups by id (
get/get_nodes/delete_nodes) and the vector store'sdelete(ref_doc_id)are index-backed. - Relation upserts are UNWIND-batched per relationship type (not one query per relation).
Metadata-filtered vector search. A metadata filter cannot use the HNSW index for the filter itself, so a filter on an un-indexed property degrades to a sequential scan over the embedded nodes. Index the keys you filter on to keep it fast:
# Property graph store
graph_store.create_property_index("country")
# Vector store
vector_store.create_property_index("topic")
With the index present, the planner pre-selects matching rows via an index/bitmap scan and then ranks them — instead of scanning every node.
Counting and type filters. Prefer count(*) over count(n) in aggregations:
count(n) materializes each matched node (including its embedding), so it is much
slower on a graph that stores embeddings. For "all nodes of type X", filter on the
indexed n.__type__ scalar rather than 'X' IN n.labels.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llama_index_agensgraph-0.2.0.tar.gz.
File metadata
- Download URL: llama_index_agensgraph-0.2.0.tar.gz
- Upload date:
- Size: 46.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50f857ffa370f9b27204b98e29d256e2efca75464d6aebce56ddf0f26294be1f
|
|
| MD5 |
ada1aed0ac6a897a6d20352a7e7df86a
|
|
| BLAKE2b-256 |
c412b08053de7de47a7bc509f75053033b48d9068d097e00c7b07af3beaf916a
|
File details
Details for the file llama_index_agensgraph-0.2.0-py3-none-any.whl.
File metadata
- Download URL: llama_index_agensgraph-0.2.0-py3-none-any.whl
- Upload date:
- Size: 42.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8927932d78acf834e7c83f5e475c6b0f1de663314da4f45b5aad93b1d6124f06
|
|
| MD5 |
b33a40868be8a230896813bd67cee170
|
|
| BLAKE2b-256 |
68c1e1b0b853084b50db33d536ec1157f4e10a1ca337bdb099552568e0cb236d
|