Skip to main content
Neo4jNeo4j, Inc에서 개발한 그래프 데이터베이스 관리 시스템입니다.
Neo4j가 저장하는 데이터 요소는 노드, 노드들을 연결하는 엣지, 그리고 노드와 엣지의 속성입니다. 개발자들은 이를 네이티브 그래프 스토리지 및 처리 기능을 갖춘 ACID 준수 트랜잭션 데이터베이스로 설명합니다. Neo4j는 GNU General Public License를 수정한 라이선스로 제공되는 비오픈소스 “커뮤니티 에디션”과, 온라인 백업 및 고가용성 확장 기능이 포함된 상용 라이선스 버전으로 제공됩니다. Neo는 이러한 확장 기능이 포함된 Neo4j를 상용 라이선스로도 제공합니다.
이 노트북은 LLM을 사용하여 Cypher 쿼리 언어로 조회할 수 있는 그래프 데이터베이스에 자연어 인터페이스를 제공하는 방법을 보여줍니다.
Cypher는 속성 그래프에서 효율적이고 표현력 있는 데이터 쿼리를 가능하게 하는 선언적 그래프 쿼리 언어입니다.

설정하기

실행 중인 Neo4j 인스턴스가 필요합니다. 한 가지 옵션은 Aura 클라우드 서비스에서 무료 Neo4j 데이터베이스 인스턴스를 생성하는 것입니다. Neo4j Desktop 애플리케이션을 사용하여 로컬에서 데이터베이스를 실행하거나, Docker 컨테이너를 실행할 수도 있습니다. 다음 스크립트를 실행하여 로컬 Docker 컨테이너를 실행할 수 있습니다:
docker run \
    --name neo4j \
    -p 7474:7474 -p 7687:7687 \
    -d \
    -e NEO4J_AUTH=neo4j/password \
    -e NEO4J_PLUGINS=\["apoc"\]  \
    neo4j:latest
Docker 컨테이너를 사용하는 경우, 데이터베이스가 시작될 때까지 몇 초 정도 기다려야 합니다.
from langchain_neo4j import GraphCypherQAChain, Neo4jGraph
from langchain_openai import ChatOpenAI
graph = Neo4jGraph(url="bolt://localhost:7687", username="neo4j", password="password")
이 가이드에서는 기본적으로 OpenAI 모델을 사용합니다.
import getpass
import os

if "OPENAI_API_KEY" not in os.environ:
    os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")

데이터베이스에 데이터 추가하기

데이터베이스가 비어 있다고 가정하고, Cypher 쿼리 언어를 사용하여 데이터를 추가할 수 있습니다. 다음 Cypher 문은 멱등성을 가지므로, 한 번 실행하든 여러 번 실행하든 데이터베이스 정보는 동일하게 유지됩니다.
graph.query(
    """
MERGE (m:Movie {name:"Top Gun", runtime: 120})
WITH m
UNWIND ["Tom Cruise", "Val Kilmer", "Anthony Edwards", "Meg Ryan"] AS actor
MERGE (a:Actor {name:actor})
MERGE (a)-[:ACTED_IN]->(m)
"""
)
[]

그래프 스키마 정보 새로고침

데이터베이스 스키마가 변경되면, Cypher 문을 생성하는 데 필요한 스키마 정보를 새로고침할 수 있습니다.
graph.refresh_schema()
print(graph.schema)
Node properties:
Movie {runtime: INTEGER, name: STRING}
Actor {name: STRING}
Relationship properties:

The relationships:
(:Actor)-[:ACTED_IN]->(:Movie)

향상된 스키마 정보

향상된 스키마 버전을 선택하면 시스템이 데이터베이스 내의 예시 값을 자동으로 스캔하고 일부 분포 메트릭을 계산합니다. 예를 들어, 노드 속성이 10개 미만의 고유 값을 가지면 스키마에서 모든 가능한 값을 반환합니다. 그렇지 않으면 노드 및 관계 속성당 하나의 예시 값만 반환합니다.
enhanced_graph = Neo4jGraph(
    url="bolt://localhost:7687",
    username="neo4j",
    password="password",
    enhanced_schema=True,
)
print(enhanced_graph.schema)
Node properties:
- **Movie**
  - `runtime`: INTEGER Min: 120, Max: 120
  - `name`: STRING Available options: ['Top Gun']
- **Actor**
  - `name`: STRING Available options: ['Tom Cruise', 'Val Kilmer', 'Anthony Edwards', 'Meg Ryan']
