이 튜토리얼에서는 LangChain 에이전트를 사용하여 SQL 데이터베이스에 대한 질문에 답변할 수 있는 에이전트를 구축하는 방법을 배웁니다.높은 수준에서 에이전트는 다음 작업을 수행합니다:
1
데이터베이스에서 사용 가능한 테이블과 스키마 가져오기
2
질문과 관련된 테이블 결정하기
3
관련 테이블의 스키마 가져오기
4
질문과 스키마 정보를 기반으로 쿼리 생성하기
5
LLM을 사용하여 일반적인 실수가 있는지 쿼리 재확인하기
6
쿼리 실행하고 결과 반환하기
7
쿼리가 성공할 때까지 데이터베이스 엔진에서 발생한 오류 수정하기
8
결과를 기반으로 응답 작성하기
SQL 데이터베이스에 대한 Q&A 시스템을 구축하려면 모델이 생성한 SQL 쿼리를 실행해야 합니다. 이 작업에는 본질적인 위험이 있습니다. 에이전트의 필요에 따라 데이터베이스 연결 권한을 항상 가능한 한 좁게 범위를 지정해야 합니다. 이렇게 하면 모델 기반 시스템 구축의 위험을 완전히 제거할 수는 없지만 완화할 수 있습니다.
이 튜토리얼에서는 SQLite 데이터베이스를 생성합니다. SQLite는 설정하고 사용하기 쉬운 경량 데이터베이스입니다. 디지털 미디어 스토어를 나타내는 샘플 데이터베이스인 chinook 데이터베이스를 로드합니다.편의를 위해 공개 GCS 버킷에 데이터베이스(Chinook.db)를 호스팅했습니다.
Copy
import fs from "node:fs/promises";import path from "node:path";const url = "https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db";const localPath = path.resolve("Chinook.db");async function resolveDbPath() { if (await fs.exists(localPath)) { return localPath; } const resp = await fetch(url); if (!resp.ok) throw new Error(`Failed to download DB. Status code: ${resp.status}`); const buf = Buffer.from(await resp.arrayBuffer()); await fs.writeFile(localPath, buf); return localPath;}
에이전트의 SQL 쿼리가 실행되기 전에 의도하지 않은 작업이나 비효율성이 있는지 확인하는 것이 현명할 수 있습니다.LangChain 에이전트는 에이전트 도구 호출에 대한 감독을 추가하기 위해 내장된 human-in-the-loop 미들웨어를 지원합니다. sql_db_query 도구 호출 시 사람의 검토를 위해 일시 중지하도록 에이전트를 구성해 보겠습니다:
에이전트를 실행하면 이제 sql_db_query 도구를 실행하기 전에 검토를 위해 일시 중지됩니다:
Copy
question = "Which genre on average has the longest tracks?"config = {"configurable": {"thread_id": "1"}} for step in agent.stream( {"messages": [{"role": "user", "content": question}]}, config, stream_mode="values",): if "messages" in step: step["messages"][-1].pretty_print() elif "__interrupt__" in step: print("INTERRUPTED:") interrupt = step["__interrupt__"][0] for request in interrupt.value: print(request["description"]) else: pass
Copy
...INTERRUPTED:Tool execution pending approvalTool: sql_db_queryArgs: {'query': 'SELECT g.Name AS Genre, AVG(t.Milliseconds) AS AvgTrackLength FROM Track t JOIN Genre g ON t.GenreId = g.GenreId GROUP BY g.Name ORDER BY AvgTrackLength DESC LIMIT 1;'}
from langgraph.types import Command for step in agent.stream( Command(resume=[{"type": "accept"}]), config, stream_mode="values",): if "messages" in step: step["messages"][-1].pretty_print() elif "__interrupt__" in step: print("INTERRUPTED:") interrupt = step["__interrupt__"][0] for request in interrupt.value: print(request["description"]) else: pass
Copy
================================== Ai Message ==================================Tool Calls: sql_db_query (call_7oz86Epg7lYRqi9rQHbZPS1U) Call ID: call_7oz86Epg7lYRqi9rQHbZPS1U Args: query: SELECT Genre.Name, AVG(Track.Milliseconds) AS AvgDuration FROM Track JOIN Genre ON Track.GenreId = Genre.GenreId GROUP BY Genre.Name ORDER BY AvgDuration DESC LIMIT 5;================================= Tool Message =================================Name: sql_db_query[('Sci Fi & Fantasy', 2911783.0384615385), ('Science Fiction', 2625549.076923077), ('Drama', 2575283.78125), ('TV Shows', 2145041.0215053763), ('Comedy', 1585263.705882353)]================================== Ai Message ==================================The genre with the longest average track length is "Sci Fi & Fantasy" with an average duration of about 2,911,783 milliseconds, followed by "Science Fiction" and "Drama."
createAgent를 사용하여 최소한의 코드로 ReAct 에이전트를 구축합니다. 에이전트는 요청을 해석하고 SQL 명령을 생성합니다. 도구는 명령의 안전성을 확인한 다음 명령을 실행하려고 시도합니다. 명령에 오류가 있으면 오류 메시지가 모델로 반환됩니다. 그러면 모델은 원래 요청과 새 오류 메시지를 검토하고 새 명령을 생성할 수 있습니다. 이는 LLM이 명령을 성공적으로 생성하거나 종료 횟수에 도달할 때까지 계속될 수 있습니다. 이 경우 오류 메시지와 같은 피드백을 모델에 제공하는 패턴은 매우 강력합니다.에이전트의 동작을 사용자 정의하기 위해 설명적인 시스템 프롬프트로 에이전트를 초기화합니다:
Copy
import { SystemMessage } from "langchain";const getSystemPrompt = async () => new SystemMessage(`You are a careful SQLite analyst.Authoritative schema (do not invent columns/tables):${await getSchema()}Rules:- Think step-by-step.- When you need data, call the tool \`execute_sql\` with ONE SELECT query.- Read-only only; no INSERT/UPDATE/DELETE/ALTER/DROP/CREATE/REPLACE/TRUNCATE.- Limit to 5 rows unless user explicitly asks otherwise.- If the tool returns 'Error:', revise the SQL and try again.- Limit the number of attempts to 5.- If you are not successful after 5 attempts, return a note to the user.- Prefer explicit column lists; avoid SELECT *.`);
human: Which genre, on average, has the longest tracks?ai:tool: [{"Genre":"Sci Fi & Fantasy","AvgMilliseconds":2911783.0384615385}]ai: Sci Fi & Fantasy — average track length ≈ 48.5 minutes (about 2,911,783 ms).
에이전트는 쿼리를 올바르게 작성하고, 쿼리를 확인하고, 실행하여 최종 응답을 알렸습니다.
위 실행의 모든 측면(수행된 단계, 호출된 도구, LLM이 본 프롬프트 등)을 LangSmith 트레이스에서 검사할 수 있습니다.
Studio는 “클라이언트 측” 루프와 메모리를 제공하므로 이를 채팅 인터페이스로 실행하고 데이터베이스를 쿼리할 수 있습니다. “데이터베이스 스키마를 알려주세요” 또는 “상위 5명의 고객에 대한 인보이스를 보여주세요”와 같은 질문을 할 수 있습니다. 생성된 SQL 명령과 결과 출력이 표시됩니다. 시작 방법에 대한 자세한 내용은 아래를 참조하세요.