0Pricing
AI Agents · บทเรียน

การทำงานของตัวแทน NL-to-SQL

การแทรกโครงร่าง การสร้างคำสั่งค้นหา การประมวลผล และการจัดรูปแบบผลลัพธ์

การทำงานของตัวแทน NL-to-SQL เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

เอเจนต์แปลงภาษาธรรมชาติเป็น SQL คืออะไร

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

แทนที่จะเขียน SELECT COUNT(*) FROM orders WHERE status='pending' ผู้ใช้เพียงถามว่า "เรามีคำสั่งซื้อที่รอดำเนินการอยู่กี่รายการ"

สถาปัตยกรรมหลัก

เอเจนต์แปลงภาษาธรรมชาติเป็น SQL ทุกตัวมีกระบวนการพื้นฐานเหมือนกัน:

  1. การแทรกสคีมา — แทรกโครงสร้างฐานข้อมูลลงในพรอมต์
  2. LLM สร้าง SQL — โมเดลสร้างคำสั่งค้นหา
  3. ดำเนินการ — เรียกใช้คำสั่งกับฐานข้อมูล
  4. จัดรูปแบบผลลัพธ์ — แปลงแถวข้อมูลเป็นข้อความที่อ่านง่าย
  5. ส่งคืนคำตอบ — ตอบกลับผู้ใช้
# High-level pipeline
def nl_to_sql_agent(user_question, db_connection):
    schema = get_schema(db_connection)
    sql = llm_generate_sql(user_question, schema)
    rows = execute_query(db_connection, sql)
    answer = format_results(rows, user_question)
    return answer

คำอธิบายการแทรกสคีมา

LLM ไม่มีความรู้เกี่ยวกับโครงสร้างฐานข้อมูลของคุณ คุณต้องแทรกสคีมาลงในทุกพรอมต์ เพื่อให้โมเดลทราบว่ามีตารางและคอลัมน์ใดอยู่บ้าง

คำอธิบายสคีมาแบบกระชับจะบอกโมเดลว่า "ตาราง orders มีคอลัมน์: id, user_id, status, total, created_at"

def build_schema_prompt(schema_info):
    lines = []
    for table in schema_info:
        cols = ', '.join(
            f"{c['name']} ({c['type']})"
            for c in table['columns']
        )
        lines.append(f"Table {table['name']}: {cols}")
    return '\n'.join(lines)

# Output:
# Table users: id (INT), email (VARCHAR), created_at (TIMESTAMP)
# Table orders: id (INT), user_id (INT), status (VARCHAR), total (FLOAT)

if __name__ == '__main__':
    demo_schema = [
        {'name': 'users', 'columns': [{'name': 'id', 'type': 'INT'}, {'name': 'email', 'type': 'VARCHAR'}]},
        {'name': 'orders', 'columns': [{'name': 'id', 'type': 'INT'}, {'name': 'user_id', 'type': 'INT'}]},
    ]
    print(build_schema_prompt(demo_schema))

พรอมต์การสร้าง SQL โดย LLM

พรอมต์ต้องให้ข้อมูลสามอย่างแก่ LLM ได้แก่ สคีมา คำถาม และคำสั่งที่ชัดเจนให้ส่งคืนเฉพาะ SQL ที่ถูกต้อง

การระบุอย่างชัดเจนว่าให้ใช้ SELECT เท่านั้น และระบุรูปแบบ SQL เป้าหมาย (PostgreSQL, MySQL, SQLite) มีความสำคัญอย่างยิ่งต่อความปลอดภัยและความถูกต้อง

SYSTEM_PROMPT = '''You are a SQL expert. Given a database schema and a question,
generate a valid {dialect} SELECT query. Return ONLY the SQL query, no explanation.
Do not use INSERT, UPDATE, DELETE, or DROP.

Schema:
{schema}
'''

def llm_generate_sql(question, schema, dialect='PostgreSQL'):
    prompt = SYSTEM_PROMPT.format(schema=schema, dialect=dialect)
    response = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': prompt},
            {'role': 'user', 'content': question}
        ]
    )
    return response.choices[0].message.content.strip()

การเรียกใช้ SQL ที่สร้างขึ้น

