0Pricing
AI Engineering Academy · บทเรียน

การสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ

สร้างระบบที่ผู้ใช้ถามคำถามด้วยภาษาธรรมดา โมเดลสร้าง SQL ผ่านการเรียกใช้ฟังก์ชัน แอปพลิเคชันของคุณเรียกใช้คำสั่งค้นหาอย่างปลอดภัย และโมเดลอธิบายผลลัพธ์เป็นภาษาธรรมชาติ

การสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ เป็นบทเรียน AI Engineering Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Engineering Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน

ภาษาธรรมชาติสู่ SQL: วิสัยทัศน์

ลองนึกภาพว่าคุณถามฐานข้อมูลว่า “ลูกค้ารายใดใช้จ่ายมากกว่า 1,000 ดอลลาร์เมื่อเดือนที่แล้ว” แล้วได้รับคำตอบ โดยไม่ต้องเขียนคำสั่ง SQL แม้แต่คำสั่งเดียว ส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติใช้การเรียกฟังก์ชันเพื่อให้ LLM สร้าง SQL จากนั้นแอปพลิเคชันของคุณจะดำเนินการอย่างปลอดภัย และโมเดลจะอธิบายผลลัพธ์ด้วยภาษาอังกฤษทั่วไป รูปแบบนี้ช่วยให้ผู้ใช้ที่ไม่ใช่ผู้เชี่ยวชาญด้านเทคนิคเข้าถึงข้อมูลได้ง่ายขึ้น

ภาพรวมสถาปัตยกรรมระบบ

กระบวนการ SQL จากภาษาธรรมชาติประกอบด้วยสี่ส่วนที่ทำงานร่วมกัน:

  • บริบทโครงสร้าง: LLM ได้รับโครงสร้างฐานข้อมูลของคุณ จึงทราบว่ามีตารางและคอลัมน์ใดบ้าง
  • การสร้าง SQL: โมเดลสร้างคำสั่ง SQL เป็นอาร์กิวเมนต์ของการเรียกฟังก์ชัน
  • การดำเนินการอย่างปลอดภัย: แอปของคุณตรวจสอบและเรียกใช้คำสั่ง จากนั้นส่งผลลัพธ์กลับ
  • การบรรยายผลลัพธ์: โมเดลได้รับผลลัพธ์ของคำสั่งและอธิบายด้วยภาษาธรรมชาติ

การกำหนดเครื่องมือฐานข้อมูลสำหรับการสืบค้น

กำหนดฟังก์ชัน query_database ที่รับคำสั่ง SQL SELECT คำอธิบายโครงสร้างในนิยามฟังก์ชันจะสอนโมเดลว่ามีตารางและคอลัมน์ใดให้ใช้ได้บ้าง ทำให้โมเดลสร้างคำสั่งที่ถูกต้องได้โดยไม่ต้องคาดเดา

query_db_tool = {
    'type': 'function',
    'function': {
        'name': 'query_database',
        'description': '''Execute a read-only SQL query on the company database.
Use this to answer questions about customers, orders, and products.
Only SELECT statements are allowed. Never use DROP, DELETE, UPDATE, or INSERT.

Available tables:
- customers (id, name, email, created_at, country)
- orders (id, customer_id, total_amount, status, created_at)
- order_items (id, order_id, product_id, quantity, unit_price)
- products (id, name, category, price, stock_quantity)
''',
        'parameters': {
            'type': 'object',
            'properties': {
                'sql': {
                    'type': 'string',
                    'description': 'A valid PostgreSQL SELECT statement.'
                },
                'explanation': {
                    'type': 'string',
                    'description': 'One-sentence explanation of what this query does.'
                }
            },
            'required': ['sql', 'explanation']
        }
    }
}

การดำเนินการ SQL อย่างปลอดภัย

อย่าเรียกใช้ SQL ดิบจากโมเดลโดยไม่ตรวจสอบโดยเด็ดขาด ให้สร้างชั้นความปลอดภัยที่อนุญาตเฉพาะคำสั่ง SELECT ปฏิเสธคำสำคัญที่เป็นอันตราย จำกัดจำนวนแถวผลลัพธ์เพื่อป้องกันปัญหาหน่วยความจำ และเรียกใช้ภายในธุรกรรมฐานข้อมูลแบบอ่านอย่างเดียว การป้องกันหลายชั้นมีความสำคัญอย่างยิ่งเมื่อเรียกใช้โค้ดที่ LLM สร้างขึ้น

