本页还没有译文
本页 86 段还没译成한국어,下面显示的是英文原文。Build a SQL assistant with on-demand skills
还没翻译译自英文(docs.langchain.com)本页 86 段都还没译
This tutorial shows how to use progressive disclosure - a context management technique where the agent loads information on-demand rather than upfront - to implement skills (specialized prompt-based instructions). The agent loads skills via tool calls, rather than dynamically changing the system prompt, discovering and loading only the skills it needs for each task.
Use case: Imagine building an agent to help write SQL queries across different business verticals in a large enterprise. Your organization might have separate datastores for each vertical, or a single monolithic database with thousands of tables. Either way, loading all schemas upfront would overwhelm the context window. Progressive disclosure solves this by loading only the relevant schema when needed. This architecture also enables different product owners and stakeholders to independently contribute and maintain skills for their specific business verticals.
What you'll build: A SQL query assistant with two skills (sales analytics and inventory management). The agent sees lightweight skill descriptions in its system prompt, then loads full database schemas and business logic through tool calls only when relevant to the user's query.
Note
For a complete example of a SQL agent with query execution, error correction, and validation, see our SQL Agent tutorial. This tutorial focuses on the progressive disclosure pattern which can be applied to any domain.
Tip
Progressive disclosure was popularized by Anthropic as a technique for building scalable agent skills systems. This approach uses a three-level architecture (metadata → core content → detailed resources) where agents load information only as needed. For more on this technique, see Equipping agents for the real world with Agent Skills.
How it works
Here's the flow when a user asks for a SQL query:
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#E5F4FF','primaryTextColor':'#030710','primaryBorderColor':'#006DDD','lineColor':'#40668D','secondaryColor':'#F6FFDB','tertiaryColor':'#FDF3FF','tertiaryBorderColor':'#7E65AE','tertiaryTextColor':'#504B5F'}}}%%
flowchart TD
Start([💬 User: Write SQL query<br/>for high-value customers]) --> SystemPrompt[📋 Agent sees skill descriptions:<br/>• sales_analytics<br/>• inventory_management]
SystemPrompt --> Decide{🤔 Need sales schema}
Decide --> LoadSkill[🔧 load_skill<br/>'sales_analytics']
LoadSkill --> Schema[📊 Schema loaded:<br/>customers, orders tables<br/>+ business logic]
Schema --> WriteQuery[✍️ Agent writes SQL query<br/>using schema knowledge]
WriteQuery --> Response([✅ Returns valid SQL<br/>following business rules])
%% Styling for light and dark modes
classDef startEnd fill:#F6FFDB,stroke:#6E8900,stroke-width:2px,color:#2E3900
classDef process fill:#FDF3FF,stroke:#7E65AE,stroke-width:2px,color:#504B5F
classDef decision fill:#E5F4FF,stroke:#006DDD,stroke-width:2px,color:#030710
classDef enrichment fill:#EBD0F0,stroke:#885270,stroke-width:2px,color:#441E33
class Start,Response startEnd
class SystemPrompt,LoadSkill,WriteQuery process
class Decide decision
class Schema enrichmentWhy progressive disclosure:
- Reduces context usage - load only the 2-3 skills needed for a task, not all available skills
- Enables team autonomy - different teams can develop specialized skills independently (similar to other multi-agent architectures)
- Scales efficiently - add dozens or hundreds of skills without overwhelming context
- Simplifies conversation history - single agent with one conversation thread
What are skills: Skills, as popularized by Claude Code, are primarily prompt-based: self-contained units of specialized instructions for specific business tasks. In Claude Code, skills are exposed as directories with files on the file system, discovered through file operations. Skills guide behavior through prompts and can provide information about tool usage or include sample code for a coding agent to execute.
Tip
Skills with progressive disclosure can be viewed as a form of RAG (Retrieval-Augmented Generation), where each skill is a retrieval unit—though not necessarily backed by embeddings or keyword search, but by tools for browsing content (like file operations or, in this tutorial, direct lookup).
Trade-offs:
- Latency: Loading skills on-demand requires additional tool calls, which adds latency to the first request that needs each skill
- Workflow control: Basic implementations rely on prompting to guide skill usage - you cannot enforce hard constraints like "always try skill A before skill B" without custom logic
Tip
Implementing your own skills system
When building your own skills implementation (as we do in this tutorial), the core concept is progressive disclosure - loading information on-demand. Beyond that, you have full flexibility in implementation:
- Storage: databases, S3, in-memory data structures, or any backend
- Discovery: direct lookup (this tutorial), RAG for large skill collections, file system scanning, or API calls
- Loading logic: customize latency characteristics and add logic to search through skill content or rank relevance
- Side effects: define what happens when a skill loads, such as exposing tools associated with that skill (covered in section 8)
This flexibility lets you optimize for your specific requirements around performance, storage, and workflow control.
Setup
Installation
This tutorial requires the langchain package:
pip install langchainuv add langchainconda install langchain -c conda-forgeFor more details, see our Installation guide.
LangSmith
Set up LangSmith to inspect what is happening inside your agent. Then set the following environment variables:
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."import getpass
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = getpass.getpass()Select an LLM
Select a chat model from LangChain's suite of integrations:
👉 Read the OpenAI chat model integration docs
pip install -U "langchain[openai]"uv add "langchain[openai]"import os
from langchain.chat_models import init_chat_model
os.environ["OPENAI_API_KEY"] = "sk-..."
model = init_chat_model("gpt-5.5")import os
from langchain_openai import ChatOpenAI
os.environ["OPENAI_API_KEY"] = "sk-..."
model = ChatOpenAI(model="gpt-5.5")👉 Read the Anthropic chat model integration docs
pip install -U "langchain[anthropic]"uv add "langchain[anthropic]"import os
from langchain.chat_models import init_chat_model
os.environ["ANTHROPIC_API_KEY"] = "sk-..."
model = init_chat_model("claude-sonnet-4-6")import os
from langchain_anthropic import ChatAnthropic
os.environ["ANTHROPIC_API_KEY"] = "sk-..."
model = ChatAnthropic(model="claude-sonnet-4-6")👉 Read the Azure chat model integration docs
pip install -U "langchain[openai]"uv add "langchain[openai]"import os
from langchain.chat_models import init_chat_model
os.environ["AZURE_OPENAI_API_KEY"] = "..."
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview"
model = init_chat_model(
"azure_openai:gpt-5.5",
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
)import os
from langchain_openai import AzureChatOpenAI
os.environ["AZURE_OPENAI_API_KEY"] = "..."
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview"
model = AzureChatOpenAI(
model="gpt-5.5",
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"]
)👉 Read the Google GenAI chat model integration docs
pip install -U "langchain[google-genai]"uv add "langchain[google-genai]"import os
from langchain.chat_models import init_chat_model
os.environ["GOOGLE_API_KEY"] = "..."
model = init_chat_model("google_genai:gemini-3.7-flash")import os
from langchain_google_genai import ChatGoogleGenerativeAI
os.environ["GOOGLE_API_KEY"] = "..."
model = ChatGoogleGenerativeAI(model="gemini-3.7-flash")👉 Read the AWS Bedrock chat model integration docs
pip install -U "langchain[aws]"uv add "langchain[aws]"from langchain.chat_models import init_chat_model
# Follow the steps here to configure your credentials:
# https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
model = init_chat_model(
"us.anthropic.claude-sonnet-4-6",
model_provider="bedrock_converse",
)from langchain_aws import ChatBedrock
model = ChatBedrock(model="us.anthropic.claude-sonnet-4-6")👉 Read the HuggingFace chat model integration docs
pip install -U "langchain[huggingface]"uv add "langchain[huggingface]"import os
from langchain.chat_models import init_chat_model
os.environ["HUGGINGFACEHUB_API_TOKEN"] = "hf_..."
model = init_chat_model(
"microsoft/Phi-3-mini-4k-instruct",
model_provider="huggingface",
temperature=0.7,
max_tokens=1024,
)import os
from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint
os.environ["HUGGINGFACEHUB_API_TOKEN"] = "hf_..."
llm = HuggingFaceEndpoint(
repo_id="microsoft/Phi-3-mini-4k-instruct",
temperature=0.7,
max_length=1024,
)
model = ChatHuggingFace(llm=llm)👉 Read the OpenRouter chat model integration docs
pip install -U "langchain-openrouter"uv add "langchain-openrouter"import os
from langchain.chat_models import init_chat_model
os.environ["OPENROUTER_API_KEY"] = "sk-..."
model = init_chat_model(
"auto",
model_provider="openrouter",
)import os
from langchain_openrouter import ChatOpenRouter
os.environ["OPENROUTER_API_KEY"] = "sk-..."
model = ChatOpenRouter(model="auto")1. Define skills
First, define the structure for skills. Each skill has a name, a brief description (shown in the system prompt), and full content (loaded on-demand):
from typing import TypedDict
class Skill(TypedDict): # [!code highlight]
"""A skill that can be progressively disclosed to the agent."""
name: str # Unique identifier for the skill
description: str # 1-2 sentence description to show in system prompt
content: str # Full skill content with detailed instructionsNow define example skills for a SQL query assistant. The skills are designed to be lightweight in description (shown to the agent upfront) but detailed in content (loaded only when needed):
2. Create skill loading tool
Create a tool to load full skill content on-demand:
from langchain.tools import tool
@tool # [!code highlight]
def load_skill(skill_name: str) -> str:
"""Load the full content of a skill into the agent's context.
Use this when you need detailed information about how to handle a specific
type of request. This will provide you with comprehensive instructions,
policies, and guidelines for the skill area.
Args:
skill_name: The name of the skill to load (e.g., "expense_reporting", "travel_booking")
"""
# Find and return the requested skill
for skill in SKILLS:
if skill["name"] == skill_name:
return f"Loaded skill: {skill_name}\n\n{skill['content']}" # [!code highlight]
# Skill not found
available = ", ".join(s["name"] for s in SKILLS)
return f"Skill '{skill_name}' not found. Available skills: {available}"The load_skill tool returns the full skill content as a string, which becomes part of the conversation as a ToolMessage. For more details on creating and using tools, see the Tools guide.
3. Build skill middleware
Create custom middleware that injects skill descriptions into the system prompt. This middleware makes skills discoverable without loading their full content upfront.
Note
This guide demonstrates creating custom middleware. For a comprehensive guide on middleware concepts and patterns, see the custom middleware documentation.
from langchain.agents.middleware import ModelRequest, ModelResponse, AgentMiddleware
from langchain.messages import SystemMessage
from typing import Callable
class SkillMiddleware(AgentMiddleware): # [!code highlight]
"""Middleware that injects skill descriptions into the system prompt."""
# Register the load_skill tool as a class variable
tools = [load_skill] # [!code highlight]
def __init__(self):
"""Initialize and generate the skills prompt from SKILLS."""
# Build skills prompt from the SKILLS list
skills_list = []
for skill in SKILLS:
skills_list.append(
f"- **{skill['name']}**: {skill['description']}"
)
self.skills_prompt = "\n".join(skills_list)
def wrap_model_call(
self,
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
"""Sync: Inject skill descriptions into system prompt."""
# Build the skills addendum
skills_addendum = ( # [!code highlight]
f"\n\n## Available Skills\n\n{self.skills_prompt}\n\n" # [!code highlight]
"Use the load_skill tool when you need detailed information " # [!code highlight]
"about handling a specific type of request." # [!code highlight]
)
# Append to system message content blocks
new_content = list(request.system_message.content_blocks) + [
{"type": "text", "text": skills_addendum}
]
new_system_message = SystemMessage(content=new_content)
modified_request = request.override(system_message=new_system_message)
return handler(modified_request)
The middleware appends skill descriptions to the system prompt, making the agent aware of available skills without loading their full content. The load_skill tool is registered as a class variable, making it available to the agent.
Note
Production consideration: This tutorial loads the skill list in __init__ for simplicity. In a production system, you may want to load skills in the before_agent hook instead, allowing them to be refreshed periodically to reflect up-to-date changes (e.g., when new skills are added or existing ones are modified). See the before_agent hook documentation for details.
4. Create the agent with skill support
Now create the agent with the skill middleware and a checkpointer for state persistence:
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
# Create the agent with skill support
agent = create_agent(
model,
system_prompt=(
"You are a SQL query assistant that helps users "
"write queries against business databases."
),
middleware=[SkillMiddleware()], # [!code highlight]
checkpointer=InMemorySaver(),
)The agent now has access to skill descriptions in its system prompt and can call load_skill to retrieve full skill content when needed. The checkpointer maintains conversation history across turns.
5. Test progressive disclosure
Test the agent with a question that requires skill-specific knowledge:
from langchain_core.utils.uuid import uuid7
# Configuration for this conversation thread
thread_id = str(uuid7())
config = {"configurable": {"thread_id": thread_id}}
# Ask for a SQL query
result = agent.invoke( # [!code highlight]
{
"messages": [
{
"role": "user",
"content": (
"Write a SQL query to find all customers "
"who made orders over $1000 in the last month"
),
}
]
},
config
)
# Print the conversation
for message in result["messages"]:
if hasattr(message, 'pretty_print'):
message.pretty_print()
else:
print(f"{message.type}: {message.content}")
Expected output:
================================ Human Message =================================
Write a SQL query to find all customers who made orders over $1000 in the last month
================================== Ai Message ==================================
Tool Calls:
load_skill (call_abc123)
Call ID: call_abc123
Args:
skill_name: sales_analytics
================================= Tool Message =================================
Name: load_skill
Loaded skill: sales_analytics
# Sales Analytics Schema
## Tables
### customers
- customer_id (PRIMARY KEY)
- name
- email
- signup_date
- status (active/inactive)
- customer_tier (bronze/silver/gold/platinum)
### orders
- order_id (PRIMARY KEY)
- customer_id (FOREIGN KEY -> customers)
- order_date
- status (pending/completed/cancelled/refunded)
- total_amount
- sales_region (north/south/east/west)
[... rest of schema ...]
## Business Logic
**High-value orders**: Orders with `total_amount > 1000`
**Revenue calculation**: Only count orders with `status = 'completed'`
================================== Ai Message ==================================
Here's a SQL query to find all customers who made orders over $1000 in the last month:
\`\`\`sql
SELECT DISTINCT
c.customer_id,
c.name,
c.email,
c.customer_tier
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.total_amount > 1000
AND o.status = 'completed'
AND o.order_date >= CURRENT_DATE - INTERVAL '1 month'
ORDER BY c.customer_id;
\`\`\`
This query:
- Joins customers with their orders
- Filters for high-value orders (>$1000) using the total_amount field
- Only includes completed orders (as per the business logic)
- Restricts to orders from the last month
- Returns distinct customers to avoid duplicates if they made multiple qualifying orders
The agent saw the lightweight skill description in its system prompt, recognized the question required sales database knowledge, called load_skill("sales_analytics") to get the full schema and business logic, and then used that information to write a correct query following the database conventions.
6. Advanced: Add constraints with custom state
Complete example
Implementation variations
Progressive disclosure and context engineering
Next steps
- Learn about middleware for more dynamic agent behaviors
- Explore context engineering techniques for managing agent context
- Explore the handoffs pattern for sequential workflows
- Read the subagents pattern for parallel task routing
- See multi-agent patterns for other approaches to specialized agents
- Use LangSmith to debug and monitor skill loading