หลังจาก LLM ส่งคืน SQL แล้ว ให้เรียกใช้คำสั่งนั้นกับฐานข้อมูลจริง ใช้คำสั่งแบบมีพารามิเตอร์เมื่อทำได้ และจัดการข้อยกเว้นเสมอ — LLM อาจสร้าง SQL ที่ไม่ถูกต้อง

การครอบการเรียกใช้ด้วยการจัดการข้อยกเว้นช่วยให้คุณลองใหม่ได้ โดยส่งคำใบ้เกี่ยวกับข้อผิดพลาดกลับไปยัง LLM

import psycopg2

def execute_query(conn, sql):
    try:
        with conn.cursor() as cur:
            cur.execute(sql)
            columns = [desc[0] for desc in cur.description]
            rows = cur.fetchmany(100)  # limit rows
            return {'columns': columns, 'rows': rows}
    except psycopg2.Error as e:
        return {'error': str(e), 'sql': sql}

การจัดรูปแบบผลลัพธ์สำหรับผู้ใช้

แถวข้อมูลดิบจากฐานข้อมูลไม่เหมาะสำหรับผู้ใช้ เอเจนต์ต้องแปลงข้อมูลเหล่านั้นเป็นคำตอบภาษาธรรมชาติ

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

def format_results(result, original_question):
    if 'error' in result:
        return f'Query failed: {result["error"]}'

    rows = result['rows']
    columns = result['columns']

    if not rows:
        return 'No results found.'

    # For simple counts/aggregates — just return the value
    if len(columns) == 1 and len(rows) == 1:
        return f'Result: {rows[0][0]}'

    # For multi-row results — summarize
    summary = f'Found {len(rows)} rows.\n'
    for row in rows[:5]:  # show first 5
        summary += ', '.join(f'{columns[i]}: {row[i]}' for i in range(len(columns))) + '\n'
    return summary

if __name__ == '__main__':
    demo_result = {'rows': [[42]], 'columns': ['count']}
    print(format_results(demo_result, 'How many users signed up?'))
    demo_result2 = {'rows': [], 'columns': ['id']}
    print(format_results(demo_result2, 'Any orders today?'))

เหตุใดการแปลงภาษาธรรมชาติเป็น SQL จึงยาก: ความกำกวม

ความกำกวม เป็นความท้าทายที่ใหญ่ที่สุด ลองพิจารณาคำถามว่า "แสดงลูกค้าชั้นนำให้ฉันดู"

  • ชั้นนำตามรายได้ ตามจำนวนคำสั่งซื้อ หรือความใหม่ของการสั่งซื้อ
  • เฉพาะเดือนที่แล้ว หรือตลอดช่วงเวลา
  • 10 อันดับแรก หรือ 100 อันดับแรก

มนุษย์เข้าใจบริบท แต่ LLM มักตั้งสมมติฐาน เอเจนต์จึงต้องมีกลยุทธ์เพื่อจัดการหรือขอคำชี้แจงสำหรับคำถามที่กำกวม

AMBIGUITY_PROMPT = '''If the question is ambiguous, respond with JSON:
{"needs_clarification": true, "question": "your clarifying question"}

If clear, respond with the SQL query directly.

User question: {question}
'''

def generate_or_clarify(question, schema):
    response = llm_call(AMBIGUITY_PROMPT.format(
        question=question, schema=schema
    ))
    if '"needs_clarification"' in response:
        import json
        return json.loads(response)
    return {'sql': response}

เหตุใดการแปลงภาษาธรรมชาติเป็น SQL จึงยาก: ขนาดสคีมา

ฐานข้อมูลระดับองค์กรอาจมีหลายร้อยตารางและหลายพันคอลัมน์ การแทรกสคีมาทั้งหมดจะเกินหน้าต่างบริบทของ LLM

แนวทางแก้ไข ได้แก่ การค้นหาสคีมา (ฝังคำอธิบายตารางแล้วดึงรายการที่เกี่ยวข้อง) การกรองตาราง (ถาม LLM ก่อนว่าจำเป็นต้องใช้ตารางใด) และ การบีบอัดสคีมา (ตัดคอลัมน์ดัชนีและคอลัมน์ตรวจสอบออก)

# Two-phase approach for large schemas
def get_relevant_tables(question, all_tables):
    prompt = f'''Given these tables: {all_tables}
Which 3-5 tables are most relevant to answer: "{question}"?
Return a JSON list of table names only.'''
    response = llm_call(prompt)
    import json
    return json.loads(response)