import re
import psycopg2

DANGEROUS_KEYWORDS = ['DROP', 'DELETE', 'UPDATE', 'INSERT', 'TRUNCATE', 'ALTER', 'CREATE', 'EXEC']

def execute_safe_query(sql: str, max_rows: int = 100) -> list:
    '''Execute a read-only SQL query with safety guards.'''
    sql_upper = sql.upper().strip()

    # Only allow SELECT
    if not sql_upper.startswith('SELECT'):
        raise ValueError('Only SELECT statements are allowed.')

    # Block dangerous keywords
    for keyword in DANGEROUS_KEYWORDS:
        if re.search(r'\b' + keyword + r'\b', sql_upper):
            raise ValueError(f'Forbidden keyword: {keyword}')

    conn = psycopg2.connect('postgresql://readonly_user:pass@localhost/appdb')
    with conn:
        with conn.cursor() as cur:
            # Enforce read-only transaction
            cur.execute('SET TRANSACTION READ ONLY')
            cur.execute(sql)
            columns = [desc[0] for desc in cur.description]
            rows = cur.fetchmany(max_rows)
    return [dict(zip(columns, row)) for row in rows]

การแทรกบริบทโครงสร้างลงในคำสั่งระบบ

โมเดลจะสร้าง SQL ได้ดีขึ้นเมื่อมองเห็นโครงสร้างฐานข้อมูลทั้งหมด ให้สร้างคำสั่งระบบที่มีนิยามตาราง ชื่อและชนิดของคอลัมน์ รวมถึงค่าตัวอย่างของคอลัมน์ประเภทหมวดหมู่ วิธีนี้ทำให้โมเดลทราบว่าควรใช้ country = 'US' หรือ country_code = 'US' โดยไม่ต้องคาดเดา

SYSTEM_PROMPT = '''You are a data analyst assistant with access to the company database.
When users ask data questions, use the query_database tool to look up the answer.
Always explain your query in plain English before executing it.

Database schema:

CREATE TABLE customers (
    id SERIAL PRIMARY KEY,
    name VARCHAR NOT NULL,
    email VARCHAR UNIQUE,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    country VARCHAR(2)  -- ISO 2-letter code: 'US', 'UK', 'DE', etc.
);

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    customer_id INTEGER REFERENCES customers(id),
    total_amount NUMERIC(10,2),
    status VARCHAR  -- 'pending', 'shipped', 'delivered', 'cancelled'
    created_at TIMESTAMPTZ DEFAULT NOW()
);

Only use columns that exist in the schema above.
'''

การจัดรูปแบบผลลัพธ์การสืบค้นสำหรับโมเดล

ผลลัพธ์ดิบจากฐานข้อมูล (รายการของพจนานุกรม) ต้องจัดรูปแบบเป็นข้อความที่อ่านง่ายก่อนส่งกลับไปยังโมเดล ให้แปลงชุดผลลัพธ์เป็นรูปแบบกะทัดรัด เช่น ตารางหรือสรุปแบบ JSON ที่โมเดลใช้เป็นข้อมูลอ้างอิงขณะอธิบายคำตอบ หลีกเลี่ยงการส่งหลายพันแถว ให้สรุปชุดผลลัพธ์ขนาดใหญ่แทน

import json

def format_results(rows: list, max_display: int = 20) -> str:
    if not rows:
        return 'The query returned no results.'

    total = len(rows)
    display = rows[:max_display]

    # Format as a simple table
    if display:
        columns = list(display[0].keys())
        lines = [' | '.join(columns)]
        lines.append('-' * len(lines[0]))
        for row in display:
            lines.append(' | '.join(str(row[col]) for col in columns))

    result = '\n'.join(lines)
    if total > max_display:
        result += f'\n... ({total - max_display} more rows not shown)'
    return result

การนำกระบวนการทั้งหมดไปใช้งาน

