بناء واجهة قاعدة بيانات باللغة الطبيعية
أنشئوا نظامًا يطرح فيه المستخدمون أسئلة باللغة الإنجليزية البسيطة، ويولّد النموذج استعلامات SQL عبر استدعاء الدوال، وينفذ تطبيقكم الاستعلام بأمان، ثم يشرح النموذج النتائج.
بناء واجهة قاعدة بيانات باللغة الطبيعية درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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)، وهو المعيار المفتوح لربط الذكاء الاصطناعي بالأدوات الخارجية.
الأسئلة الشائعة
هل درس «بناء واجهة قاعدة بيانات باللغة الطبيعية» مجاني؟
نعم — نص درس «بناء واجهة قاعدة بيانات باللغة الطبيعية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
ماذا ستتعلم في «بناء واجهة قاعدة بيانات باللغة الطبيعية»؟
أنشئوا نظامًا يطرح فيه المستخدمون أسئلة باللغة الإنجليزية البسيطة، ويولّد النموذج استعلامات SQL عبر استدعاء الدوال، وينفذ تطبيقكم الاستعلام بأمان، ثم يشرح النموذج النتائج. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟
لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «بناء واجهة قاعدة بيانات باللغة الطبيعية»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟
نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تعريف مخططات الدوال لواجهة API
- معالجة استدعاءات الأدوات في تطبيقكم
- استدعاء الدوال بالتوازي
- بناء واجهة قاعدة بيانات باللغة الطبيعية