Uma implementação GraphRAG simples e fácil de hackear
? GraphRAG é bom e poderoso, mas a implementação oficial é difícil/dolorosa de ler ou hackear .
? Este projeto fornece um GraphRAG menor, mais rápido e mais limpo , mantendo a funcionalidade principal (consulte benchmark e problemas).
? Excluindo tests
e prompts, nano-graphrag
tem cerca de 1100 linhas de código .
? Pequeno mas portátil (faiss, neo4j, ollama...), assíncrono e totalmente digitado.
Instalar da fonte (recomendado)
# clone this repo first
cd nano-graphrag
pip install -e .
Instalar a partir do PyPi
pip install nano-graphrag
Dica
Defina a chave da API OpenAI no ambiente: export OPENAI_API_KEY="sk-..."
.
Dica
Se você estiver usando a API Azure OpenAI, consulte .env.example para definir seu Azure OpenAI. Em seguida, passe GraphRAG(...,using_azure_openai=True,...)
para ativar.
Dica
Se você estiver usando a API Amazon Bedrock, certifique-se de que suas credenciais estejam definidas corretamente por meio de comandos como aws configure
. Em seguida, habilite-o configurando assim: GraphRAG(...,using_amazon_bedrock=True, best_model_id="us.anthropic.claude-3-sonnet-20240229-v1:0", cheap_model_id="us.anthropic.claude-3-haiku-20240307-v1:0",...)
. Consulte um script de exemplo.
Dica
Se você não tiver nenhuma chave, confira este exemplo que usa transformers
e ollama
. Se você gosta de usar outro LLM ou modelo de incorporação, verifique Avanços.
baixe uma cópia de A Christmas Carol de Charles Dickens:
curl https://raw.githubusercontent.com/gusye1234/nano-graphrag/main/tests/mock_data.txt > ./book.txt
Use o trecho python abaixo:
from nano_graphrag import GraphRAG , QueryParam
graph_func = GraphRAG ( working_dir = "./dickens" )
with open ( "./book.txt" ) as f :
graph_func . insert ( f . read ())
# Perform global graphrag search
print ( graph_func . query ( "What are the top themes in this story?" ))
# Perform local graphrag search (I think is better and more scalable one)
print ( graph_func . query ( "What are the top themes in this story?" , param = QueryParam ( mode = "local" )))
Na próxima vez que você inicializar um GraphRAG
do mesmo working_dir
, ele recarregará todos os contextos automaticamente.
graph_func . insert ([ "TEXT1" , "TEXT2" ,...])
nano-graphrag
suporta inserção incremental, nenhum cálculo ou dados duplicados serão adicionados:
with open ( "./book.txt" ) as f :
book = f . read ()
half_len = len ( book ) // 2
graph_func . insert ( book [: half_len ])
graph_func . insert ( book [ half_len :])
nano-graphrag
usa md5-hash do conteúdo como chave, para que não haja nenhum pedaço duplicado.No entanto, cada vez que você inserir, as comunidades do gráfico serão recalculadas e os relatórios da comunidade serão gerados novamente
nano-graphrag
também suporta inserção e consulta ingênua de RAG:
graph_func = GraphRAG ( working_dir = "./dickens" , enable_naive_rag = True )
...
# Query
print ( rag . query (
"What are the top themes in this story?" ,
param = QueryParam ( mode = "naive" )
)
Para cada método NAME(...)
, existe um método assíncrono correspondente aNAME(...)
await graph_func . ainsert (...)
await graph_func . aquery (...)
...
GraphRAG
e QueryParam
são dataclass
em Python. Use help(GraphRAG)
e help(QueryParam)
para ver todos os parâmetros disponíveis! Ou confira a seção Avanços para ver algumas opções.
Abaixo estão os componentes que você pode usar:
Tipo | O que | Onde |
---|---|---|
LLM | OpenAI | Integrado |
Base Amazônica | Integrado | |
DeepSeek | exemplos | |
ollama | exemplos | |
Incorporação | OpenAI | Integrado |
Base Amazônica | Integrado | |
Transformadores de frases | exemplos | |
Banco de dados vetorial | nano-vectordb | Integrado |
hnswlib | Integrado, exemplos | |
milvus-lite | exemplos | |
faisse | exemplos | |
Armazenamento gráfico | networkx | Integrado |
neo4j | Integrado (doc) | |
Visualização | gráficoml | exemplos |
Pedaço | por tamanho do token | Integrado |
por divisor de texto | Integrado |
Built-in
significa que temos essa implementação dentro nano-graphrag
. examples
significa que temos essa implementação dentro de um tutorial na pasta de exemplos.
Verifique exemplos/benchmarks para ver algumas comparações entre componentes.
Sempre seja bem-vindo para contribuir com mais componentes.
GraphRAG(...,always_create_working_dir=False,...)
pulará a etapa de criação do diretório. Use-o se você mudar todos os seus componentes para armazenamentos que não sejam de arquivos. graph_func.query
retorna a resposta final sem streaming.
Se você gosta de interagir nano-graphrag
em seu projeto, você pode usar param=QueryParam(..., only_need_context=True,...)
, que retornará apenas o contexto recuperado do gráfico, algo como:
# Local mode
-----Reports-----
```csv
id, content
0, # FOX News and Key Figures in Media and Politics...
1, ...
```
...
# Global mode
----Analyst 3----
Importance Score: 100
Donald J. Trump: Frequently discussed in relation to his political activities...
...
Você pode integrar esse contexto ao seu prompt personalizado.
nano-graphrag
usa prompts do objeto dict nano_graphrag.prompt.PROMPTS
. Você pode brincar com ele e substituir qualquer prompt interno.
Algumas dicas importantes:
PROMPTS["entity_extraction"]
é usado para extrair as entidades e relações de um pedaço de texto.PROMPTS["community_report"]
é usado para organizar e resumir a descrição do cluster de gráfico.PROMPTS["local_rag_response"]
é o modelo de prompt do sistema da geração de pesquisa local.PROMPTS["global_reduce_rag_response"]
é o modelo de prompt do sistema da geração de pesquisa global.PROMPTS["fail_response"]
é a resposta substituta quando nada está relacionado à consulta do usuário. nano-graphrag
permite que você personalize seu próprio método de chunking, confira o exemplo.
Mude para o método integrado de fragmentação do divisor de texto:
from nano_graphrag . _op import chunking_by_seperators
GraphRAG (..., chunk_func = chunking_by_seperators ,...)
No nano-graphrag
, precisamos de dois tipos de LLM, um excelente e outro barato. O primeiro é usado para planejar e responder, o segundo é usado para resumir. Por padrão, o melhor é gpt-4o
e o mais barato é gpt-4o-mini
Você pode implementar sua própria função LLM (consulte _llm.gpt_4o_complete
):
async def my_llm_complete (
prompt , system_prompt = None , history_messages = [], ** kwargs
) -> str :
# pop cache KV database if any
hashing_kv : BaseKVStorage = kwargs . pop ( "hashing_kv" , None )
# the rest kwargs are for calling LLM, for example, `max_tokens=xxx`
...
# YOUR LLM calling
response = await call_your_LLM ( messages , ** kwargs )
return response
Substitua o padrão por:
# Adjust the max token size or the max async requests if needed
GraphRAG ( best_model_func = my_llm_complete , best_model_max_token_size = ..., best_model_max_async = ...)
GraphRAG ( cheap_model_func = my_llm_complete , cheap_model_max_token_size = ..., cheap_model_max_async = ...)
Você pode consultar este exemplo que usa deepseek-chat
como modelo LLM
Você pode consultar este exemplo que usa ollama
como modelo LLM
nano-graphrag
usará best_model_func
para gerar JSON com parâmetros "response_format": {"type": "json_object"}
. No entanto, existem alguns modelos de código aberto que podem produzir JSON instável.
nano-graphrag
apresenta uma interface pós-processamento para você converter a resposta em JSON. A assinatura desta função está abaixo:
def YOUR_STRING_TO_JSON_FUNC ( response : str ) -> dict :
"Convert the string response to JSON"
...
E passe sua própria função por GraphRAG(...convert_response_to_json_func=YOUR_STRING_TO_JSON_FUNC,...)
.
Por exemplo, você pode consultar json_repair para reparar a string JSON retornada pelo LLM.
Você pode substituir as funções de incorporação padrão por qualquer instância _utils.EmbedddingFunc
.
Por exemplo, o padrão é usar a API de incorporação OpenAI:
@ wrap_embedding_func_with_attrs ( embedding_dim = 1536 , max_token_size = 8192 )
async def openai_embedding ( texts : list [ str ]) -> np . ndarray :
openai_async_client = AsyncOpenAI ()
response = await openai_async_client . embeddings . create (
model = "text-embedding-3-small" , input = texts , encoding_format = "float"
)
return np . array ([ dp . embedding for dp in response . data ])
Substitua a função de incorporação padrão por:
GraphRAG ( embedding_func = your_embed_func , embedding_batch_num = ..., embedding_func_max_async = ...)
Você pode consultar um exemplo que usa sentence-transformer
para calcular embeddings localmente.
Você pode substituir todos os componentes relacionados ao armazenamento por sua própria implementação. nano-graphrag
usa principalmente três tipos de armazenamento:
base.BaseKVStorage
para armazenar pares de dados key-json
GraphRAG(.., key_string_value_json_storage_cls=YOURS,...)
base.BaseVectorStorage
para indexação de embeddings
nano-vectordb
como backend.hnswlib
integrado, confira este exemplo.milvus-lite
como backend (não disponível no Windows).GraphRAG(.., vector_db_storage_cls=YOURS,...)
base.BaseGraphStorage
para armazenar gráfico de conhecimento
networkx
como backend.Neo4jStorage
integrado para gráfico, confira este tutorial.GraphRAG(.., graph_storage_cls=YOURS,...)
Você pode consultar nano_graphrag.base
para ver interfaces detalhadas para cada componente.
Verifique o FQA.
Veja ROADMAP.md
nano-graphrag
está aberto a qualquer tipo de contribuição. Leia isto antes de contribuir.
nano-graphrag
Bem-vindo a receber solicitações se o seu projeto usar
nano-graphrag
, isso ajudará outras pessoas a confiar neste repositório❤️
nano-graphrag
não implementou o recurso covariates
do GraphRAG
nano-graphrag
implementa a busca global diferente da original. O original usa um estilo de redução de mapa para preencher todas as comunidades no contexto, enquanto nano-graphrag
usa apenas as principais comunidades importantes e centrais (use QueryParam.global_max_consider_community
para controlar, padrão para 512 comunidades).