เมื่อนำทุกส่วนมารวมกัน เราจะได้ฟังก์ชันที่ประมวลผลคำถามของผู้ใช้ เรียกโมเดลเพื่อสร้าง SQL เรียกใช้คำสั่งอย่างปลอดภัย และส่งผลลัพธ์กลับไปให้โมเดลอธิบาย โมเดลจะได้รับทั้งคำถามเดิมและผลลัพธ์ของคำสั่ง จากนั้นสร้างคำตอบเป็นภาษาอังกฤษทั่วไป

from openai import OpenAI
import json

client = OpenAI()

def answer_data_question(user_question: str) -> str:
    messages = [
        {'role': 'system', 'content': SYSTEM_PROMPT},
        {'role': 'user', 'content': user_question}
    ]

    # First call: get SQL from model
    resp = client.chat.completions.create(
        model='gpt-4o', messages=messages, tools=[query_db_tool]
    )
    assistant_msg = resp.choices[0].message
    messages.append(assistant_msg)

    if resp.choices[0].finish_reason == 'tool_calls':
        tc = assistant_msg.tool_calls[0]
        args = json.loads(tc.function.arguments)
        print(f'Executing: {args["explanation"]}')
        print(f'SQL: {args["sql"]}')

        try:
            rows = execute_safe_query(args['sql'])
            result_text = format_results(rows)
        except ValueError as e:
            result_text = f'Query blocked: {str(e)}'

        messages.append({'role': 'tool', 'tool_call_id': tc.id, 'content': result_text})

        # Second call: narrate results
        final = client.chat.completions.create(model='gpt-4o', messages=messages)
        return final.choices[0].message.content

    return assistant_msg.content

การจัดการคำถามข้อมูลหลายขั้นตอน

คำถามซับซ้อนอาจต้องใช้การสืบค้นหลายครั้ง เช่น “ลูกค้า 5 อันดับแรกของเราตามรายได้คือใคร และคำสั่งซื้อล่าสุดของพวกเขาคืออะไร” ต้องใช้การสืบค้นสองครั้ง ครั้งแรกเพื่อค้นหาลูกค้าอันดับต้น ๆ แล้วจึงค้นหาคำสั่งซื้อของพวกเขา อนุญาตให้โมเดลเรียกใช้เครื่องมือหลายครั้งตามลำดับ โดยเรียกใช้ลูปส่งต่อหลายรอบจนกว่า finish_reason='stop'

def answer_complex_question(user_question: str) -> str:
    messages = [
        {'role': 'system', 'content': SYSTEM_PROMPT},
        {'role': 'user', 'content': user_question}
    ]

    for _ in range(5):  # Max 5 query rounds
        resp = client.chat.completions.create(
            model='gpt-4o', messages=messages, tools=[query_db_tool]
        )
        msg = resp.choices[0].message
        messages.append(msg)

        if resp.choices[0].finish_reason == 'stop':
            return msg.content  # Model is done

        # Process tool call and loop
        tc = msg.tool_calls[0]
        args = json.loads(tc.function.arguments)
        try:
            rows = execute_safe_query(args['sql'])
            result = format_results(rows)
        except Exception as e:
            result = f'Error: {str(e)}'

        messages.append({'role': 'tool', 'tool_call_id': tc.id, 'content': result})

    return 'Could not complete the analysis within the step limit.'

การป้องกันความเสี่ยงจากการแทรกคำสั่ง SQL

แม้จะมีตัวป้องกันที่อนุญาตเฉพาะ SELECT โมเดลที่มีเจตนาร้าย (หรือผู้ใช้ที่โจมตีระบบ) ก็อาจพยายามลักลอบนำข้อมูลออกผ่านการสืบค้นย่อยหรือเทคนิคที่ใช้ความคิดเห็นเพิ่มเติม การป้องกันอื่น ๆ ได้แก่ การใช้ผู้ใช้ฐานข้อมูลแบบอ่านอย่างเดียวที่มีสิทธิ์ SELECT เท่านั้น การทำงานผ่านกลุ่มการเชื่อมต่อแยกต่างหาก และการตรวจสอบว่าชื่อตารางในคำสั่งตรงกับรายการที่อนุญาตในโครงสร้างของคุณ

ALLOWED_TABLES = {'customers', 'orders', 'order_items', 'products'}

