LangChain Skills 모드 실전: 필요에 따라 지식을 로드하는 SQL 도우미 구축

2/13/2026
7 min read

이전 글에서 Deep Agents CLI를 통해 Deep Agent가 Skills를 사용하는 모드를 시뮬레이션하는 방법을 살펴보았습니다. 이제 LangChain은 이 기능을 기본적으로 지원하여 개발 프로세스를 크게 간소화했습니다. 이 글에서는 이 기능을 자세히 살펴보고 더 스마트한 SQL 도우미를 구축합니다.

복잡한 AI Agent를 구축할 때 개발자는 종종 딜레마에 빠집니다. 모든 컨텍스트(데이터베이스 테이블 구조, API 문서, 비즈니스 규칙)를 한 번에 System Prompt에 주입하여 컨텍스트 창(Context Window)이 넘치고 모델의 주의력을 분산시킬까요? 아니면 비용이 많이 드는 빈번한 미세 조정(Fine-tuning)을 선택할까요?

**Skills 모드(Skills Pattern)**는 우아한 중간 경로를 제공합니다. 필요한 지식을 동적으로 로드하여 컨텍스트를 효율적으로 활용합니다. LangChain의 이 모드에 대한 기본 지원은 "필요에 따라 학습"할 수 있는 Agent를 더 쉽게 구축할 수 있음을 의미합니다.

이 글에서는 공식 문서 Build a SQL assistant with on-demand skills를 통해 독자가 처음부터 "필요에 따라 지식 로드"를 지원하는 SQL Assistant를 구축하도록 안내합니다.

1. 핵심 개념: Skills 모드를 선택하는 이유는 무엇입니까?

기존 SQL Agent의 한계

기존 SQL Agent 아키텍처에서는 일반적으로 System Prompt에 전체 Database Schema를 제공해야 합니다. 비즈니스가 발전함에 따라 테이블 수가 수백 개로 확장되면 이 방법은 다음과 같은 중요한 문제를 야기합니다.

  • Token 소모가 큼: 대량의 관련 없는 테이블 구조를 매번 대화에 포함하여 리소스 낭비를 초래합니다.

  • 환각 위험 증가: 너무 많은 관련 없는 간섭 정보는 모델의 추론 정확도를 떨어뜨립니다.

  • 유지 관리 어려움: 모든 비즈니스 라인의 지식이 긴밀하게 결합되어 독립적으로 반복하기 어렵습니다.

Skills 모드: 점진적 공개 기반 솔루션

Skills 모드는 점진적 공개(Progressive Disclosure) 원칙을 기반으로 지식 습득 프로세스를 계층화합니다.

  • Agent 초기 상태: 어떤 "기술(Skills)"이 있는지와 간략한 설명(Description)만 파악하여 가볍게 유지합니다.

  • 런타임 로드: 구체적인 문제(예: "재고 조회")에 직면하면 Agent는 도구(load_skill)를 적극적으로 호출하여 해당 기술의 자세한 컨텍스트(Schema + Prompt)를 로드합니다.

  • 작업 실행: 로드된 정확한 컨텍스트를 기반으로 구체적인 작업(예: SQL 작성 및 실행)을 실행합니다.

이 모드는 무한 확장팀 분리를 효과적으로 지원하여 Agent가 점점 더 복잡해지는 비즈니스 시나리오에 적응할 수 있도록 합니다.

2. 시스템 아키텍처 설계

이 실전 프로젝트에서는 두 개의 핵심 Skills를 포함하는 SQL Assistant를 구축하여 이 모드의 실제 적용을 보여줍니다.

  • Sales Analytics(판매 분석):sales_data** 테이블을 담당하며, 수익 통계, 주문 추세 분석 등을 처리합니다.**

  • Inventory Management(재고 관리):inventory_items** 테이블을 담당하며, 재고 수준 모니터링, 위치 조회 등을 처리합니다.**

3. 개발 환경 구축

이 프로젝트에서는 Pythonuv를 사용하여 효율적인 종속성 관리를 수행합니다.

핵심 종속성 설치

uv add langchain langchain-openai langgraph psycopg2-binary python-dotenv langchain-community

PostgreSQL 환경 구성

로컬에서 Postgres 인스턴스를 시작하고agent_platform 데이터베이스를 만듭니다. 테이블 구조와 테스트 데이터를 자동으로 초기화하는setup_db.py 스크립트를 제공합니다(자세한 내용은 글 말미의 소스 코드 참조).

4. 핵심 구현 단계 상세 설명### 1단계: 도메인 기술 정의 (The Knowledge)

기술을 딕셔너리 구조로 정의하여 파일 시스템 또는 데이터베이스에서 로드하는 과정을 시뮬레이션합니다. description (Agent 의사 결정에 사용)과 content (실제로 로드되는 상세 컨텍스트)를 구분하십시오.