def nl_to_sql_large_db(question, conn):
    all_tables = list_all_tables(conn)  # just names
    relevant = get_relevant_tables(question, all_tables)
    schema = get_schema_for_tables(conn, relevant)
    return llm_generate_sql(question, schema)

เหตุใดการแปลงภาษาธรรมชาติเป็น SQL จึงยาก: ความแตกต่างของรูปแบบ SQL

SQL ไม่ได้มีรูปแบบเดียวกันทั้งหมด LIMIT ใน PostgreSQL/MySQL จะกลายเป็น TOP ใน SQL Server ฟังก์ชันวันที่ก็แตกต่างกันไปในแต่ละฐานข้อมูล LLM ต้องทราบว่าจะใช้รูปแบบใด

ระบุรูปแบบ SQL เป้าหมายในพรอมต์ระบบเสมอ และพิจารณาเพิ่มตัวอย่างเฉพาะรูปแบบ SQL ในการป้อนพรอมต์แบบใช้ตัวอย่างไม่กี่ตัวอย่าง

DIALECT_EXAMPLES = {
    'postgresql': 'Use LIMIT for row limits. Use NOW() for current time.',
    'mysql': 'Use LIMIT for row limits. Use NOW() for current time.',
    'sqlite': 'Use LIMIT. Use datetime("now") for current time.',
    'mssql': 'Use TOP N for row limits. Use GETDATE() for current time.',
    'bigquery': 'Use LIMIT. Use CURRENT_TIMESTAMP() for current time. Use backtick for table names.'
}

def get_dialect_hint(dialect):
    return DIALECT_EXAMPLES.get(dialect.lower(), '')

if __name__ == '__main__':
    for dialect in ['postgresql', 'sqlite', 'mssql']:
        print(f'{dialect}: {get_dialect_hint(dialect)}')

วงจรกู้คืนจากข้อผิดพลาด

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

จำกัดการลองใหม่ไว้ที่ 2–3 ครั้ง เพื่อหลีกเลี่ยงวงจรไม่รู้จบสำหรับคำสั่งที่แก้ไขไม่ได้

def nl_to_sql_with_retry(question, schema, conn, max_retries=3):
    sql = llm_generate_sql(question, schema)
    for attempt in range(max_retries):
        result = execute_query(conn, sql)
        if 'error' not in result:
            return format_results(result, question)
        # Ask LLM to fix the error
        fix_prompt = f'The SQL query failed with error: {result["error"]}\n'\
                     f'Original SQL: {sql}\n'\
                     f'Please fix the SQL query.'
        sql = llm_call(fix_prompt)
        print(f'Retry {attempt + 1} with fixed SQL')
    return 'Could not generate a valid query after retries.'

นำทุกส่วนมาประกอบกัน

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

การเพิ่ม การแคชคำสั่งค้นหา (คำถามเดียวกัน → SQL เดิม) ช่วยลดเวลาแฝงและค่าใช้จ่ายของ LLM สำหรับคำถามซ้ำได้อย่างมาก

import hashlib

query_cache = {}

def cached_nl_to_sql(question, schema_hash, conn):
    cache_key = hashlib.md5((question + schema_hash).encode()).hexdigest()
    if cache_key in query_cache:
        print('Cache hit!')
        sql = query_cache[cache_key]
    else:
        schema = get_schema(conn)
        sql = llm_generate_sql(question, schema)
        query_cache[cache_key] = sql

    result = execute_query(conn, sql)
    return format_results(result, question)

แบบทดสอบความรู้

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

สรุป: สถาปัตยกรรมการแปลงภาษาธรรมชาติเป็น SQL

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

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

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

บทเรียน “การทำงานของตัวแทน NL-to-SQL” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การทำงานของตัวแทน NL-to-SQL” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การทำงานของตัวแทน NL-to-SQL”

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

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

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

บทเรียน “การทำงานของตัวแทน NL-to-SQL” ใช้เวลานานแค่ไหน

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

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

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

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

  1. การทำงานของตัวแทน NL-to-SQL
  2. การทำความเข้าใจและแทรกโครงร่าง
  3. การสร้างและตรวจสอบความถูกต้องของคำสั่ง SQL
  4. การจัดการคำถามฐานข้อมูลที่กำกวม
← กลับไปที่ AI Agents