def validate_tables_in_sql(sql: str) -> bool:
    '''Check that only whitelisted tables are referenced in the query.'''
    import sqlparse
    parsed = sqlparse.parse(sql)[0]
    table_names = set()
    from_seen = False
    for token in parsed.flatten():
        if token.ttype is sqlparse.tokens.Keyword and token.value.upper() in ('FROM', 'JOIN'):
            from_seen = True
        elif from_seen and token.ttype is sqlparse.tokens.Name:
            table_names.add(token.value.lower())
            from_seen = False
    unknown = table_names - ALLOWED_TABLES
    if unknown:
        raise ValueError(f'References unknown tables: {unknown}')
    return True

การแคชคำสั่งสืบค้นที่ใช้บ่อย

คำถามทางธุรกิจหลายคำถามถูกถามซ้ำ ๆ และมีคำตอบเดิม เช่น “เรามีลูกค้ากี่ราย” หรือ “รายได้ของเดือนที่แล้วเป็นเท่าไร” ให้แคชผลลัพธ์เหล่านี้ไว้ใน Redis โดยกำหนด TTL ระยะสั้น ตรวจสอบแคชก่อนเรียกใช้คำสั่ง วิธีนี้ช่วยลดภาระฐานข้อมูลและทำให้ตอบคำถามเชิงวิเคราะห์ที่พบบ่อยได้เร็วขึ้น

import redis
import hashlib
import json

r = redis.Redis.from_url('redis://localhost:6379')

def cached_query(sql: str, ttl_seconds: int = 300) -> list:
    cache_key = 'nl_query:' + hashlib.sha256(sql.encode()).hexdigest()
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)
    rows = execute_safe_query(sql)
    r.setex(cache_key, ttl_seconds, json.dumps(rows, default=str))
    return rows

การอธิบายคำสั่งสืบค้นแก่ผู้ใช้

สร้างความไว้วางใจด้วยการแสดงคำสั่ง SQL ที่สร้างขึ้นควบคู่กับคำตอบภาษาธรรมชาติ เมื่อผู้ใช้เห็นว่า “ฉันเรียกใช้คำสั่งนี้: SELECT COUNT(*) FROM customers WHERE country = ?UK?” ผู้ใช้จะตรวจสอบได้ว่าคำตอบถูกต้อง และเรียนรู้รูปแบบ SQL ได้ ฟิลด์ explanation ในโครงสร้างเครื่องมือของเราเหมาะสำหรับจุดประสงค์นี้

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจของคุณเกี่ยวกับการสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ

สรุปบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า โครงสร้างเครื่องมือ query_database จะแทรกบริบทโครงสร้าง ทำให้โมเดลสร้าง SQL ที่ถูกต้อง การตรวจสอบความปลอดภัยต้องบล็อกคำสั่งที่ไม่ใช่ SELECT และคำสำคัญที่เป็นอันตรายก่อนการดำเนินการ และ ลูปการเรียกโมเดลช่วยให้วิเคราะห์ข้อมูลหลายขั้นตอนที่ต้องสืบค้นตามลำดับได้ ถัดไป เราจะสำรวจ Model Context Protocol (MCP) ซึ่งเป็นมาตรฐานแบบเปิดสำหรับเชื่อมต่อเอไอกับเครื่องมือภายนอก

คำถามที่พบบ่อย

บทเรียน “การสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Engineering Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ”

สร้างระบบที่ผู้ใช้ถามคำถามด้วยภาษาธรรมดา โมเดลสร้าง SQL ผ่านการเรียกใช้ฟังก์ชัน แอปพลิเคชันของคุณเรียกใช้คำสั่งค้นหาอย่างปลอดภัย และโมเดลอธิบายผลลัพธ์เป็นภาษาธรรมชาติ คุณปฏิบัติ AI Engineering Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Engineering Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Engineering Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน AI Engineering Academy นี้ได้ไหม

ได้ บทเรียน AI Engineering Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การกำหนดโครงสร้างฟังก์ชันสำหรับ API
  2. การประมวลผลการเรียกใช้เครื่องมือในแอปพลิเคชัน
  3. การเรียกใช้ฟังก์ชันแบบขนาน
  4. การสร้างส่วนติดต่อฐานข้อมูลด้วยภาษาธรรมชาติ
← กลับไปที่ AI Engineering Academy