Eelmises artiklis uurisime, kuidas Deep Agents CLI abil simuleerida Deep Agendi oskuste kasutamise mustrit. Nüüd toetab LangChain seda funktsiooni algselt, mis oluliselt lihtsustab arendusprotsessi. See artikkel juhatab teid selle funktsiooni põhjalikult kogema ja ehitama intelligentsema SQL abimehe.
Keeruliste AI agentide ehitamisel satuvad arendajad sageli dilemma ette: kas süstida kogu kontekst (andmebaasi tabeli struktuur, API dokumentatsioon, ärireeglid) korraga süsteemipäringusse (System Prompt), mis viib kontekstiakna (Context Window) ületäitumiseni ja mudeli tähelepanu hajumiseni? Või valida kulukas sagedane peenhäälestus (Fine-tuning)?
Oskuste muster (Skills Pattern) pakub elegantse vahetee. See saavutab konteksti tõhusa kasutamise, laadides dünaamiliselt vajalikud teadmised. LangChaini algne tugi sellele mustrile tähendab, et saame hõlpsamini ehitada agente, millel on "vajadusel õppimise" võime.
See artikkel juhendab lugejaid nullist, kombineerides ametliku dokumentatsiooni Build a SQL assistant with on-demand skills, et ehitada SQL Assistant, mis toetab "vajadusel teadmiste laadimist".
1. Põhimõisted: Miks valida oskuste muster?
Traditsioonilise SQL Agendi piirangud
Traditsioonilises SQL Agendi arhitektuuris peame tavaliselt süsteemipäringus esitama kogu andmebaasi skeemi (Database Schema). Kui tabelite arv äri arenguga laieneb sadadele, toob see lähenemisviis kaasa märkimisväärseid probleeme:
- Tokenite tohutu tarbimine: iga vestlus kannab endaga kaasas suurt hulka ebaolulisi tabelistruktuure, mis põhjustab ressursside raiskamist.
- Hallutsinatsioonide riski suurenemine: liigne ebaoluline häiriv teave vähendab mudeli järelduste täpsust.
- Hooldusraskused: kõigi ärivaldkondade teadmised on tihedalt seotud, mistõttu on neid raske iseseisvalt itereerida.
Oskuste muster: järkjärgulise avalikustamise lahendus
Oskuste muster põhineb järkjärgulise avalikustamise (Progressive Disclosure) põhimõttel, mis töötleb teadmiste omandamise protsessi kihiti:
- Agendi algseisund: valdab ainult seda, millised "oskused" (Skills) on olemas ja nende lühikirjeldused (Description), hoides selle kergena.
- Käitusajal laadimine: kui agent seisab silmitsi konkreetse probleemiga (nt "laoseisu päring"), kutsub agent aktiivselt tööriista (
load_skill) selle oskuse üksikasjaliku konteksti (skeem + päring) laadimiseks.
- Ülesande täitmine: tuginedes laaditud täpsele kontekstile, täidab konkreetseid ülesandeid (nt SQL-i kirjutamine ja käivitamine).
See muster toetab tõhusalt lõpmatut laiendamist ja meeskonna lahtisidumist, võimaldades agendil kohaneda üha keerukamate äriotsustega.
2. Süsteemi arhitektuuri disain
See praktiline projekt ehitab SQL Assistandi, mis sisaldab kahte põhioskust, et demonstreerida selle mustri praktilist rakendamist:
- Müügianalüüs (Sales Analytics): vastutab
sales_datatabeli eest, tegeleb tulude statistika, tellimuste trendide analüüsiga jne.
- Laohaldus (Inventory Management): vastutab
inventory_itemstabeli eest, tegeleb laoseisu taseme jälgimise, asukoha päringute jne.
3. Arenduskeskkonna seadistamine
See projekt kasutab Pythoniuvtõhusaks sõltuvuste haldamiseks.
Põhisõltuvuste installimine
uv add langchain langchain-openai langgraph psycopg2-binary python-dotenv langchain-community
PostgreSQL keskkonna konfigureerimine
Käivitage kohalik Postgresi eksemplar ja loogeagent_platformandmebaas. Pakumesetup_db.pyskripti tabelistruktuuri ja testandmete automaatseks initsialiseerimiseks (vt allpool lähtekoodi).
4. Põhiliste rakendusetappide üksikasjalik selgitus
### **Samm 1: Valdkonna oskuste määratlemine (The Knowledge)**
Määratleme oskused sõnastikustruktuurina, simuleerides failisüsteemist või andmebaasist laadimise protsessi. Pange tähele erinevust `description` (mida Agent kasutab otsuste tegemiseks) ja `content` (tegelik laaditud detailne kontekst) vahel.
`SKILLS = {"sales_analytics": {"description":"Kasulik müügitulu, trendide analüüsimiseks...","content":"""... Tabeli skeem: sales_data ..."" },"inventory_management": {"description":"Kasulik laoseisu kontrollimiseks...","content":"""... Tabeli skeem: inventory_items ..."" }}`
### **Samm 2: Põhitööriistade rakendamine (The Capabilities)**
Agent sõltub ülesannete täitmiseks kahest olulisest tööriistast:
- `load_skill(skill_name)`**: Laadib käivitamise ajal dünaamiliselt määratud oskuse üksikasjad. **
- `run_sql_query(query)`**: Teostab konkreetseid SQL-lauseid. **
### **Samm 3: Agendi loogika korraldamine (The Brain)**
Kasutame LangGraphi ReAct Agendi loomiseks. System Prompt mängib siin olulist rolli, juhendades Agenti rangelt järgima `Identify -> Load -> Query` standardset tööprotseduuri (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. Töötamise efektiivsuse kontrollimine**
Käivitades `test_agent.py`, testisime müügi ja laoseisu kahte erinevat valdkonna päringut. Allpool on konsooli tegelik väljundlogi, mis näitab, kuidas Agent oskusi vastavalt küsimusele dünaamiliselt laadib:
`Testing Sales Query...Agent calling tools: [{'name': 'load_skill', 'args': {'skill_name': 'sales_analytics'}, 'id': 'call_f270d76b7ce4404cb5f61bf1', '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. Täielik lähtekoodi näidis**\n\nAllpool on projekti täielik lähtekood, mis sisaldab andmebaasi initsialiseerimise skripti ja Agendi põhiprogrammi.\n\n### **1. Andmebaasi initsialiseerimine (setup_db.py)**\n\n`importpsycopg2frompsycopg2.extensionsimportISOLATION_LEVEL_AUTOCOMMITimportosfromdotenvimportload_dotenvload_dotenv()# Palun veendu, et .env failis on andmebaasi ühenduse infoDB_HOST = os.getenv(\### **2. Agendi peaprogramm (main.py)**
`importosfromtypingimportAnnotated, Literal, TypedDict, Union, Dictfromdotenvimportload_dotenvfromlangchain_openaiimportChatOpenAIfromlangchain_core.toolsimporttoolfromlangchain_core.messagesimportSystemMessage, HumanMessage, AIMessage, ToolMessagefromlangchain_community.utilitiesimportSQLDatabasefromlangchain_community.agent_toolkitsimportSQLDatabaseToolkitfromlanggraph.graphimportStateGraph, START, END, MessagesStatefromlanggraph.prebuiltimportToolNode, tools_conditionload_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":"Kasulik müügitulu, trendide ja piirkondliku tulemuslikkuse analüüsimiseks.","content": """Oled müügianalüüsi ekspert.Sul on juurdepääs tabelile 'sales_data'.Tabeli skeem:- id: täisarv (primaarvõti)- transaction_date: kuupäev- product_id: varchar(50)- amount: decimal(10, 2)- region: varchar(50)Levinud päringud:- Kogutulu: SUM(amount)- Tulu piirkonna järgi: GROUP BY region- Müügitrend: GROUP BY transaction_date""" },"inventory_management": {"description":"Kasulik laoseisu, toodete asukohtade ja laohalduse kontrollimiseks.","content": """Oled laohalduse ekspert.Sul on juurdepääs tabelile '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()# --- Põhiline käivitus ---if__name__ =="__main__": system_prompt ="""Sa oled abivalmis SQL-i assistent.Sul on juurdepääs spetsiaalsetele oskustele, mis sisaldavad andmebaasi skeeme ja domeeniteadmisi.Kasutaja küsimusele vastamiseks:1. Tuvasta asjakohane oskus (sales_analytics või inventory_management).2. Kasuta tööriista 'load_skill' skeemi ja juhiste hankimiseks.3. Laaditud oskuse põhjal kirjuta ja käivita SQL-päring, kasutades 'run_sql_query'.4. Vasta kasutaja küsimusele päringu tulemuste põhjal.Ära arva tabelite nimesid. Laadi alati kõigepealt oskus.""" print("SQL-i assistent on initsialiseeritud. Väljumiseks kirjuta 'quit'.") print("-"*50) messages = [SystemMessage(content=system_prompt)]# Eelsoojenduse ühenduse kontrolltry: print(f"Ühendatud andmebaasiga: {DB_URI.split('@')[-1]}")exceptExceptionase: print(f"Andmebaasi ühenduse hoiatus: {e}")whileTrue:try: user_input = input("Kasutaja: ")ifuser_input.lower()in["quit","exit"]:break messages.append(HumanMessage(content=user_input))# Voogesita käivitus print("Agent: ", end="", flush=True) final_response =Noneforeventinapp.stream({"messages": messages}, stream_mode="values"):# Režiimis 'values' saame täieliku oleku. Me tahame näha ainult viimast sõnumit, kui see on uus. last_message = event["messages"][-1]# Uuenda oma sõnumite ajalugu viimase olekugapass# Pärast voogesituse lõpetamist on viimases olekus lõplik vastus 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`