الخطوة 5 في الواجهة الخلفية: التكامل مع OpenAI

استبدل ردّ البوت الثابت في الكود باستجابة حقيقية من الذكاء الاصطناعي عبر OpenAI.

ما ستتعلمه

  • تحميل متغيرات البيئة باستخدام python-dotenv
  • استدعاء واجهة Chat Completions من OpenAI
  • التعامل مع أخطاء API
  • بث الاستجابات (اختياري)

الخطوة 1: احصل على مفتاح OpenAI API

  1. انتقل إلى https://platform.openai.com/api-keys
  2. أنشئ حسابًا أو سجّل الدخول
  3. انقر على "Create new secret key"
  4. انسخ المفتاح (يبدأ بـ sk-...)

الخطوة 2: إعداد ملف .env

cd aiproject/backend
cp app/.env.example app/.env

حرّر ملف app/.env وضع مفتاحك الفعلي مكان القيمة:

OPENAI_API_KEY=sk-proj-your-actual-key-here

إذا كنت تستخدم Docker: يُحمّل docker-compose.yml ملف .env هذا تلقائيًا داخل الحاوية. لا حاجة إلى أي إعداد إضافي.

الخطوة 3: أضف .env إلى .gitignore

echo ".env" >> .gitignore

بهذا تضمن ألا يصل مفتاحك السري إلى commit عن غير قصد.

الخطوة 4: تحميل متغيرات البيئة

حدّث ملف backend/app/__init__.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv

load_dotenv()

db = SQLAlchemy()


def create_app():
    app = Flask(__name__)
    app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///chat.db"
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

    db.init_app(app)

    from .routes import api
    app.register_blueprint(api, url_prefix="/api")

    with app.app_context():
        db.create_all()

    return app


if __name__ == "__main__":
    app = create_app()
    app.run(debug=True, port=5000)

يقرأ الاستدعاء load_dotenv() ملف .env ويجعل محتواه متاحًا عبر os.getenv().

الخطوة 5: تحديث routes.py لدعم OpenAI

from flask import Blueprint, request, jsonify
from . import db
from .models import Message
from openai import OpenAI
import os

api = Blueprint("api", __name__)

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))


@api.route("/status")
def status():
    return jsonify({"status": "ok"})


@api.route("/messages", methods=["GET"])
def get_messages():
    messages = Message.query.order_by(Message.timestamp.asc()).all()
    return jsonify([msg.to_dict() for msg in messages])


@api.route("/chat", methods=["POST"])
def chat():
    data = request.get_json()
    user_message = data.get("message", "")

    if not user_message:
        return jsonify({"error": "Message is required"}), 400

    user_msg = Message(content=user_message, sender="user")
    db.session.add(user_msg)
    db.session.commit()

    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {
                    "role": "system",
                    "content": "You are a helpful assistant.",
                },
                {"role": "user", "content": user_message},
            ],
        )

        ai_reply = response.choices[0].message.content
        ai_msg = Message(content=ai_reply, sender="ai")
        db.session.add(ai_msg)
        db.session.commit()

        return jsonify({
            "user_message": user_msg.to_dict(),
            "ai_message": ai_msg.to_dict(),
        })

    except Exception as e:
        return jsonify({"error": str(e)}), 500

الخطوة 6: اختبره

cd aiproject/backend
source venv/bin/activate
python app/__init__.py

في طرفية أخرى:

curl -X POST http://127.0.0.1:5000/api/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "What is the capital of France?"}'

الاستجابة المتوقعة (ردّ الذكاء الاصطناعي الفعلي):

{
  "user_message": {"id": 1, "content": "What is the capital of France?", "sender": "user", ...},
  "ai_message": {"id": 2, "content": "The capital of France is Paris.", "sender": "ai", ...}
}

كيف يعمل استدعاء OpenAI

response = client.chat.completions.create(
    model="gpt-4o-mini",           # أي نموذج ذكاء اصطناعي تستخدم
    messages=[                     # سجل المحادثة
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": user_message},
    ],
)
  • model: أي نموذج ذكاء اصطناعي تستخدمه. gpt-4o-mini سريع ورخيص.
  • messages: سياق المحادثة. رسالة system تحدد شخصية الذكاء الاصطناعي، ورسالة user هي السؤال.
  • response.choices[0].message.content: يستخرج نص ردّ الذكاء الاصطناعي.

التعامل مع الأخطاء

تلتقط كتلة try/except أي أخطاء من API: - مفتاح API غير صالح ← خطأ مصادقة - تجاوز حد الطلبات المسموح ← طلبات كثيرة جدًا - خطأ في الشبكة ← تعذّر الوصول إلى OpenAI

إذا حدث خطأ، تُرجع API {"error": "..."} برمز الحالة 500 بدلًا من أن تتعطل.

الخطوة 7: محاكاة OpenAI في الاختبارات

اختباراتك تستدعي حاليًا واجهة OpenAI API الحقيقية، وهذا يهدر المال ويستغرق وقتًا طويلًا. بدلًا من ذلك، حاكِ API — استبدلها بنسخة وهمية تُرجع ردًّا جاهزًا.

هذه مهارة بالغة الأهمية: لا تدع اختباراتك تعتمد على خدمات خارجية أبدًا.

حدّث اختبارك لتحاكي OpenAI:

from unittest.mock import patch


@patch("app.routes.client.chat.completions.create")
def test_chat_with_mock_openai(mock_openai, client):
    mock_openai.return_value.choices = [
        MockChoice(MockMessage("Mock AI reply"))
    ]

    response = client.post("/api/chat", json={"message": "Hello"})
    assert response.status_code == 200
    data = response.get_json()
    assert data["ai_message"]["content"] == "Mock AI reply"

أضف أصنافًا مساعدة في أعلى ملف الاختبار:

class MockMessage:
    def __init__(self, content):
        self.content = content


class MockChoice:
    def __init__(self, message):
        self.message = message

الآن تعمل اختباراتك فورًا ودون تكلفة — لا حاجة إلى مفتاح API، ولا اتصالات بالشبكة.

التحقق

  • أرسل سؤالًا واحصل على استجابة حقيقية من الذكاء الاصطناعي
  • أرسل رسالة فارغة واحصل على خطأ 400
  • افحص instance/chat.db لتتأكد من حفظ الرسائل
  • شغّل pytest tests/ -v — ينبغي أن تنجح الاختبارات دون مفتاح API حقيقي