인프런 커뮤니티 질문&답변

Seongc좋은ᅵᅵheol Kim님의 프로필 이미지

작성한 질문수

모두를 위한 대규모 언어 모델 LLM(Large Language Model) Part 2 - 랭체인(LangChain)으로 나만의 ChatGPT 만들기

랭체인(LangChain) SQL Agents 예제를 통해 Text-To-SQL 심화예제 살펴보기

SQL 에이전트 제작 예제 프로그램과 관련하여 질문 드립니다.

24.09.20 11:22 작성

·

38

0

db = SQLDatabase.from_uri("sqlite:///chinook.db")

write_query = create_sql_query_chain(llm, db)
execute_query = QuerySQLDataBaseTool(db=db)
answer_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: """
)

print("write_query : ", write_query)

answer = answer_prompt | llm | StrOutputParser()

chain = (
    RunnablePassthrough.assign(query=write_query).assign(
        result=itemgetter("query") | execute_query
    )
    | answer
)

이 부분에서 chain 과 관련한 동작을 좀 더 상세히 설명 부탁드릴 수 있는지요?

답변 2

0

AISchool님의 프로필 이미지
AISchool
지식공유자

2024. 09. 21. 00:43

안녕하세요~. 반갑습니다.

해당 chain의 단계별 동작과정을 살펴보면 아래와 같습니다.

 

  1. 아래와 같은 프롬프트로 question 대응되는 적절한 SQL query문 생성

    You are a SQLite expert. Given an input question, first create a syntactically correct SQLite query to run, then look at the results of the query and return the answer to the input question.
    Unless the user specifies in the question a specific number of examples to obtain, query for at most 5 results using the LIMIT clause as per SQLite. You can order the results to return the most informative data in the database.
    Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (") to denote them as delimited identifiers.
    Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.
    Pay attention to use date('now') function to get the current date, if the question involves "today".
    
    Use the following format:
    
    Question: Question here
    SQLQuery: SQL Query to run
    SQLResult: Result of the SQLQuery
    Answer: Final answer here
    
    Only use the following tables:
    
    CREATE TABLE albums (
    	"AlbumId" INTEGER NOT NULL, 
    	"Title" NVARCHAR(160) NOT NULL, 
    	"ArtistId" INTEGER NOT NULL, 
    	PRIMARY KEY ("AlbumId"), 
    	FOREIGN KEY("ArtistId") REFERENCES artists ("ArtistId")
    )
    
    /*
    3 rows from albums table:
    AlbumId	Title	ArtistId
    1	For Those About To Rock We Salute You	1
    2	Balls to the Wall	2
    3	Restless and Wild	2
    */
    
    
    CREATE TABLE artists (
    	"ArtistId" INTEGER NOT NULL, 
    	"Name" NVARCHAR(120), 
    	PRIMARY KEY ("ArtistId")
    )
    
    /*
    3 rows from artists table:
    ArtistId	Name
    1	AC/DC
    2	Accept
    3	Aerosmith
    */
    
    
    CREATE TABLE customers (
    	"CustomerId" INTEGER NOT NULL, 
    	"FirstName" NVARCHAR(40) NOT NULL, 
    	"LastName" NVARCHAR(20) NOT NULL, 
    	"Company" NVARCHAR(80), 
    	"Address" NVARCHAR(70), 
    	"City" NVARCHAR(40), 
    	"State" NVARCHAR(40), 
    	"Country" NVARCHAR(40), 
    	"PostalCode" NVARCHAR(10), 
    	"Phone" NVARCHAR(24), 
    	"Fax" NVARCHAR(24), 
    	"Email" NVARCHAR(60) NOT NULL, 
    	"SupportRepId" INTEGER, 
    	PRIMARY KEY ("CustomerId"), 
    	FOREIGN KEY("SupportRepId") REFERENCES employees ("EmployeeId")
    )
    
    /*
    3 rows from customers table:
    CustomerId	FirstName	LastName	Company	Address	City	State	Country	PostalCode	Phone	Fax	Email	SupportRepId
    1	Luís	Gonçalves	Embraer - Empresa Brasileira de Aeronáutica S.A.	Av. Brigadeiro Faria Lima, 2170	São José dos Campos	SP	Brazil	12227-000	+55 (12) 3923-5555	+55 (12) 3923-5566	luisg@embraer.com.br	3
    2	Leonie	Köhler	None	Theodor-Heuss-Straße 34	Stuttgart	None	Germany	70174	+49 0711 2842222	None	leonekohler@surfeu.de	5
    3	François	Tremblay	None	1498 rue Bélanger	Montréal	QC	Canada	H2G 1A7	+1 (514) 721-4711	None	ftremblay@gmail.com	3
    */
    
    
    CREATE TABLE employees (
    	"EmployeeId" INTEGER NOT NULL, 
    	"LastName" NVARCHAR(20) NOT NULL, 
    	"FirstName" NVARCHAR(20) NOT NULL, 
    	"Title" NVARCHAR(30), 
    	"ReportsTo" INTEGER, 
    	"BirthDate" DATETIME, 
    	"HireDate" DATETIME, 
    	"Address" NVARCHAR(70), 
    	"City" NVARCHAR(40), 
    	"State" NVARCHAR(40), 
    	"Country" NVARCHAR(40), 
    	"PostalCode" NVARCHAR(10), 
    	"Phone" NVARCHAR(24), 
    	"Fax" NVARCHAR(24), 
    	"Email" NVARCHAR(60), 
    	PRIMARY KEY ("EmployeeId"), 
    	FOREIGN KEY("ReportsTo") REFERENCES employees ("EmployeeId")
    )
    
    /*
    3 rows from employees table:
    EmployeeId	LastName	FirstName	Title	ReportsTo	BirthDate	HireDate	Address	City	State	Country	PostalCode	Phone	Fax	Email
    1	Adams	Andrew	General Manager	None	1962-02-18 00:00:00	2002-08-14 00:00:00	11120 Jasper Ave NW	Edmonton	AB	Canada	T5K 2N1	+1 (780) 428-9482	+1 (780) 428-3457	andrew@chinookcorp.com
    2	Edwards	Nancy	Sales Manager	1	1958-12-08 00:00:00	2002-05-01 00:00:00	825 8 Ave SW	Calgary	AB	Canada	T2P 2T3	+1 (403) 262-3443	+1 (403) 262-3322	nancy@chinookcorp.com
    3	Peacock	Jane	Sales Support Agent	2	1973-08-29 00:00:00	2002-04-01 00:00:00	1111 6 Ave SW	Calgary	AB	Canada	T2P 5M5	+1 (403) 262-3443	+1 (403) 262-6712	jane@chinookcorp.com
    */
    
    
    CREATE TABLE genres (
    	"GenreId" INTEGER NOT NULL, 
    	"Name" NVARCHAR(120), 
    	PRIMARY KEY ("GenreId")
    )
    
    /*
    3 rows from genres table:
    GenreId	Name
    1	Rock
    2	Jazz
    3	Metal
    */
    
    
    CREATE TABLE invoice_items (
    	"InvoiceLineId" INTEGER NOT NULL, 
    	"InvoiceId" INTEGER NOT NULL, 
    	"TrackId" INTEGER NOT NULL, 
    	"UnitPrice" NUMERIC(10, 2) NOT NULL, 
    	"Quantity" INTEGER NOT NULL, 
    	PRIMARY KEY ("InvoiceLineId"), 
    	FOREIGN KEY("TrackId") REFERENCES tracks ("TrackId"), 
    	FOREIGN KEY("InvoiceId") REFERENCES invoices ("InvoiceId")
    )
    
    /*
    3 rows from invoice_items table:
    InvoiceLineId	InvoiceId	TrackId	UnitPrice	Quantity
    1	1	2	0.99	1
    2	1	4	0.99	1
    3	2	6	0.99	1
    */
    
    
    CREATE TABLE invoices (
    	"InvoiceId" INTEGER NOT NULL, 
    	"CustomerId" INTEGER NOT NULL, 
    	"InvoiceDate" DATETIME NOT NULL, 
    	"BillingAddress" NVARCHAR(70), 
    	"BillingCity" NVARCHAR(40), 
    	"BillingState" NVARCHAR(40), 
    	"BillingCountry" NVARCHAR(40), 
    	"BillingPostalCode" NVARCHAR(10), 
    	"Total" NUMERIC(10, 2) NOT NULL, 
    	PRIMARY KEY ("InvoiceId"), 
    	FOREIGN KEY("CustomerId") REFERENCES customers ("CustomerId")
    )
    
    /*
    3 rows from invoices table:
    InvoiceId	CustomerId	InvoiceDate	BillingAddress	BillingCity	BillingState	BillingCountry	BillingPostalCode	Total
    1	2	2009-01-01 00:00:00	Theodor-Heuss-Straße 34	Stuttgart	None	Germany	70174	1.98
    2	4	2009-01-02 00:00:00	Ullevålsveien 14	Oslo	None	Norway	0171	3.96
    3	8	2009-01-03 00:00:00	Grétrystraat 63	Brussels	None	Belgium	1000	5.94
    */
    
    
    CREATE TABLE media_types (
    	"MediaTypeId" INTEGER NOT NULL, 
    	"Name" NVARCHAR(120), 
    	PRIMARY KEY ("MediaTypeId")
    )
    
    /*
    3 rows from media_types table:
    MediaTypeId	Name
    1	MPEG audio file
    2	Protected AAC audio file
    3	Protected MPEG-4 video file
    */
    
    
    CREATE TABLE playlist_track (
    	"PlaylistId" INTEGER NOT NULL, 
    	"TrackId" INTEGER NOT NULL, 
    	PRIMARY KEY ("PlaylistId", "TrackId"), 
    	FOREIGN KEY("TrackId") REFERENCES tracks ("TrackId"), 
    	FOREIGN KEY("PlaylistId") REFERENCES playlists ("PlaylistId")
    )
    
    /*
    3 rows from playlist_track table:
    PlaylistId	TrackId
    1	3402
    1	3389
    1	3390
    */
    
    
    CREATE TABLE playlists (
    	"PlaylistId" INTEGER NOT NULL, 
    	"Name" NVARCHAR(120), 
    	PRIMARY KEY ("PlaylistId")
    )
    
    /*
    3 rows from playlists table:
    PlaylistId	Name
    1	Music
    2	Movies
    3	TV Shows
    */
    
    
    CREATE TABLE tracks (
    	"TrackId" INTEGER NOT NULL, 
    	"Name" NVARCHAR(200) NOT NULL, 
    	"AlbumId" INTEGER, 
    	"MediaTypeId" INTEGER NOT NULL, 
    	"GenreId" INTEGER, 
    	"Composer" NVARCHAR(220), 
    	"Milliseconds" INTEGER NOT NULL, 
    	"Bytes" INTEGER, 
    	"UnitPrice" NUMERIC(10, 2) NOT NULL, 
    	PRIMARY KEY ("TrackId"), 
    	FOREIGN KEY("MediaTypeId") REFERENCES media_types ("MediaTypeId"), 
    	FOREIGN KEY("GenreId") REFERENCES genres ("GenreId"), 
    	FOREIGN KEY("AlbumId") REFERENCES albums ("AlbumId")
    )
    
    /*
    3 rows from tracks table:
    TrackId	Name	AlbumId	MediaTypeId	GenreId	Composer	Milliseconds	Bytes	UnitPrice
    1	For Those About To Rock (We Salute You)	1	1	1	Angus Young, Malcolm Young, Brian Johnson	343719	11170334	0.99
    2	Balls to the Wall	2	2	1	None	342562	5510424	0.99
    3	Fast As a Shark	3	2	1	F. Baltes, S. Kaufman, U. Dirkscneider & W. Hoffman	230619	3990994	0.99
    */
    
    Question: employees는 몇명이 있어?
    SQLQuery:

    생성결과:

    SELECT COUNT("EmployeeId") AS "EmployeeCount" FROM employees;

 

  1. 실제 DB에 위에서 생성된 SQL 쿼리를 실행해서 SQL 쿼리 실행 결과를 받아옴.


    DB에 위에 SELECT 쿼리 실행결과:

[(8,)]
  1. 위에 과정을 종합해서 사람이 이해하기 좋은(human-readable) 최종 답변 생성


    인풋 프롬프트:

    Given the following user question, corresponding SQL query, and SQL result, answer the user question.
    
    Question: employees는 몇명이 있어?
    SQL Query: SELECT COUNT("EmployeeId") AS "EmployeeCount" FROM employees;
    SQL Result: [(8,)]
    Answer:

    최종답변:

    employees 테이블에는 8명의 직원이 있습니다.


    아래 LangSmith 추적을 같이 확인해 보시면 전체 동작과정을 좀더 쉽게 이해하실 수 있을것입니다.

     

     

     

     

     

    https://smith.langchain.com/public/0e3a4659-3b22-4fa2-8b9f-d692533b067f/r

     



    뒤에 챕터에서 LangSmith 추적 설정하는 법을 다루고 있는 부분이 있으니 해당 부분을 참조해서 직접 LangSmith 추적을 추가해서 테스트해보셔도 좋을 것 같습니다.



    좋은 하루되세요.

     

    감사합니다.

0

인프런 AI 인턴님의 프로필 이미지

2024. 09. 20. 11:26

안녕하세요, 인프런 AI 인턴입니다.

Seongcheol Kim님께서 질문해주신 SQL 에이전트 제작 예제에 대한 상세 설명을 드리겠습니다.

해당 부분의 코드는 LLM과 데이터베이스를 연동하여 사용자의 질문에 대해 SQL 쿼리를 실행하고, 그 결과를 기반으로 답변을 생성하는 과정입니다. 각 단계별로 설명을 드리겠습니다:

  1. DB 인스턴스 생성:

    db = SQLDatabase.from_uri("sqlite:///chinook.db")
    

    이 부분은 지정된 URI 경로에 따라 SQLite 데이터베이스를 불러옵니다. chinook.db는 사용될 데이터베이스 파일입니다.

  2. SQL 쿼리 작성 체인 생성:

    write_query = create_sql_query_chain(llm, db)
    

    이 부분은 LLM과 데이터베이스를 연결하여 사용자의 질문을 SQL 쿼리로 변환하는 체인을 생성합니다.

  3. 쿼리 실행 도구 생성:

    execute_query = QuerySQLDataBaseTool(db=db)
    

    이 부분에서는 데이터베이스 인스턴스를 기반으로 실제로 SQL 쿼리를 실행하고 결과를 가져오는 도구를 만듭니다.

  4. 답변 프롬프트 템플릿 생성:

    answer_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: """
    )
    

    이 부분은 최종적으로 사용자가 입력한 질문, 생성된 SQL 쿼리 및 쿼리 결과를 바탕으로 최종 답변을 생성하는 프롬프트 템플릿입니다.

  5. 체인 생성 및 실행:

    chain = (
    RunnablePassthrough.assign(query=write_query).assign(
        result=itemgetter("query") | execute_query
    )
    | answer
    )
    

    이 부분에서는 전체 흐름을 연결합니다:

    • write_query를 생성하고 query로 할당합니다.
    • query를 실행하여 result로 할당하고, 그 값은 execute_query로 처리합니다.
    • 최종적으로 answer_prompt, llm, StrOutputParser를 통해 답변을 생성합니다.

기본적으로, 이 체인은 사용자의 질문 -> SQL 쿼리 작성 -> SQL 쿼리 실행 -> 결과를 바탕으로 답변 작성의 전체 과정을 자동화합니다.

관련된 유사한 질문과 답변은 하단 링크를 참고해 보시기 바랍니다:
- Gpt3.5 FineTuning 을 보고 문의 드립니다.
- self query retriever 한글

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.