మునుపటి కథనంలో, డీప్ ఏజెంట్స్ CLI ద్వారా స్కిల్స్ను ఉపయోగించే డీప్ ఏజెంట్ మోడల్ను ఎలా అనుకరించాలో మేము అన్వేషించాము. ఇప్పుడు, LangChain ఈ ఫీచర్కు స్థానికంగా మద్దతు ఇస్తుంది, అభివృద్ధి ప్రక్రియను బాగా సులభతరం చేస్తుంది. ఈ కథనం ఈ ఫంక్షనాలిటీని లోతుగా అనుభవించడానికి మరియు మరింత తెలివైన SQL అసిస్టెంట్ను నిర్మించడానికి మిమ్మల్ని నడిపిస్తుంది.
సంక్లిష్టమైన AI ఏజెంట్ను నిర్మిస్తున్నప్పుడు, డెవలపర్లు తరచుగా ఒక సందిగ్ధంలో చిక్కుకుంటారు: మొత్తం సందర్భాన్ని (డేటాబేస్ టేబుల్ స్ట్రక్చర్, API డాక్యుమెంటేషన్, వ్యాపార నియమాలు) ఒకేసారి సిస్టమ్ ప్రాంప్ట్లోకి ఇంజెక్ట్ చేయడం వల్ల సందర్భ విండో (Context Window) నిండిపోతుంది మరియు మోడల్ శ్రద్ధను మరల్చుతుందా? లేదా ఖరీదైన తరచుగా ఫైన్-ట్యూనింగ్ (Fine-tuning) ఎంచుకోవాలా?
**స్కిల్స్ మోడల్ (Skills Pattern)** ఒక సొగసైన మధ్య మార్గాన్ని అందిస్తుంది. ఇది అవసరమైన జ్ఞానాన్ని డైనమిక్గా లోడ్ చేయడం ద్వారా సందర్భాన్ని సమర్థవంతంగా ఉపయోగించుకుంటుంది. ఈ మోడల్కు LangChain యొక్క స్థానిక మద్దతు అంటే మనం "డిమాండ్పై నేర్చుకునే" సామర్థ్యం కలిగిన ఏజెంట్ను మరింత సులభంగా నిర్మించగలమని అర్థం.
ఈ కథనం అధికారిక డాక్యుమెంటేషన్ Build a SQL assistant with on-demand skillsతో కలిపి, "డిమాండ్పై నాలెడ్జ్ను లోడ్ చేయడానికి" మద్దతు ఇచ్చే SQL అసిస్టెంట్ను మొదటి నుండి నిర్మించడానికి పాఠకులకు మార్గనిర్దేశం చేస్తుంది.
## **1. ప్రధాన భావన: స్కిల్స్ మోడల్ను ఎందుకు ఎంచుకోవాలి?**
### **సాంప్రదాయ SQL ఏజెంట్ యొక్క పరిమితులు**
సాంప్రదాయ SQL ఏజెంట్ ఆర్కిటెక్చర్లో, మనం సాధారణంగా సిస్టమ్ ప్రాంప్ట్లో పూర్తి డేటాబేస్ స్కీమాను అందించాలి. వ్యాపారం అభివృద్ధి చెందుతున్నప్పుడు, టేబుల్స్ సంఖ్య వందలకి పెరిగినప్పుడు, ఈ విధానం ముఖ్యమైన సమస్యలను తెస్తుంది:
- **భారీ టోకెన్ వినియోగం: ప్రతి సంభాషణలో సంబంధం లేని టేబుల్ స్ట్రక్చర్ యొక్క పెద్ద మొత్తాన్ని తీసుకువెళ్లడం వల్ల వనరులు వృధా అవుతాయి.**
- **భ్రమ ప్రమాదం పెరుగుతుంది: చాలా సంబంధం లేని జోక్య సమాచారం మోడల్ యొక్క తగ్గింపు ఖచ్చితత్వాన్ని తగ్గిస్తుంది.**
- **నిర్వహణ కష్టం: అన్ని వ్యాపార మార్గాల జ్ఞానం గట్టిగా జతచేయబడి ఉంటుంది, స్వతంత్రంగా పునరావృతం చేయడం కష్టం.**
### **స్కిల్స్ మోడల్: క్రమంగా బహిర్గతం చేయడం ఆధారంగా పరిష్కారం**
స్కిల్స్ మోడల్ **క్రమంగా బహిర్గతం చేయడం (Progressive Disclosure)** సూత్రంపై ఆధారపడి ఉంటుంది, జ్ఞాన సముపార్జన ప్రక్రియను పొరలుగా విభజిస్తుంది:
- **ఏజెంట్ ప్రారంభ స్థితి: ఏ "నైపుణ్యాలు" (Skills) ఉన్నాయి మరియు వాటి సంక్షిప్త వివరణ (Description) మాత్రమే తెలుసు, తేలికగా ఉంచబడుతుంది.**
- **రన్టైమ్ లోడింగ్: నిర్దిష్ట సమస్యను ఎదుర్కొన్నప్పుడు (ఉదాహరణకు, "ఇన్వెంటరీని ప్రశ్నించడం"), ఏజెంట్ చురుకుగా సాధనాన్ని (**`load_skill`**) పిలుస్తుంది, ఆ నైపుణ్యం యొక్క వివరణాత్మక సందర్భాన్ని (Schema + Prompt) లోడ్ చేస్తుంది.**
- **పనిని అమలు చేయడం: లోడ్ చేసిన ఖచ్చితమైన సందర్భం ఆధారంగా, నిర్దిష్ట పనిని అమలు చేయండి (SQL వ్రాయడం మరియు అమలు చేయడం వంటివి).**
ఈ మోడల్ **అనంతమైన విస్తరణ** మరియు **బృంద విడదీయడానికి** సమర్థవంతంగా మద్దతు ఇస్తుంది, ఏజెంట్ పెరుగుతున్న సంక్లిష్ట వ్యాపార దృశ్యాలకు అనుగుణంగా ఉంటుంది.
## **2. సిస్టమ్ ఆర్కిటెక్చర్ డిజైన్**
ఈ ఆచరణాత్మక ప్రాజెక్ట్ ఈ మోడల్ యొక్క ఆచరణాత్మక అనువర్తనాన్ని ప్రదర్శించడానికి రెండు ప్రధాన నైపుణ్యాలను కలిగి ఉన్న SQL అసిస్టెంట్ను నిర్మిస్తుంది:
- **Sales Analytics (విక్రయాల విశ్లేషణ):**`sales_data`**టేబుల్కు బాధ్యత వహిస్తుంది, ఆదాయ గణాంకాలు, ఆర్డర్ ట్రెండ్ విశ్లేషణ మొదలైన వాటిని నిర్వహిస్తుంది.**
- **Inventory Management (ఇన్వెంటరీ నిర్వహణ):**`inventory_items`**టేబుల్కు బాధ్యత వహిస్తుంది, ఇన్వెంటరీ స్థాయి పర్యవేక్షణ, స్థాన ప్రశ్న మొదలైన వాటిని నిర్వహిస్తుంది.**
## **3. అభివృద్ధి వాతావరణం ఏర్పాటు**
ఈ ప్రాజెక్ట్ సమర్థవంతమైన డిపెండెన్సీ నిర్వహణ కోసం Python`uv`ని ఉపయోగిస్తుంది.
### **ప్రధాన డిపెండెన్సీ ఇన్స్టాలేషన్**
`uv add langchain langchain-openai langgraph psycopg2-binary python-dotenv langchain-community`
### **PostgreSQL వాతావరణం కాన్ఫిగరేషన్**
స్థానికంగా Postgres ఉదాహరణను ప్రారంభించండి మరియు`agent_platform`డేటాబేస్ను సృష్టించండి. టేబుల్ స్ట్రక్చర్ మరియు టెస్ట్ డేటాను స్వయంచాలకంగా ప్రారంభించడానికి మేము`setup_db.py`స్క్రిప్ట్ను అందిస్తున్నాము (చివరిలో సోర్స్ కోడ్ను చూడండి).
## **4. ప్రధాన అమలు దశల వివరణాత్మక వివరణ**### **దశ 1: డొమైన్ నైపుణ్యాలను నిర్వచించడం (జ్ఞానం)**
మేము నైపుణ్యాలను డిక్షనరీ నిర్మాణంగా నిర్వచిస్తాము, ఫైల్ సిస్టమ్ లేదా డేటాబేస్ నుండి లోడ్ చేసే ప్రక్రియను అనుకరిస్తాము. `description` (ఏజెంట్ నిర్ణయం తీసుకోవడానికి ఉపయోగపడుతుంది) మరియు `content` (వాస్తవానికి లోడ్ చేయబడిన వివరణాత్మక సందర్భం) మధ్య వ్యత్యాసాన్ని గమనించండి.
`SKILLS = {"sales_analytics": {"description":"విక్రయాల ఆదాయం, ట్రెండ్లను విశ్లేషించడానికి ఉపయోగపడుతుంది...","content":"""... టేబుల్ స్కీమా: sales_data ..."" },"inventory_management": {"description":"స్టాక్ స్థాయిలను తనిఖీ చేయడానికి ఉపయోగపడుతుంది...","content":"""... టేబుల్ స్కీమా: inventory_items ..."" }}`
### **దశ 2: కోర్ టూల్స్ను అమలు చేయడం (సామర్థ్యాలు)**
ఏజెంట్ పనిని పూర్తి చేయడానికి రెండు ముఖ్యమైన సాధనాలపై ఆధారపడి ఉంటుంది:
- `load_skill(skill_name)`**: పేర్కొన్న నైపుణ్యం యొక్క వివరాలను రన్ టైమ్లో డైనమిక్గా లోడ్ చేస్తుంది.**
- `run_sql_query(query)`**: నిర్దిష్ట SQL స్టేట్మెంట్ను అమలు చేస్తుంది.**
### **దశ 3: ఏజెంట్ లాజిక్ను క్రమబద్ధీకరించడం (మెదడు)**
LangGraphని ఉపయోగించి ReAct ఏజెంట్ను నిర్మించండి. సిస్టమ్ ప్రాంప్ట్ ఇక్కడ కీలక పాత్ర పోషిస్తుంది, ఇది `గుర్తించండి -> లోడ్ చేయండి -> ప్రశ్నించండి` అనే ప్రామాణిక ఆపరేటింగ్ విధానాన్ని (SOP) ఖచ్చితంగా అనుసరించడానికి ఏజెంట్కు మార్గనిర్దేశం చేస్తుంది.
`system_prompt ="""1. సంబంధిత నైపుణ్యాన్ని గుర్తించండి.2. స్కీమాను పొందడానికి 'load_skill' ఉపయోగించండి.3. 'run_sql_query' ఉపయోగించి SQL వ్రాయండి మరియు అమలు చేయండి....టేబుల్ పేర్లను ఊహించవద్దు. ఎల్లప్పుడూ ముందుగా నైపుణ్యాన్ని లోడ్ చేయండి.""`
## **5. రన్ ఎఫెక్ట్ వెరిఫికేషన్**
`test_agent.py`ని అమలు చేయడం ద్వారా, మేము సేల్స్ మరియు ఇన్వెంటరీ అనే రెండు వేర్వేరు డొమైన్ల కోసం ప్రశ్నలను పరీక్షించాము. ఏజెంట్ సమస్య ఆధారంగా నైపుణ్యాలను ఎలా డైనమిక్గా లోడ్ చేస్తుందో చూపించే కన్సోల్ యొక్క వాస్తవ అవుట్పుట్ లాగ్లు ఇక్కడ ఉన్నాయి:
`Testing Sales Query...Agent calling tools: [{'name': 'load_skill', 'args': {'skill_name': 'sales_analytics'}, 'id': 'call_f270d76b7ce4404cb5f61bf2', 'type': 'tool_call'}]Tool output:మీరు సేల్స్ అనలిటిక్స్ నిపుణులు. మీకు 'sales_data' టేబుల్కు యాక్సెస్ ఉంది. టేబుల్ స్కీమా:- 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'),)]...ఏజెంట్ ప్రతిస్పందన: మొత్తం ఆదాయం $730.50.Testing Inventory Query...Agent calling tools: [{'name': 'load_skill', 'args': {'skill_name': 'inventory_management'}, 'id': 'call_18c823b2d5064e95a0cfe2e3', 'type': 'tool_call'}]Tool output:మీరు ఇన్వెంటరీ మేనేజ్మెంట్ నిపుణులు. మీకు 'inventory_items' టేబుల్కు యాక్సెస్ ఉంది. టేబుల్ స్కీమా...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',)]...ఏజెంట్ ప్రతిస్పందన: ల్యాప్టాప్ **వేర్హౌస్ A**లో ఉంది.`## **6. పూర్తి సోర్స్ కోడ్ సూచన**
ఇక్కడ ప్రాజెక్ట్ యొక్క పూర్తి సోర్స్ కోడ్ ఉంది, ఇందులో డేటాబేస్ ప్రారంభ స్క్రిప్ట్ మరియు ఏజెంట్ ప్రధాన ప్రోగ్రామ్ ఉన్నాయి.
### **1. డేటాబేస్ ప్రారంభం (setup_db.py)**
`import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
import os
from dotenv import load_dotenv
load_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")
def create_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()
if not exists:
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()
except Exception as e:
print(f"Error creating database: {e}")
def create_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()
# అమ్మకాల పట్టికను సృష్టించండి
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)
)
"""
)
# ఇన్వెంటరీ పట్టికను సృష్టించండి
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.")
except Exception as e:
print(f"Error setting up tables: {e}")
if __name__ == "__main__":
create_database()
create_tables_and_data()
`
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:""" Load the detailed prompt and schema for a specific skill. Available skills: - sales_analytics: For sales, revenue, and transaction analysis. - inventory_management: For stock, products, and warehouse queries. """ skill = SKILLS.get(skill_name)ifnotskill:returnf"Error: Skill '{skill_name}' not found. Available skills:{list(SKILLS.keys())}"returnskill["content"]@tooldefrun_sql_query(query: str)-> str:""" Execute a SQL query against the database. Only use this tool AFTER loading the appropriate skill to understand the schema. """try:returndb.run(query)exceptExceptionase:returnf"Error executing SQL:{e}"@tooldeflist_tables()-> str:"""List all available tables in the database."""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):# We can add custom state if needed, but MessagesState is sufficient for simple chatpassdefagent_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`