æ¬èšäºã¯æ¥æ¬ãªã©ã¯ã«ãéå¶ããäžèšMeetupã§çºè¡šäºå®ã®å 容ã«ãªããŸããçºè¡šãŸã§ã«ä»åŸãå 容ã¯äºåãªã倿Žãããå¯èœæ§ãããããšãããããããäºæ¿ãã ããã
â»æ¬èšäºã¯äžèšèšäºã®è£è¶³èšäºãšãªããŸãã
â»ããŒãã¢ãã宿œæã®åç»ã¯âãã¡ãã
èªç¶èšèªã«ããSQLããŒã¿ããŒã¹ã®åæ
äžè¬çã«ãããŒã¿ããŒã¹ã®åæã¯SQLã§åæåŠçãå®è¡ããããBIããŒã«ã§ãã®åŠçãè¡ã£ããããŸããããããã«é¢ããæ·±ãæè¡çç¥èããªããŠããLLMãå©çšããèªç¶èšèªã§è³ªåãå ¥åããã ãã§ããŒã¿åæãå¯èœã«ãªããŠãŒã¹ã±ãŒã¹ã§ãã
ãŠãŒã¹ã±ãŒã¹
DBã®åæã«é¢é£ããã¢ããªã±ãŒã·ã§ã³ã¯å šãŠãŠãŒã¹ã±ãŒã¹ã®å¯Ÿè±¡ãšãªããŸããäŸãã°ä»¥äžã®ãããªã¢ããªã±ãŒã·ã§ã³ã§ãã
| ãŠãŒã¹ã±ãŒã¹ | 説æ |
|---|---|
| å¶æ¥ã¬ããŒãã®èªåçæ | å¶æ¥éšéã®æ¹ã ãèªç¶èšèªã§è³ªåãããªã¢ã«ã¿ã€ã ã§å¶æ¥ã¬ããŒããçæãè¿ éãªæææ±ºå®ãå¯èœã«ã |
| ããŒã±ãã£ã³ã°ãã£ã³ããŒã³ã®å¹ææž¬å® | ãã£ã³ããŒã³ã®å¹æãèªç¶èšèªã§åæããROIè©äŸ¡ãæ¬¡ã®æŠç¥èª¿æŽã«åœ¹ç«ãŠãã |
| 人äºåæ | èªç¶èšèªã§éè·çãªã©ãåæããç€Ÿå¡æºè¶³åºŠããšã³ã²ãŒãžã¡ã³ãåäžã®ã€ã³ãµã€ããåŸãã |
| 財ååæãšäºç®ç®¡ç | èªç¶èšèªã§çµè²»ããŒã¿ãéèšããäºç®ç®¡çãæ¯åºèšç»ã®è¿ éãªææ¡ããµããŒãã |
| åšåº«ç®¡çã®æé©å | èªç¶èšèªã§åšåº«ããŒã¿ãåæãã驿ã®è£å ãåšåº«ç®¡çã®æé©åãå®çŸã |
ä»çµã¿
åºæ¬çãªä»çµã¿ãšããŠã¯äžèšã®ããã«ãªãããã£ãŠã·ã³ãã«ã§ãã
â ãŠãŒã¶ãŒããã®è³ªåãLLMã«å
¥å
â¡LLMã質åãSQLã«å€æ
â¢å€æãããSQLã§ããŒã¿ããŒã¹ã«ã¯ãšãªãå®è¡
â£ã¯ãšãªçµæãšè³ªåæãLLMã«å
¥åãåçãçæ
ãšããæµãã§åŠçãé²ããããªã³ãŒããå®è£ ããŸããäºæ³éããšããæãã§ã¯ãªãã§ããããããšã¯èšããããã¹ã¯ã©ããã§å®è£ ãããšå®ã¯æå€ãšé¢åãªããšã«ãªããŸããããã§LangChain(ã®SQL Database Agent)ã®ç»å Žã§ããLangChainãããã¯ãšã³ãã§èªåçã«åŠçããŠãããå 容ãå«ããŠãµã³ãã«ã³ãŒããæŠèª¬ããŸãã
#ã³ãŒãæŠèª¬
å©çšããåã³ã³ããŒãã³ãã¯äžèšã®éãã§ãã
- LLM : OpenAI
- SQL Database : SQLite
- Dataset : èåãªé³æ¥œã¢ãŒãã£ã¹ãåãšãªãªãŒã¹ã¢ã«ãã ã®ã¹ããŒã
ãŸãã¯å¿ èŠãªã©ã€ãã©ãªã®ã€ã³ã¹ããŒã«ã§ãã
!pip install --upgrade --quiet langchain langchain-community langchain-experimental langchain-openai
次ã«OpenAIãšLangSmithã®APIããŒãå ¥åããŸãã(LangSmithã¯ãªãã·ã§ã³ã§ããå¿ é ã§ã¯ãããŸããã)
import getpass
import os
def _set_if_undefined(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"Please provide your {var}")
_set_if_undefined("OPENAI_API_KEY")
_set_if_undefined("LANGCHAIN_API_KEY")
# Optional, add tracing in LangSmith
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = "Database Agent"
SQLããŒã¿ããŒã¹(sqlite)ã«æ¥ç¶ããŠã¯ãšãªãå®è¡ãã¹ããŒãã確èªããŸãã
from langchain_community.utilities import SQLDatabase
db = SQLDatabase.from_uri("sqlite:///Chinook.db", sample_rows_in_table_info=3)
ããŒã¿ããŒã¹å ã«ãã衚ã確èªããŸãã
print(db.get_usable_table_names())
['Album', 'Artist', 'Customer', 'Employee', 'Genre', 'Invoice', 'InvoiceLine', 'MediaType', 'Playlist', 'PlaylistTrack', 'Track']
äžèšã11åã®è¡šãããããšãããããŸãã
衚 Artist ã«ã¯ãšãªãå®è¡ããŸãã
print(db.run("SELECT * FROM Artist LIMIT 10;"))
[(1, 'AC/DC'), (2, 'Accept'), (3, 'Aerosmith'), (4, 'Alanis Morissette'), (5, 'Alice In Chains'), (6, 'AntÃŽnio Carlos Jobim'), (7, 'Apocalyptica'), (8, 'Audioslave'), (9, 'BackBeat'), (10, 'Billy Cobham')]
èåãªããã¯ãã³ãã®ãã³ãåãå ¥åãããŠãã衚ã ãšããããšãããããŸãã衚åã®éãã§ããæ¬¡ã«ã衚Albumã«ã¯ãšãªãå®è¡ããŸãã
print(db.run("SELECT * FROM Album LIMIT 10;"))
[(1, 'For Those About To Rock We Salute You', 1), (2, 'Balls to the Wall', 2), (3, 'Restless and Wild', 2), (4, 'Let There Be Rock', 1), (5, 'Big Ones', 3), (6, 'Jagged Little Pill', 4), (7, 'Facelift', 5), (8, 'Warner 25 Anos', 6), (9, 'Plays Metallica By Four Cellos', 7), (10, 'Audioslave', 8)]
衚åéãããã¡ãã¯ã¢ãŒãã£ã¹ãã®ã¢ã«ãã åã§ãã
ä»åã¯ãã®2ã€ã®è¡šã䜿ã£ãŠèªç¶èšèªã§ã¯ãšãªãå®è¡ããŠã¿ãããšæããŸãã
ãŸããLLMãšããŠOpenAIãå®çŸ©ããŸãã
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo")
次ã«Agentãå®çŸ©ããŸããæ¬¡ã®äžè¡ããã®å®è£ ã®èã«ãªããŸããLangChainã®Agentã¯æ§ã ãªã¿ã€ãã®ãã®ããããšäžè¿°ããŸããããããã§ã¯create_sql_agent颿°ã«ããSQLDatabaseAgentãã€ãŸãRDBçšã®Agentãå®çŸ©ããŠããŸãããããŠãAgentã察象ãšããtoolã¯LangChainã§äºåã«çšæãããŠããopenai-toolsã§ãã
agent_executor = create_sql_agent(llm, db=db, agent_type="openai-tools", verbose=True)
ãã®äžè¡ã®å®çŸ©ã ãã§ããŠãŒã¶ãŒã®è³ªåãLLMã«é£æºããSQLã«å€æåŸãDBã«ã¯ãšãªå®è¡ããã®åŸã質åæãšSQLã®çµæãLLMã«å ¥åãããšããå šäœã®ãã€ãã©ã€ã³ãå®çŸ©ãããŸãã
å ·äœçã«ã©ã®ãããªãã€ãã©ã€ã³ãå®çŸ©ãããŠããã®ããagent_executorã®äžèº«ãã®ãããŠã¿ãŸãã
print(agent_executor)
AgentExecutor(name='SQL Agent Executor', verbose=True, agent=RunnableMultiActionAgent(runnable=RunnableAssign(mapper={
agent_scratchpad: RunnableLambda(lambda x: format_to_openai_tool_messages(x['intermediate_steps']))
})
| ChatPromptTemplate(input_variables=['agent_scratchpad', 'input'], input_types={'agent_scratchpad': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]]}, messages=[SystemMessage(content='You are an agent designed to interact with a SQL database.\nGiven an input question, create a syntactically correct sqlite query to run, then look at the results of the query and return the answer.\nUnless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 10 results.\nYou can order the results by a relevant column to return the most interesting examples in the database.\nNever query for all the columns from a specific table, only ask for the relevant columns given the question.\nYou have access to tools for interacting with the database.\nOnly use the below tools. Only use the information returned by the below tools to construct your final answer.\nYou MUST double check your query before executing it. If you get an error while executing a query, rewrite the query and try again.\n\nDO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.\n\nIf the question does not seem related to the database, just return "I don\'t know" as the answer.\n'), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], template='{input}')), AIMessage(content='I should look at the tables in the database to see what I can query. Then I should query the schema of the most relevant tables.'), MessagesPlaceholder(variable_name='agent_scratchpad')])
| RunnableBinding(bound=ChatOpenAI(client=<openai.resources.chat.completions.Completions object at 0x7ffa4aa815d0>, async_client=<openai.resources.chat.completions.AsyncCompletions object at 0x7ffa4aa853d0>, root_client=<openai.OpenAI object at 0x7ffa4b263ed0>, root_async_client=<openai.AsyncOpenAI object at 0x7ffa4aa81790>, temperature=0.0, openai_api_key=SecretStr('**********'), openai_proxy=''), kwargs={'tools': [{'type': 'function', 'function': {'name': 'sql_db_query', 'description': "Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.", 'parameters': {'type': 'object', 'properties': {'query': {'description': 'A detailed and correct SQL query.', 'type': 'string'}}, 'required': ['query']}}}, {'type': 'function', 'function': {'name': 'sql_db_schema', 'description': 'Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: table1, table2, table3', 'parameters': {'type': 'object', 'properties': {'table_names': {'description': "A comma-separated list of the table names for which to return the schema. Example input: 'table1, table2, table3'", 'type': 'string'}}, 'required': ['table_names']}}}, {'type': 'function', 'function': {'name': 'sql_db_list_tables', 'description': 'Input is an empty string, output is a comma-separated list of tables in the database.', 'parameters': {'type': 'object', 'properties': {'tool_input': {'description': 'An empty string', 'default': '', 'type': 'string'}}}}}, {'type': 'function', 'function': {'name': 'sql_db_query_checker', 'description': 'Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!', 'parameters': {'type': 'object', 'properties': {'query': {'description': 'A detailed and SQL query to be checked.', 'type': 'string'}}, 'required': ['query']}}}]})
| OpenAIToolsAgentOutputParser(), input_keys_arg=['input'], return_keys_arg=['output'], stream_runnable=True), tools=[QuerySQLDataBaseTool(description="Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.", db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x7ffa60b65350>), InfoSQLDatabaseTool(description='Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: table1, table2, table3', db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x7ffa60b65350>), ListSQLDatabaseTool(db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x7ffa60b65350>), QuerySQLCheckerTool(description='Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!', db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x7ffa60b65350>, llm=ChatOpenAI(client=<openai.resources.chat.completions.Completions object at 0x7ffa4aa815d0>, async_client=<openai.resources.chat.completions.AsyncCompletions object at 0x7ffa4aa853d0>, root_client=<openai.OpenAI object at 0x7ffa4b263ed0>, root_async_client=<openai.AsyncOpenAI object at 0x7ffa4aa81790>, temperature=0.0, openai_api_key=SecretStr('**********'), openai_proxy=''), llm_chain=LLMChain(prompt=PromptTemplate(input_variables=['dialect', 'query'], template='\n{query}\nDouble check the {dialect} query above for common mistakes, including:\n- Using NOT IN with NULL values\n- Using UNION when UNION ALL should have been used\n- Using BETWEEN for exclusive ranges\n- Data type mismatch in predicates\n- Properly quoting identifiers\n- Using the correct number of arguments for functions\n- Casting to the correct data type\n- Using the proper columns for joins\n\nIf there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.\n\nOutput the final SQL query only.\n\nSQL Query: '), llm=ChatOpenAI(client=<openai.resources.chat.completions.Completions object at 0x7ffa4aa815d0>, async_client=<openai.resources.chat.completions.AsyncCompletions object at 0x7ffa4aa853d0>, root_client=<openai.OpenAI object at 0x7ffa4b263ed0>, root_async_client=<openai.AsyncOpenAI object at 0x7ffa4aa81790>, temperature=0.0, openai_api_key=SecretStr('**********'), openai_proxy='')))])
èŠãŠã®éããçµæ§è€éãªãã€ãã©ã€ã³ãcreate_sql_agent颿°äžã€ã§å®çŸ©ã§ããŠããŸããäžèšã®å®çŸ©ã¯LangChainç¹æã®ãã§ãŒã³å®çŸ©ãã®ãã®ã§ããLangChainã§ã¯æœè±¡åãããæ§ã ãªé¢æ°ãäžèšã®ããã«ãã€ã(|)ã§ç¹ããŠãããã€ãã©ã€ã³ãå®çŸ©ããŸãã
ããªãã¡ãAgentã®äžèº«ã¯ãã§ãŒã³å®çŸ©ãšããããšã«ãªããcreate_sql_agent颿°ã䜿ã£ãŠAgentãå®çŸ©ãããšSQLããŒã¿ããŒã¹ãšLLMãçµã¿åãããŠå©çšããããã®ãã§ãŒã³å®çŸ©ãèªåçã«å®çŸ©ããããã®AgentãSQLDatabaseAgentãšåŒãã§ããŸãã
ãã§ãŒã³ã®äžã§å®è¡ãããŠãã颿°ã®èª¬æã¯ä»¥äžã®éãã§ãã
- AgentExecutor : ãšãŒãžã§ã³ãã®å®è¡è ã§ãSQL Agent Executor ãšããååãä»ããããŠããŸãããšãŒãžã§ã³ãã®åäœã管çããã¿ã¹ã¯ãå®è¡ããŸããverbose=True ã«ãããåŠçå 容ã詳现ã«è¡šç€ºãããŸãã
- RunnableMultiActionAgent : è€æ°ã®ã¢ã¯ã·ã§ã³ãå®è¡ãããšãŒãžã§ã³ããèªç¶èšèªã®è³ªåãSQLã¯ãšãªã«å€æããããã«ãRunnableAssign ãšãããããã³ã°ããŒã«ã䜿çšããŸãã
- RunnableLambda : ãšãŒãžã§ã³ããå éšã®äžéçµæã圢åŒåãããããOpenAIããŒã«ã¡ãã»ãŒãžã«é©ãã圢åŒã«å€æããããã«äœ¿ãããŸãã
- ChatPromptTemplate : ãšãŒãžã§ã³ãã«äžããããããã³ããïŒåœä»€æïŒãå ã«ãå ¥åããã質åãåŠçããŸãããã®ããã³ããã«ã¯ããšãŒãžã§ã³ããSQLã¯ãšãªãäœæããããŒã¿ããŒã¹ãšããåãããéã®æé ãå¶çŽãèšè¿°ãããŠããŸãã
- RunnableBinding : LLMïŒå€§èŠæš¡èšèªã¢ãã«ïŒã«å¯ŸããŠãã€ã³ãã£ã³ã°ãè¡ããSQLã¯ãšãªã®çæããã§ãã¯ãæ åœããŸããç¹å®ã®ããŒã«ã«ã¢ã¯ã»ã¹ããŠSQLã¯ãšãªãå®è¡ãããã®çµæãåŠçããŸãã
å颿°ã®äžèº«ãèŠãŠã¿ããšãååŠçã«æé©ãªããã³ãããèªåçã«å®çŸ©ãããŠããããæ§ã ãªåŒæ°ã«å€ãå ¥ããããŠããããšãããããŸããäžçªããããããChatPromptTemplateã®æç€ºæãåèš³ããŠã¿ããšä»¥äžã®éãã§ãã
ãããªã㯠SQL ããŒã¿ããŒã¹ãšå¯Ÿè©±ããããã«èšèšããããšãŒãžã§ã³ãã§ããå ¥å質åãäžããããå Žåãå®è¡ããæ§æçã«æ£ãã SQLite ã¯ãšãªãäœæããã¯ãšãªã®çµæã確èªããŠçããè¿ããŸãããŠãŒã¶ãŒãç¹å®ã®æ°ã®äŸãæå®ããªãéããååŸãããå Žåã¯ãåžžã«ã¯ãšãªã®çµæãæå€§ 10 ä»¶ã«å¶éããŠãã ããã
é¢é£ããåããšã«çµæãäžŠã¹æ¿ããããšã§ãããŒã¿ããŒã¹å ã®æãè峿·±ãäŸãè¿ãããšãã§ããŸããç¹å®ã®ããŒãã«ã®ãã¹ãŠã®åã«å¯ŸããŠã¯ãšãªãå®è¡ããã®ã§ã¯ãªããã¯ãšãªã®ã¿ãèŠæ±ããŸãã質åã«é¢é£ããåãããŒã¿ããŒã¹ãšå¯Ÿè©±ããããã®ããŒã«ã«ã¢ã¯ã»ã¹ã§ããŸãã以äžã®ããŒã«ã®ã¿ã䜿çšããŠãã ããã
以äžã®ããŒã«ããè¿ãããæ å ±ã¯ãæçµçãªåçãäœæããå Žåã«ã®ã¿äœ¿çšããŠãã ãããã¯ãšãªãå®è¡ããåã«ãã¯ãšãªãå確èªããå¿ èŠããããŸããã¯ãšãªã®å®è¡äžã«ãšã©ãŒãçºçããå Žåã¯ãã¯ãšãªãæžãçŽããŠå詊è¡ããŠãã ããã
ããŒã¿ããŒã¹ã«å¯Ÿã㊠DML ã¹ããŒãã¡ã³ã (INSERTãUPDATEãDELETEãDROP ãªã©) ãäœæããªãã§ãã ããã質åãæ£ãããªãå Žåã¯ãããŒã¿ããŒã¹ã«é¢é£ããå Žåã¯ãåçãšããŠãããããŸããããè¿ããŠãã ãããã
ãã€ãã©ã€ã³ã«å ¥ã£ãŠããæç€ºæãèªãã ãã§ããªããšãªãåŠçã®æµããããããŸãããšããããšã§å®éã«èªç¶èšèªã§ã¯ãšãªãå®è¡ããŠã¿ãŸãã
agent_executor.invoke("ã¢ãŒãã£ã¹ãã¯äœäººç»é²ãããŠããŸããïŒæ¥æ¬èªã§åçããŠãã ããã")
ãããšãäžèšã®ããã«Agentã«ããèå¯ãå§ãŸããæçµçã«ããã¹ããçæãããŸãã
> Entering new SQL Agent Executor chain...
Invoking: `sql_db_list_tables` with `{}`
Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, Track
Invoking: `sql_db_query` with `{'query': 'SELECT COUNT(*) AS total_artists FROM Artist'}`
[(275,)]ããŒã¿ããŒã¹ã«ã¯275人ã®ã¢ãŒãã£ã¹ããç»é²ãããŠããŸãã
> Finished chain.
{'input': 'ã¢ãŒãã£ã¹ãã¯äœäººç»é²ãããŠããŸããïŒæ¥æ¬èªã§åçããŠãã ããã',
'output': 'ããŒã¿ããŒã¹ã«ã¯275人ã®ã¢ãŒãã£ã¹ããç»é²ãããŠããŸãã'}
ããŒã¿ããŒã¹ã®ã¹ããŒãæ å ±ããå ¥åãããããã³ããã«é¢ä¿ãã衚ArtistãèŠã€ãã人æ°ãã«ãŠã³ãããããã®SQLãçæããŠããããšãããããŸããSQLã®çµæ([(275,)])ã確èªã§ããæçµçã«outputãçæãããããã¹ããšããããšã«ãªããŸãã
念ã®ããçãåããããããšä»¥äžã®ããã«æ£è§£ãšããããšã«ãªããŸããã
print(db.run("SELECT count(*) FROM artist;"))
[(275,)]
ãã¯ãã«ããŒã¿ããŒã¹ã䜿ã£ãSQLçæç²ŸåºŠã®åäž
äžã€åã®ç« ã§ç޹ä»ããå®è£ ã¯éåžžã«ç°¡åãªã¹ããŒããªã®ã§LLMãSQL倿ããã¹ããå¯èœæ§ã¯äœããšæããŸãããã ãå®ã·ã¹ãã ã§ã¯èšå€§ãªè¡šæ°ãã¬ã³ãŒãæ°ã衚æãã«ã©ã åã衚ã®é¢é£æ§ãªã©éåžžã«è€éãªã¹ããŒãã«ãªãããã®ãããªå Žåãå®ã¯ããŸãç®çã®SQLã«å€æããããæçµçã«æ£ããå¿çããã¹ããçæãããªãã±ãŒã¹ãå€ã çºçããŸãã
ããã§å°ãã§ãSQLã®å€æç²ŸåºŠãäžããããã«ãäºããããã質å(ããè€éãªSQLã«ãªããããªè³ªå)ãšãããã«å¯Ÿå¿ããSQLãexampleãšããŠçšæããŠããããŠãŒã¶ãŒããã®è³ªåãå ¥åãããéã«ãLLMããã®exampleãåç §ããªããSQLãçæãããšããã¢ãŒããã¯ãã£ãå®è£ ããããšã«ãããã®åé¡ã解決ããŸããäžå³ã®éããèµ€æ ã§å²ã£ãéšåã®å®è£ ã远å ããã€ã¡ãŒãžã§ãã
ããã¯ããšããšLLMãæµè¡ãå§ããåæã®ããããããFew-shot Promptingãšããtips(質åãšåçã®ãµã³ãã«ãäºãæºåããŠããæ¹æ³)ãSQL倿ã«å¿çšãããã¿ãŒã³ã§ãã(â»Few-shot Promptingã¯ç°¡åã«å¿çããã¹ãã®ç²ŸåºŠãå€§å¹ ã«åäžãããããšãã§ãããšèªç¥ãããŠããææ³ã§ãã)ãã®éããã¡ãããexampleã«ã¯æ²¢å±±ã®ãã¿ãŒã³ãç»é²ãããŠããæ¹ã粟床ã¯äžãããããã§ããã§ãããå€ããããš
- LLMã«å ¥åããããŒã¯ã³æ°ãå¢ãã
- åŠçãé ããªã
- ç¡é§ã«èª²éãå¢ãã
- ã³ã³ããã¹ããŠã£ã³ããŠã«åãŸããªããªãå¯èœæ§ããã(ç¹ã«å€ãLLM)
- é¢é£æ§ã®äœãSQL exampleã¯ãã«ã·ããŒã·ã§ã³ãåŒãèµ·ããå¯èœæ§ããã
ãªã©ã®åé¡ãçºçããŸããåŸã£ãŠãã§ããã°ãæãå¯èœæ§ã®é«ãSQL Exampleã®ã¿ãLLMã«å ¥åãããããšããã§ããããã§ç»å Žããã®ããã¯ãã«ããŒã¿ããŒã¹ã§ããSQL Exampleããã¯ãã«ããŒã¿ããŒã¹ã«ããŒãããŠãããããSQL Exampleãå¿ èŠã«ãªã£ããšãã«ããã¯ãã«ããŒã¿ããŒã¹ã®é¡äŒŒæ€çŽ¢ã§è³ªåå 容ãšè¿ããSQL Exampleã®ã¿ãæœåºããLLMã«å ¥åãããšããä»çµã¿ã远å ã§å®è£ ããŸãããã®å®è£ ã«ããäžè¿°ããåé¡ãå šãŠè§£æ±ºããŸããéåžžã«ã¹ããŒããªé¡äŒŒæ€çŽ¢ã®äœ¿ãæ¹ã ãšæããŸãã
ã³ãŒãæŠèª¬
æ§æãšããŠã¯äžã€åã®ç« ã®æ§æã«ãã¯ãã«ããŒã¿ããŒã¹ã远å ããå®è£ ã«ãªããååéšåã®ã³ãŒãã¯å šãåãã§ãã
- LLM : OpenAI
- SQL Database : SQLite
- ãã¯ãã«ããŒã¿ããŒã¹ : FAISS
- Dataset : èåãªé³æ¥œã¢ãŒãã£ã¹ãåãšãªãªãŒã¹ã¢ã«ãã ã®ã¹ããŒã
APIããŒã®å®çŸ©
import getpass
import os
def _set_if_undefined(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"Please provide your {var}")
_set_if_undefined("OPENAI_API_KEY")
_set_if_undefined("LANGCHAIN_API_KEY")
# Optional, add tracing in LangSmith
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = "Database Agent"
DBã®å®çŸ©
from langchain_community.utilities import SQLDatabase
db = SQLDatabase.from_uri("sqlite:///Chinook.db", sample_rows_in_table_info=3)
ãšãŒãžã§ã³ã(SQLDatabaseAgent)ã®å®çŸ©
from langchain_community.agent_toolkits import create_sql_agent
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
agent_executor = create_sql_agent(llm, db=db, agent_type="openai-tools", verbose=True)
ãããŸã§ã®ã³ãŒãã¯åã®ç« ã®ãã®ãšå šãåãã§ãã
ããããäžèšã®ããã«7ã€ã®SQLäŸãäœæããŸãã
examples = [
{"input": "ãã¹ãŠã®ã¢ãŒãã£ã¹ãããªã¹ãã¢ããããŠãã ããã", "query": "SELECT * FROM Artist;"},
{
"input": "ã¢ãŒãã£ã¹ã 'AC/DC' ã®ãã¹ãŠã®ã¢ã«ãã ãèŠã€ããŠãã ããã",
"query": "SELECT * FROM Album WHERE ArtistId = (SELECT ArtistId FROM Artist WHERE Name = 'AC/DC');",
},
{
"input": "ãžã£ã³ã«ã 'Rock' ã®ãã¹ãŠã®ãã©ãã¯ããªã¹ãã¢ããããŠãã ããã",
"query": "SELECT * FROM Track WHERE GenreId = (SELECT GenreId FROM Genre WHERE Name = 'Rock');",
},
{
"input": "ãã¹ãŠã®ãã©ãã¯ã®ç·åçæéãæ±ããŠãã ããã",
"query": "SELECT SUM(Milliseconds) FROM Track;",
},
{
"input": "IDã5ã®ã¢ã«ãã ã«ã¯ãã©ãã¯ãäœæ²ãããŸããïŒ",
"query": "SELECT COUNT(*) FROM Track WHERE AlbumId = 5;",
},
{
"input": "5å以äžã®ãã©ãã¯ããªã¹ãã¢ããããŠãã ããã",
"query": "SELECT * FROM Track WHERE Milliseconds > 300000;",
},
{
"input": "2000幎ã®ã¢ã«ãã ã¯ã©ãã§ããïŒ",
"query": "SELECT * FROM Album WHERE strftime('%Y', ReleaseDate) = '2000';",
},
]
ãã¯ãã«ããŒã¿ããŒã¹ãå®çŸ©ããŠãããŸããäžèšã³ãŒãã§ã¯äžèšã®exampleãOpenAIã®åã蟌ã¿ã¢ãã«ã§ãã¯ãã«ã«å€æããFAISSã«ããŒãããŠããŸããå ¥å質åæç« ã®äžã®"input"ã«å¯Ÿå¿ãããã£ãŒã«ããããŒãšããŠãé¡äŒŒæ€çŽ¢ãããæãé¡äŒŒåºŠã®é«ãäžäœ3ä»¶ãçµæãšããŠååŸããããSemanticSimilarityExampleSelectorã§å®çŸ©ããŠããŸãã
from langchain_community.vectorstores import FAISS
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_openai import OpenAIEmbeddings
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
FAISS,
k=3,
input_keys=["input"],
)
ããããã質åæç« ãããšã«å¹æçãªããã³ãã(ããã³ãããã³ãã¬ãŒã)ãæ§ç¯ããããã®æ§ã ãªããŒã«ãã€ã³ããŒãããæç€ºæã远å ããªããæçµçãªããã³ãããå®çŸ©ããŠããŸãã
from langchain_core.prompts import (
ChatPromptTemplate,
FewShotPromptTemplate,
MessagesPlaceholder,
PromptTemplate,
SystemMessagePromptTemplate,
)
system_prefix = """
You are an agent designed to interact with a SQL database. Given an input question, create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer.
Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most {top_k} results. You can order the results by a relevant column to return the most interesting examples in the database.
Never query for all the columns from a specific table, only ask for the relevant columns given the question.
You have access to tools for interacting with the database. Only use the given tools. Only use the information returned by the tools to construct your final answer.
You MUST double check your query before executing it.
If you get an error while executing a query, rewrite the query and try again.
DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database. If the question does not seem related to the database, just return "I don't know" as the answer.
Here are some examples of user inputs and their corresponding SQL queries:
"""
few_shot_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=PromptTemplate.from_template(
"User input: {input}\nSQL query: {query}"
),
input_variables=["input", "dialect", "top_k"],
prefix=system_prefix,
suffix="",
)
ã€ã³ããŒãããŠããã¯ã©ã¹ã®èª¬æã¯äžèšã®éãã§ããããã®å®è£ ã§ããŒã«ãªãã®ã¯ãã¯ãã«ããŒã¿ããŒã¹ã«ããŒãæžã®SQLäŸãå«ããŠããã³ãããçæããFewShotPromptTemplateã®éšåã§ãã
- ChatPromptTemplate : ãã£ãã圢åŒã®ããã³ãããå®çŸ©ããããã®ãã³ãã¬ãŒãã¯ã©ã¹ã§ãããŠãŒã¶ãŒããã®å ¥åãã·ã¹ãã ããã®ã¡ãã»ãŒãžãå«ãäžé£ã®ã¡ãã»ãŒãžã®æµããå®çŸ©ããããã«äœ¿çšãããŸãã
- FewShotPromptTemplate : Few-shot learning(ãã®ã³ãŒãã®å ŽåSQLäŸ)ã«åºã¥ãããã³ãããã³ãã¬ãŒããäœæããããã®ã¯ã©ã¹ã§ãã
- MessagesPlaceholder : ãã£ããã®æµãã®äžã§åçã«çæãããã¡ãã»ãŒãžãæ ŒçŽããããã®å Žæã確ä¿ããŸãã
- PromptTemplate : äžè¬çãªããã³ãããã³ãã¬ãŒããå®çŸ©ããããã®ã¯ã©ã¹ã§ãã
- SystemMessagePromptTemplate : ã·ã¹ãã ã¡ãã»ãŒãžçšã®ããã³ãããã³ãã¬ãŒããäœæããããã®ã¯ã©ã¹ã§ããã·ã¹ãã ãããŠãŒã¶ãŒãžã®æç€ºãèæ¯æ å ±ãå«ãã¡ãã»ãŒãžãå®çŸ©ããŸãã
ãããŠã远å ãããŠããæç€ºæ(system_prefix)ã®åèš³ã以äžã«ãªããŸãã
ãããªãã¯ãSQL ããŒã¿ããŒã¹ãšå¯Ÿè©±ããããã«èšèšããããšãŒãžã§ã³ãã§ããå ¥å質åãäžããããå Žåãæ§æçã«æ£ãã {dialect} ã¯ãšãªãäœæããŠå®è¡ããã¯ãšãªã®çµæã確èªããŠåçãè¿ããŸãã
ãŠãŒã¶ãŒãååŸãããäŸã®ç¹å®ã®æ°ãæå®ããªãéããã¯ãšãªã¯åžžã«æå€§ {top_k} ä»¶ã®çµæã«å¶éããŠãã ãããé¢é£ããåããšã«çµæãäžŠã¹æ¿ããŠãããŒã¿ããŒã¹å ã®æãè峿·±ãäŸãè¿ãããšãã§ããŸããç¹å®ã®ããŒãã«ã®ãã¹ãŠã®åãã¯ãšãªããã®ã§ã¯ãªãã質åã«å¿ããŠé¢é£ããåã®ã¿ãèŠæ±ããŸãã
ããŒã¿ããŒã¹ãšå¯Ÿè©±ããããã®ããŒã«ã«ã¢ã¯ã»ã¹ã§ããŸããæå®ãããããŒã«ã®ã¿ã䜿çšããŠãã ãããããŒã«ããè¿ãããæ å ±ã¯ãæçµçãªçããäœæããããã«ã®ã¿äœ¿çšããŠãã ãããã¯ãšãªãå®è¡ããåã«ãã¯ãšãªãå確èªããå¿ èŠããããŸããã¯ãšãªã®å®è¡äžã«ãšã©ãŒãçºçããå Žåã¯ãã¯ãšãªãæžãçŽããŠå詊è¡ããŠãã ãããããŒã¿ããŒã¹ã«å¯Ÿã㊠DML ã¹ããŒãã¡ã³ã (INSERTãUPDATEãDELETEãDROP ãªã©) ãå®è¡ããªãã§ãã ããã
質åãããŒã¿ããŒã¹ã«é¢é£ããŠããªããšæãããå Žåã¯ãåã«ãããããŸããããšçããŠãã ããã
ãŠãŒã¶ãŒå ¥åãšããã«å¯Ÿå¿ãã SQL ã¯ãšãªã®äŸãããã€ã瀺ããŸããã
æç€ºææåŸã®è¡ã®ããŠãŒã¶ãŒå ¥åãšããã«å¯Ÿå¿ãã SQLã¯ãšãªã®äŸãããã€ã瀺ããŸãããããã¯ãã«ããŒã¿ããŒã¹ããååŸããSQLã®exampleãåç §ããåŠçã®æç€ºã§ãã
ããã«å¯Ÿå¿ããããã«ãäºåã«å®çŸ©ããexample_selectorã䜿ã£ãŠSQLäŸããã¯ãã«ããŒã¿ããŒã¹ããååŸãããã³ããã«å«ããåŠçãFewShotPromptTemplateã§å®çŸ©ããŠãããšããã®ãäžèšã®ã³ãŒãã®æŠèŠã§ãã
ãããŠãããããæçµçãªããã³ãããã³ãã¬ãŒãã®å®çŸ©ã«ãªããŸãã
full_prompt = ChatPromptTemplate.from_messages(
[
SystemMessagePromptTemplate(prompt=few_shot_prompt),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
]
)
äžèšã³ãŒãã¯ãChatPromptTemplate ã䜿çšããŠããã£ãã圢åŒã®ããã³ãããå®çŸ©ããŠããŸããå ·äœçã«ã¯ãSystemMessagePromptTemplateã人éã®ã¡ãã»ãŒãžããããŠãšãŒãžã§ã³ãã®ã¹ã¯ã©ãããããã¡ãã»ãŒãžã®ãã¬ãŒã¹ãã«ããŒãçµã¿åãããŠããã£ããã®æµããèšèšããŠãããšãããæ±ºãŸãã®ã³ãŒãã§ãã
ãããŸã§ã§å®è£ ã¯å®äºã§ãã®ã§è³ªåæç« ãå ¥åããããšæããŸãã
agent.invoke({"input": "10æä»¥äžã®ã¢ã«ãã ããªãªãŒã¹ããŠããã¢ãŒãã£ã¹ãã®ååããªã¹ãããŠãã ããã"})
> Entering new SQL Agent Executor chain...
Invoking: `sql_db_query` with `{'query': 'SELECT Artist.Name FROM Artist JOIN Album ON Artist.ArtistId = Album.ArtistId GROUP BY Artist.Name HAVING COUNT(Album.AlbumId) >= 10'}`
[('Deep Purple',), ('Iron Maiden',), ('Led Zeppelin',), ('Metallica',), ('U2',)]10æä»¥äžã®ã¢ã«ãã ããªãªãŒã¹ããŠããã¢ãŒãã£ã¹ãã®ååã¯ãDeep PurpleãIron MaidenãLed ZeppelinãMetallicaãU2ã§ãã
> Finished chain.
{'input': '10æä»¥äžã®ã¢ã«ãã ããªãªãŒã¹ããŠããã¢ãŒãã£ã¹ãã®ååããªã¹ãããŠãã ããã',
'output': '10æä»¥äžã®ã¢ã«ãã ããªãªãŒã¹ããŠããã¢ãŒãã£ã¹ãã®ååã¯ãDeep PurpleãIron MaidenãLed ZeppelinãMetallicaãU2ã§ãã'}
以äžããã¯ãã«ããŒã¿ããŒã¹ãšçµã¿åããããã¿ãŒã³ã®å®è£ ãšãªããŸãã
å ã¿ã«ããã®å®è£ ã§ã¯RDBã¯SQLiteããã¯ãã«DBã¯FAISSãå©çšããŠããŸãããOracle DB23aiã§ã¯RDBãšãã¯ãã«DBã®äž¡æ¹ã¯ãŒã¯ããŒãããµããŒãããŠãããããããŒã¿ããŒã¹ãäžã€ã«çºãããããšããã¡ãªããããããŸãã


