延續上一篇使用大型語言模型(LLM)完成Text-to-SQL任務 ,在開發大型語言模型的應用程式時,可以採用像LangChain一樣的開源框架。LangChain目的在幫助開發者建立大型語言模型的應用程式,縮短開發時間並更容易的實現各種複雜的大型語言模型應用程式。
準備資料庫 一開始先建立Chinook這個資料庫來當作範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import sqlite3import requestsurl = "https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql" response = requests.get(url) with open ('Chinook_Sqlite.sql' , 'w' ) as f: f.write(response.text) conn = sqlite3.connect('Chinook.db' ) cursor = conn.cursor() with open ('Chinook_Sqlite.sql' , 'r' ) as f: cursor.executescript(f.read()) conn.commit() conn.close()
接著使用langchain的SQLDatabase來建立資料庫的查詢引擎
1 2 3 4 5 6 7 from langchain_community.utilities import SQLDatabasedb = SQLDatabase.from_uri("sqlite:///Chinook.db" ) print (db.dialect)print (db.get_usable_table_names())db.run("SELECT * FROM Artist LIMIT 10;" )
1 2 sqlite [(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')]
準備大型語言模型服務 再來準備一個大型語言模型服務,這裡使用Gemini當範例
1 2 3 4 5 6 7 8 9 10 import osimport getpassimport google.generativeai as genaiGEMINI_API_KEY = getpass.getpass() genai.configure(api_key=GEMINI_API_KEY) llm = genai.GenerativeModel('gemini-1.5-flash' )
使用LangChain 在LangChain中「Chain」是代表一組有順序執行的步驟或是一系列連接在一起的組件,可以把Chain視為一個工作流程,其中每個步驟都由一個特定的組件負責。每一個組件可能是語言模型、資料來源或是API,這些組件經由連接後可以形成一個完整的應用程式,進而建構更複雜模型推論應用。
使用create_sql_query_chain 準備好資料庫和大型語言模型服務後,透過create_sql_query_chain建立一個SQL查詢的Chain,這個Chain能夠在給定的資料庫下生成SQL查詢。前面使用的SQLDatabase則可以在這裡用來查詢資料庫類型和調用get_table_info來取得欄位資訊。
1 2 3 4 5 from langchain.chains import create_sql_query_chainchain = create_sql_query_chain(llm, db) response = chain.invoke({"question" : "How many employees are there" }) print (f"response: {response} " )
這裡使用到的create_sql_query_chain會把使用者輸入的文字轉成以下的SQL查詢
1 response: SQLQuery: SELECT COUNT(*) FROM Employee
接著可以詠唱一段解析程式把SQL取出來後對資料庫下查詢。
1 2 3 4 5 6 7 8 9 10 11 12 import redef parse_sql (answer ): match = re.search(r"SQLQuery: (.*)" , answer) if match: return match.group(1 ) else : return None sql_str = parse_sql(response) db.run(sql_str)
Chain起生成SQL到下查詢 再來可以把這些動作都Chain起來,步驟會是(1) 將輸入問題透過LLM生成出SQL查詢 (2) 從生成結果解析出SQL (3) 對資料庫下SQL查詢
1 2 3 4 5 6 7 8 from langchain_community.tools.sql_database.tool import QuerySQLDataBaseToolfrom langchain_core.runnables import RunnableLambdaexecute_query = QuerySQLDataBaseTool(db=db, verbose=True ) write_query = create_sql_query_chain(llm, db) chain = write_query | RunnableLambda(lambda x: parse_sql(x)) | execute_query chain.invoke({"question" : "How many employees are there" })
把查詢結果轉換成人話 到這麼已經可以自動化的從輸入問題到生成結果並查詢資料庫,這時如果不想讓Agent只有回覆查詢數字,就可以把資料庫的查詢結果再丟給LLM生成出像人一樣的回答。
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 from operator import itemgetterfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.prompts import PromptTemplatefrom langchain_core.runnables import RunnablePassthroughanswer_prompt = PromptTemplate.from_template( """Given the following user question, corresponding SQL query, and SQL result, answer the user question. Question: {question} SQL Query: {query} SQL Result: {result} Answer: """ ) answer = answer_prompt | llm | StrOutputParser() chain = ( RunnablePassthrough .assign(query=write_query) .assign(result=itemgetter("query" ) | RunnableLambda(lambda x: parse_sql(x)) | execute_query ) | answer ) chain.invoke({"question" : "How many employees are there" })
從answer_prompt可以看到把question、query、result組成prompt後,就可以請LLM生成一個答案。所以完整的步驟會有四個 (1) 將輸入問題透過LLM生成出SQL查詢 (2) 從生成結果解析出SQL (3) 對資料庫下SQL查詢 (4) 將查詢結果透過LLM生成自然語言回答
LangChain提供了開發大型語言模型所需要的組件,透過上面的範例可以快速的建立Text-to-SQL核心功能,加速開發大型語言應用程式。
參考資料: Build a Question/Answering system over SQL data