Relationship properties:

The relationships:
(:Actor)-[:ACTED_IN]->(:Movie)

그래프 쿼리하기

이제 그래프 Cypher QA 체인을 사용하여 그래프에 질문할 수 있습니다.
chain = GraphCypherQAChain.from_llm(
    ChatOpenAI(temperature=0), graph=graph, verbose=True, allow_dangerous_requests=True
)
chain.invoke({"query": "Who played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name
Full Context:
[{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}, {'a.name': 'Anthony Edwards'}, {'a.name': 'Meg Ryan'}]

> Finished chain.
{'query': 'Who played in Top Gun?',
 'result': 'Tom Cruise, Val Kilmer, Anthony Edwards, and Meg Ryan played in Top Gun.'}

결과 수 제한하기

top_k 매개변수를 사용하여 Cypher QA 체인의 결과 수를 제한할 수 있습니다. 기본값은 10입니다.
chain = GraphCypherQAChain.from_llm(
    ChatOpenAI(temperature=0),
    graph=graph,
    verbose=True,
    top_k=2,
    allow_dangerous_requests=True,
)
chain.invoke({"query": "Who played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name
Full Context:
[{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}]

> Finished chain.
{'query': 'Who played in Top Gun?',
 'result': 'Tom Cruise, Val Kilmer played in Top Gun.'}

중간 결과 반환하기

return_intermediate_steps 매개변수를 사용하여 Cypher QA 체인의 중간 단계를 반환할 수 있습니다.
chain = GraphCypherQAChain.from_llm(
    ChatOpenAI(temperature=0),
    graph=graph,
    verbose=True,
    return_intermediate_steps=True,
    allow_dangerous_requests=True,
)
result = chain.invoke({"query": "Who played in Top Gun?"})
print(f"Intermediate steps: {result['intermediate_steps']}")
print(f"Final answer: {result['result']}")
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name
Full Context:
[{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}, {'a.name': 'Anthony Edwards'}, {'a.name': 'Meg Ryan'}]

> Finished chain.
Intermediate steps: [{'query': "MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)\nWHERE m.name = 'Top Gun'\nRETURN a.name"}, {'context': [{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}, {'a.name': 'Anthony Edwards'}, {'a.name': 'Meg Ryan'}]}]
Final answer: Tom Cruise, Val Kilmer, Anthony Edwards, and Meg Ryan played in Top Gun.

직접 결과 반환하기

return_direct 매개변수를 사용하여 Cypher QA 체인에서 직접 결과를 반환할 수 있습니다.
chain = GraphCypherQAChain.from_llm(
    ChatOpenAI(temperature=0),
    graph=graph,
    verbose=True,
    return_direct=True,
    allow_dangerous_requests=True,
)
chain.invoke({"query": "Who played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name

> Finished chain.
{'query': 'Who played in Top Gun?',
 'result': [{'a.name': 'Tom Cruise'},
  {'a.name': 'Val Kilmer'},
  {'a.name': 'Anthony Edwards'},
  {'a.name': 'Meg Ryan'}]}

Cypher 생성 프롬프트에 예시 추가하기

특정 질문에 대해 LLM이 생성할 Cypher 문을 정의할 수 있습니다.
from langchain_core.prompts.prompt import PromptTemplate

CYPHER_GENERATION_TEMPLATE = """Task:Generate Cypher statement to query a graph database.
Instructions:
Use only the provided relationship types and properties in the schema.
Do not use any other relationship types or properties that are not provided.
Schema:
{schema}
Note: Do not include any explanations or apologies in your responses.
Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.
Do not include any text except the generated Cypher statement.
Examples: Here are a few examples of generated Cypher statements for particular questions:
# How many people played in Top Gun?
MATCH (m:Movie {{name:"Top Gun"}})<-[:ACTED_IN]-()
RETURN count(*) AS numberOfActors

The question is:
{question}"""

CYPHER_GENERATION_PROMPT = PromptTemplate(
    input_variables=["schema", "question"], template=CYPHER_GENERATION_TEMPLATE
)

chain = GraphCypherQAChain.from_llm(
    ChatOpenAI(temperature=0),
    graph=graph,
    verbose=True,
    cypher_prompt=CYPHER_GENERATION_PROMPT,
    allow_dangerous_requests=True,
)
chain.invoke({"query": "How many people played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (m:Movie {name:"Top Gun"})<-[:ACTED_IN]-()
RETURN count(*) AS numberOfActors
Full Context:
[{'numberOfActors': 4}]

> Finished chain.
{'query': 'How many people played in Top Gun?',
 'result': 'There were 4 actors in Top Gun.'}

Cypher 생성과 답변 생성에 서로 다른 LLM 사용하기

cypher_llmqa_llm 매개변수를 사용하여 서로 다른 LLM을 정의할 수 있습니다.
chain = GraphCypherQAChain.from_llm(
    graph=graph,
    cypher_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
    qa_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo-16k"),
    verbose=True,
    allow_dangerous_requests=True,
)
chain.invoke({"query": "Who played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name
Full Context:
[{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}, {'a.name': 'Anthony Edwards'}, {'a.name': 'Meg Ryan'}]

> Finished chain.
{'query': 'Who played in Top Gun?',
 'result': 'Tom Cruise, Val Kilmer, Anthony Edwards, and Meg Ryan played in Top Gun.'}

특정 노드 및 관계 유형 무시하기

Cypher 문을 생성할 때 그래프 스키마의 일부를 무시하려면 include_types 또는 exclude_types를 사용할 수 있습니다.
chain = GraphCypherQAChain.from_llm(
    graph=graph,
    cypher_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
    qa_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo-16k"),
    verbose=True,
    exclude_types=["Movie"],
    allow_dangerous_requests=True,
)
# Inspect graph schema
print(chain.graph_schema)
Node properties are the following:
Actor {name: STRING}
Relationship properties are the following:

The relationships are the following:

생성된 Cypher 문 검증하기

validate_cypher 매개변수를 사용하여 생성된 Cypher 문의 관계 방향을 검증하고 수정할 수 있습니다.
chain = GraphCypherQAChain.from_llm(
    llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
    graph=graph,
    verbose=True,
    validate_cypher=True,
    allow_dangerous_requests=True,
)
chain.invoke({"query": "Who played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name
Full Context:
[{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}, {'a.name': 'Anthony Edwards'}, {'a.name': 'Meg Ryan'}]

> Finished chain.
{'query': 'Who played in Top Gun?',
 'result': 'Tom Cruise, Val Kilmer, Anthony Edwards, and Meg Ryan played in Top Gun.'}

데이터베이스 결과의 컨텍스트를 도구/함수 출력으로 제공하기

use_function_response 매개변수를 사용하여 데이터베이스 결과의 컨텍스트를 LLM에 도구/함수 출력으로 전달할 수 있습니다. 이 방법은 LLM이 제공된 컨텍스트를 더 밀접하게 따르기 때문에 답변의 정확성과 관련성을 향상시킵니다. 이 기능을 사용하려면 네이티브 함수 호출을 지원하는 LLM이 필요합니다.
chain = GraphCypherQAChain.from_llm(
    llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
    graph=graph,
    verbose=True,
    use_function_response=True,
    allow_dangerous_requests=True,
)
chain.invoke({"query": "Who played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name
Full Context:
[{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}, {'a.name': 'Anthony Edwards'}, {'a.name': 'Meg Ryan'}]

> Finished chain.
{'query': 'Who played in Top Gun?',
 'result': 'The main actors in Top Gun are Tom Cruise, Val Kilmer, Anthony Edwards, and Meg Ryan.'}
함수 응답 기능을 사용할 때 function_response_system을 제공하여 모델에게 답변 생성 방법을 지시하는 사용자 정의 시스템 메시지를 제공할 수 있습니다. use_function_response를 사용할 때 qa_prompt는 효과가 없습니다
chain = GraphCypherQAChain.from_llm(
    llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
    graph=graph,
    verbose=True,
    use_function_response=True,
    function_response_system="Respond as a pirate!",
    allow_dangerous_requests=True,
)
chain.invoke({"query": "Who played in Top Gun?"})
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name
Full Context:
[{'a.name': 'Tom Cruise'}, {'a.name': 'Val Kilmer'}, {'a.name': 'Anthony Edwards'}, {'a.name': 'Meg Ryan'}]

> Finished chain.
{'query': 'Who played in Top Gun?',
 'result': "Arrr matey! In the film Top Gun, ye be seein' Tom Cruise, Val Kilmer, Anthony Edwards, and Meg Ryan sailin' the high seas of the sky! Aye, they be a fine crew of actors, they be!"}

Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I