SKILLS = {"sales_analytics": {"description":"매출 수익, 추세 분석에 유용...","content":"""... Table Schema: sales_data ..."" },"inventory_management": {"description":"재고 수준 확인에 유용...","content":"""... Table Schema: inventory_items ..."" }}

2단계: 핵심 도구 구현 (The Capabilities)

Agent는 작업을 완료하기 위해 두 가지 핵심 도구에 의존합니다.

  • load_skill(skill_name)**: 런타임에 지정된 기술의 세부 정보를 동적으로 로드합니다. **

  • run_sql_query(query)**: 구체적인 SQL 문을 실행합니다. **

3단계: Agent 로직 편곡 (The Brain)

LangGraph를 사용하여 ReAct Agent를 구축합니다. System Prompt는 여기서 중요한 역할을 하며, Agent가 Identify -> Load -> Query 표준 작업 절차(SOP)를 엄격하게 따르도록 안내합니다.

system_prompt ="""1. Identify the relevant skill.2. Use 'load_skill' to get schema.3. Write and execute SQL using 'run_sql_query'....Do not guess table names. Always load the skill first.""

5. 실행 효과 검증

test_agent.py를 실행하여 Sales 및 Inventory의 두 가지 다른 영역에 대한 쿼리를 테스트했습니다. 다음은 콘솔의 실제 출력 로그로, Agent가 문제에 따라 기술을 동적으로 로드하는 방법을 보여줍니다.

Testing Sales Query...Agent calling tools: [{'name': 'load_skill', 'args': {'skill_name': 'sales_analytics'}, 'id': 'call_f270d76b7ce4404cb5f61bf2', 'type': 'tool_call'}]Tool output:You are a Sales Analytics Expert.You have access to the 'sales_data' table.Table Schema:- id: integer...Agent calling tools: [{'name': 'run_sql_query', 'args': {'query': 'SELECT SUM(amount) as total_revenue FROM sales_data;'}, 'id': 'call_b4f3e686cc7f4f22b3bb9ea7', 'type': 'tool_call'}]Tool output: [(Decimal('730.50'),)]...Agent response: The total revenue is $730.50.Testing Inventory Query...Agent calling tools: [{'name': 'load_skill', 'args': {'skill_name': 'inventory_management'}, 'id': 'call_18c823b2d5064e95a0cfe2e3', 'type': 'tool_call'}]Tool output:You are an Inventory Management Expert.You have access to the 'inventory_items' table.Table Schema...Agent calling tools: [{'name': 'run_sql_query', 'args': {'query': "SELECT warehouse_location FROM inventory_items WHERE product_name = 'Laptop';"}, 'id': 'call_647ee3a444804bd98a045f00', 'type': 'tool_call'}]Tool output: [('Warehouse A',)]...Agent response: The Laptop is located in **Warehouse A**.## 6. 전체 소스 코드 참조

다음은 프로젝트의 전체 소스 코드이며, 데이터베이스 초기화 스크립트와 Agent 주 프로그램을 포함합니다.

1. 데이터베이스 초기화 (setup_db.py)

importpsycopg2frompsycopg2.extensionsimportISOLATION_LEVEL_AUTOCOMMITimportosfromdotenvimportload_dotenvload_dotenv()# .env 파일에 데이터베이스 연결 정보가 구성되어 있는지 확인하십시오.DB_HOST = os.getenv("DB_HOST","localhost")DB_PORT = os.getenv("DB_PORT","5432")DB_USER = os.getenv("DB_USER","postgres")DB_PASSWORD = os.getenv("DB_PASSWORD","your_password")# 실제 비밀번호로 바꾸십시오.DB_NAME = os.getenv("DB_NAME","agent_platform")defcreate_database():try:# 새 DB를 생성하기 위해 기본 'postgres' 데이터베이스에 연결 conn = psycopg2.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, dbname="postgres" ) conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) cur = conn.cursor()# 데이터베이스가 존재하는지 확인 cur.execute(f"SELECT 1 FROM pg_catalog.pg_database WHERE datname = '{DB_NAME}'") exists = cur.fetchone()ifnotexists: print(f"Creating database{DB_NAME}...") cur.execute(f"CREATE DATABASE{DB_NAME}")else: print(f"Database{DB_NAME}already exists.") cur.close() conn.close()exceptExceptionase: print(f"Error creating database:{e}")defcreate_tables_and_data():try: conn = psycopg2.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, dbname=DB_NAME ) cur = conn.cursor()# Sales 테이블 생성 print("Creating sales_data table...") cur.execute(""" CREATE TABLE IF NOT EXISTS sales_data ( id SERIAL PRIMARY KEY, transaction_date DATE, product_id VARCHAR(50), amount DECIMAL(10, 2), region VARCHAR(50) ) """)# Inventory 테이블 생성 print("Creating inventory_items table...") cur.execute(""" CREATE TABLE IF NOT EXISTS inventory_items ( id SERIAL PRIMARY KEY, product_id VARCHAR(50), product_name VARCHAR(100), stock_count INTEGER, warehouse_location VARCHAR(50) ) """)# 모의 데이터 삽입 print("Inserting mock data...") cur.execute("TRUNCATE sales_data, inventory_items") sales_data = [ ('2023-01-01','P001',100.00,'North'), ('2023-01-02','P002',150.50,'South'), ('2023-01-03','P001',120.00,'East'), ('2023-01-04','P003',200.00,'West'), ('2023-01-05','P002',160.00,'North') ] cur.executemany("INSERT INTO sales_data (transaction_date, product_id, amount, region) VALUES (%s, %s, %s, %s)", sales_data ) inventory_data = [ ('P001','Laptop',50,'Warehouse A'), ('P002','Mouse',200,'Warehouse B'), ('P003','Keyboard',150,'Warehouse A'), ('P004','Monitor',30,'Warehouse C') ] cur.executemany("INSERT INTO inventory_items (product_id, product_name, stock_count, warehouse_location) VALUES (%s, %s, %s, %s)", inventory_data ) conn.commit() cur.close() conn.close() print("Database setup complete.")exceptExceptionase: print(f"Error setting up tables:{e}")if__name__ =="__main__": create_database() create_tables_and_data()### 2. Agent 메인 프로그램 (main.py)

`import os from typing import Annotated, Literal, TypedDict, Union, Dict from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage from langchain_community.utilities import SQLDatabase from langchain_community.agent_toolkits import SQLDatabaseToolkit from langgraph.graph import StateGraph, START, END, MessagesState from langgraph.prebuilt import ToolNode, tools_condition

load_dotenv()

--- Configuration ---

BASE_URL = os.getenv("BASIC_MODEL_BASE_URL") API_KEY = os.getenv("BASIC_MODEL_API_KEY") MODEL_NAME = os.getenv("BASIC_MODEL_MODEL") DB_URI = f"postgresql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}"

--- Database Setup ---

db = SQLDatabase.from_uri(DB_URI)

--- Skills Definition ---

SKILLS: Dict[str, Dict[str, str]] = { "sales_analytics": { "description": "Useful for analyzing sales revenue, trends, and regional performance.", "content": """ You are a Sales Analytics Expert. You have access to the 'sales_data' table. Table Schema:

  • id: integer (primary key)
  • transaction_date: date
  • product_id: varchar(50)
  • amount: decimal(10, 2)
  • region: varchar(50)

