■ CYPHER 템플리트와 커스텀 도구를 사용해 NEO4J 데이터베이스를 검색하는 방법을 보여준다.
※ NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD 환경 변수 값은 .env 파일에 정의한다.
※ OPENAI_API_KEY 환경 변수 값은 .env 파일에 정의한다.
▶ main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
from dotenv import load_dotenv from typing import List from typing import Tuple from langchain_core.messages import HumanMessage from langchain_core.messages import AIMessage from langchain_core.prompts import ChatPromptTemplate from langchain_core.prompts import MessagesPlaceholder from langchain_neo4j import Neo4jGraph from pydantic import BaseModel from pydantic import Field from langchain_core.tools import BaseTool from typing import Type from typing import Optional from langchain_core.callbacks import CallbackManagerForToolRun from langchain_core.callbacks import AsyncCallbackManagerForToolRun from langchain_openai import ChatOpenAI from langchain_core.utils.function_calling import convert_to_openai_function from langchain.agents.format_scratchpad import format_to_openai_function_messages from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser from langchain.agents import AgentExecutor load_dotenv() def formatChatHistory(chatHistory : List[Tuple[str, str]]): messageList = [] for humanMessageContent, aiMessageContent in chatHistory: messageList.append(HumanMessage(content = humanMessageContent)) messageList.append(AIMessage(content = aiMessageContent)) return messageList chatPromptTemplate = ChatPromptTemplate.from_messages( [ ("system", "You are a helpful assistant that finds information about movies and recommends them. If tools require follow up questions, make sure to ask the user for clarification. Make sure to include any available options that need to be clarified in the follow up questions Do only the things the user specifically requested."), MessagesPlaceholder(variable_name = "chat_history"), ("user", "{input}"), MessagesPlaceholder(variable_name = "agent_scratchpad") ] ) graph = Neo4jGraph() descriptionQuery = """ MATCH (m:Movie|Person) WHERE m.title CONTAINS $candidate OR m.name CONTAINS $candidate MATCH (m)-[r:ACTED_IN|HAS_GENRE]-(t) WITH m, type(r) as type, collect(coalesce(t.name, t.title)) as names WITH m, type + ": " + reduce(s = "", n IN names | s + n + ", ") as types WITH m, collect(types) as contexts WITH m, "type:" + labels(m)[0] + "\ntitle: "+ coalesce(m.title, m.name) + "\nyear: " + coalesce(m.released,"") + "\n" + reduce(s = "", c in contexts | s + substring(c, 0, size(c) - 2) +"\n") as context RETURN context LIMIT 1 """ def getInformation(entity : str) -> str: try: resultDictionaryList = graph.query(descriptionQuery, params = {"candidate" : entity}) return resultDictionaryList[0]["context"] except IndexError: return "No information was found" class InformationModel(BaseModel): entity : str = Field(description = "movie or a person mentioned in the question") class InformationTool(BaseTool): name : str = Field(default = "Information") description : str = Field(default = "useful for when you need to answer questions about various actors or movies") args_schema : Type[BaseModel] = InformationModel def _run(self, entity : str, runManager : Optional[CallbackManagerForToolRun] = None) -> str: """Use the tool.""" return getInformation(entity) async def _arun(self, entity : str, runManager : Optional[AsyncCallbackManagerForToolRun] = None) -> str: """Use the tool asynchronously.""" return getInformation(entity) toolList = [InformationTool()] chatOpenAI = ChatOpenAI(model = "gpt-4o-mini", temperature = 0) runnableBinding = chatOpenAI.bind(functions = [convert_to_openai_function(tool) for tool in toolList]) runnableSequence = ( { "input" : lambda x : x["input"], "chat_history" : lambda x : formatChatHistory(x["chat_history"]) if x.get("chat_history") else [], "agent_scratchpad" : lambda x : format_to_openai_function_messages(x["intermediate_steps"]) } | chatPromptTemplate | runnableBinding | OpenAIFunctionsAgentOutputParser() ) agentExecutor = AgentExecutor(agent = runnableSequence, tools = toolList, verbose = False) responseDictionary = agentExecutor.invoke({"input" : "Who played in Casino?"}) print(responseDictionary["output"]) """ In the movie "Casino," the main actors are: - Robert De Niro - Joe Pesci - Sharon Stone - James Woods The film was released on November 22, 1995. """ |
▶ requirements.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
aiohappyeyeballs==2.4.4 aiohttp==3.11.11 aiosignal==1.3.2 annotated-types==0.7.0 anyio==4.7.0 attrs==24.3.0 certifi==2024.12.14 charset-normalizer==3.4.0 colorama==0.4.6 distro==1.9.0 frozenlist==1.5.0 greenlet==3.1.1 h11==0.14.0 httpcore==1.0.7 httpx==0.28.1 idna==3.10 jiter==0.8.2 jsonpatch==1.33 jsonpointer==3.0.0 langchain==0.3.13 langchain-core==0.3.27 langchain-neo4j==0.2.0 langchain-openai==0.2.13 langchain-text-splitters==0.3.4 langsmith==0.2.4 multidict==6.1.0 neo4j==5.27.0 numpy==2.2.0 openai==1.58.1 orjson==3.10.12 packaging==24.2 propcache==0.2.1 pydantic==2.10.4 pydantic_core==2.27.2 python-dotenv==1.0.1 pytz==2024.2 PyYAML==6.0.2 regex==2024.11.6 requests==2.32.3 requests-toolbelt==1.0.0 sniffio==1.3.1 SQLAlchemy==2.0.36 tenacity==9.0.0 tiktoken==0.8.0 tqdm==4.67.1 typing_extensions==4.12.2 urllib3==2.2.3 yarl==1.18.3 |
※ pip install python-dotenv langchain-neo4j langchain-openai 명령을 실행했다.