Common queries:

  • Total revenue: SUM(amount)
  • Revenue by region: GROUP BY region
  • Sales trend: GROUP BY transaction_date""" }, "inventory_management": { "description": "Useful for checking stock levels, product locations, and warehouse management.", "content": """ You are an Inventory Management Expert. You have access to the 'inventory_item""" s' table.Table Schema:- id: integer (primary key)- product_id: varchar(50)- product_name: varchar(100)- stock_count: integer- warehouse_location: varchar(50)Common queries:- Check stock: WHERE product_name = '...'- Low stock: WHERE stock_count < threshold""" }}# --- Tools ---@tooldefload_skill(skill_name: str)-> str:""" 특정 스킬에 대한 자세한 프롬프트와 스키마를 로드합니다. 사용 가능한 스킬: - sales_analytics: 판매, 수익 및 거래 분석용. - inventory_management: 재고, 제품 및 창고 쿼리용. """ skill = SKILLS.get(skill_name)ifnotskill:returnf"Error: Skill '{skill_name}'을(를) 찾을 수 없습니다. 사용 가능한 스킬: {list(SKILLS.keys())}"returnskill["content"]@tooldefrun_sql_query(query: str)-> str:""" 데이터베이스에 대해 SQL 쿼리를 실행합니다. 스키마를 이해하기 위해 적절한 스킬을 로드한 후에만 이 도구를 사용하십시오. """try:returndb.run(query)exceptExceptionase:returnf"Error executing SQL:{e}"@tooldeflist_tables()-> str:"""데이터베이스에서 사용 가능한 모든 테이블을 나열합니다."""returnstr(db.get_usable_table_names())tools = [load_skill, run_sql_query, list_tables]# --- Agent Setup ---llm = ChatOpenAI( base_url=BASE_URL, api_key=API_KEY, model=MODEL_NAME, temperature=0)llm_with_tools = llm.bind_tools(tools)# --- Graph Definition ---classAgentState(MessagesState):# 필요한 경우 사용자 정의 상태를 추가할 수 있지만 간단한 채팅에는 MessagesState로 충분합니다.passdefagent_node(state: AgentState): messages = state["messages"] response = llm_with_tools.invoke(messages)return{"messages": [response]}workflow = StateGraph(AgentState)workflow.add_node("agent", agent_node)workflow.add_node("tools", ToolNode(tools))workflow.add_edge(START,"agent")workflow.add_conditional_edges("agent", tools_condition)workflow.add_edge("tools","agent")app = workflow.compile()# --- Main Execution ---if__name__ =="main": system_prompt ="""You are a helpful SQL Assistant.You have access to specialized skills that contain database schemas and domain knowledge.To answer a user's question:1. Identify the relevant skill (sales_analytics or inventory_management).2. Use the 'load_skill' tool to get the schema and instructions.3. Based on the loaded skill, write and execute a SQL query using 'run_sql_query'.4. Answer the user's question based on the query results.Do not guess table names. Always load the skill first.""" print("SQL Assistant initialized. Type 'quit' to exit.") print("-"*50) messages = [SystemMessage(content=system_prompt)]# Pre-warm connection checktry: print(f"Connected to database:{DB_URI.split('@')[-1]}")exceptExceptionase: print(f"Database connection warning:{e}")whileTrue:try: user_input = input("User: ")ifuser_input.lower()in["quit","exit"]:break messages.append(HumanMessage(content=user_input))# Stream the execution print("Agent: ", end="", flush=True) final_response =Noneforeventinapp.stream({"messages": messages}, stream_mode="values"):# In 'values' mode, we get the full state. We just want to see the last message if it's new. last_message = event["messages"][-1]# Update our message history with the latest statepass# After stream finishes, the last state has the final answer final_state = app.invoke({"messages": messages}) last_msg = final_state["messages"][-1] ifisinstance(last_msg, AIMessage): print(last_msg.content) messages = final_state["messages"]# Update history print("-"*50) exceptExceptionase: print(f"\nError:{e}") break`
Published in Technology

You Might Also Like

클라우드 컴퓨팅 기술 사용 방법: 첫 번째 클라우드 인프라 구축 완벽 가이드Technology

클라우드 컴퓨팅 기술 사용 방법: 첫 번째 클라우드 인프라 구축 완벽 가이드

클라우드 컴퓨팅 기술 사용 방법: 첫 번째 클라우드 인프라 구축 완벽 가이드 서론 디지털 전환이 가속화됨에 따라 클라우드 컴퓨팅은 기업과 개발자들이 선호하는 솔루션이 되었습니다. 클라우드 컴퓨팅을 통해 사용자는 애플...

경고! Claude Code의 아버지, 1개월 후 Plan Mode를 사용하지 않으면 소프트웨어 엔지니어 직함이 사라질 것이라고 단언하다Technology

경고! Claude Code의 아버지, 1개월 후 Plan Mode를 사용하지 않으면 소프트웨어 엔지니어 직함이 사라질 것이라고 단언하다

경고! Claude Code의 아버지, 1개월 후 Plan Mode를 사용하지 않으면 소프트웨어 엔지니어 직함이 사라질 것이라고 단언하다 최근 YC의 원탁 인터뷰가 기술계에서 화제가 되었습니다——Claude Cod...

2026년 Top 10 심층 학습 자원 추천Technology

2026년 Top 10 심층 학습 자원 추천

2026년 Top 10 심층 학습 자원 추천 심층 학습이 다양한 분야에서 빠르게 발전함에 따라, 점점 더 많은 학습 자원과 도구가 등장하고 있습니다. 본문에서는 2026년 가장 주목할 만한 10개의 심층 학습 자원을...

2026년 Top 10 AI 에이전트: 핵심 판매 포인트 분석Technology

2026년 Top 10 AI 에이전트: 핵심 판매 포인트 분석

2026년 Top 10 AI 에이전트: 핵심 판매 포인트 분석 서론 인공지능의 빠른 발전과 함께 AI 에이전트(AI Agents)는 기술 분야의 핫 이슈가 되었습니다. 점점 더 많은 개발자와 기업들이 이러한 스마트 ...

2026년 Top 10 AI 도구 추천: 인공지능의 진정한 잠재력 발휘하기Technology

2026년 Top 10 AI 도구 추천: 인공지능의 진정한 잠재력 발휘하기

2026년 Top 10 AI 도구 추천: 인공지능의 진정한 잠재력 발휘하기 기술이 빠르게 발전하는 오늘날, 인공지능(AI)은 각 산업에서 인기 있는 주제가 되었습니다. 의료 건강에서 금융 서비스, 교육에서 엔터테인먼...

2026년 Top 10 AWS 도구 및 리소스 추천Technology

2026년 Top 10 AWS 도구 및 리소스 추천

2026년 Top 10 AWS 도구 및 리소스 추천 빠르게 발전하는 클라우드 컴퓨팅 분야에서 Amazon Web Services (AWS)는 선두주자로서 풍부한 서비스와 도구를 제공하여 개발자, 기업 및 기술 전문가...