تنفيذ استراتيجيات التقسيم في RAG باستخدام LangChain وwatsonx.ai

في هذا البرنامج التعليمي، ستجرّب عدة استراتيجيات للتقسيم باستخدام LangChain وأحدث نموذج من IBM Granite المتوفر الآن على watsonx.ai. الهدف العام هو إجراء التوليد المعزز بالاسترجاع (RAG).

ما المقصود بالتقسيم؟

يشير التقسيم إلى عملية تقسيم أجزاء كبيرة من النص إلى مقاطع أو أجزاء نصية أصغر. وللتأكيد على أهمية التقسيم، من المفيد فهم RAG. يُعَد RAG تقنية في معالجة اللغة الطبيعية (NLP) تجمع بين استرجاع المعلومات والنماذج اللغوية الكبيرة لاسترداد المعلومات ذات الصلة من مجموعات البيانات التكميلية لتحسين جودة مخرجات النموذج اللغوي الكبير. لإدارة المستندات الكبيرة، يمكننا استخدام تقسيم النصوص لتقسيم المحتوى إلى مقاطع أصغر ذات معنى. يمكن بعد ذلك تضمين هذه الأجزاء النصية وتخزينها في قاعدة بيانات متجهات من خلال استخدام نموذج التضمين. أخيرًا، يمكن لنظام RAG بعد ذلك استخدام البحث الدلالي لاسترداد المقاطع الأكثر صلة فقط. تميل المقاطع الأصغر إلى التفوق على المقاطع الأكبر لأنها تُعَد أجزاءً أكثر قابلية للإدارة لنماذج ذات حجم نافذة سياق أصغر.

تتضمن بعض العناصر الرئيسية للتقسيم ما يلي:

  • استراتيجية التقسيم: يُعَد اختيار استراتيجية التقسيم المناسبة لتطبيق RAG الخاص بك أمرًا مهمًا لأنها تحدِّد حدود إنشاء المقاطع. وسنستكشف بعضًا منها في القسم التالي.
  • حجم المقطع: الحد الأقصى لعدد الرموز المميزة في كل مقطع. عادةً ما يتضمن تحديد حجم المقطع المناسب بعض التجارب.  
  • تداخل المقاطع: عدد الرموز المشتركة بين المقاطع للحفاظ على السياق. هذه مَعلمة اختيارية.

استراتيجية التقسيم

هناك عدة استراتيجيات مختلفة لتقسيم المقاطع يمكن الاختيار من بينها. من المهم اختيار تقنية التقييم الأنسب لحالة الاستخدام الخاصة بتطبيق النموذج اللغوي الكبير الخاص بك. بعض أساليب تقسيم النصوص المستخدمة بشكل شائع تشمل ما يلي: 

  • التقسيم ثابت الحجم: فصل النصوص بحجم محدد لكل مقطع مع إمكانية وجود تداخل بين المقاطع. هذا النهج هو الأكثر شيوعًا ووضوحًا.
  • التقسيم التكراري: تكرار استخدام الفواصل الافتراضية حتى يتم الوصول إلى حجم المقطع المرغوب. الفواصل الافتراضية تشمل ["\n\n"، و"\n"، و" "، و""]. تستخدم طريقة التقسيم هذه فواصل هرمية بحيث يتم الاحتفاظ بالفقرات أولًا، ثم الجمل، وأخيرًا الكلمات معًا قدر الإمكان.
  • التقسيم الدلالي: تقسيم النص بطريقة تجمع الجمل بناءً على التشابه الدلالي لتضميناتها. التضمينات ذات التشابه الدلالي العالي أقرب إلى بعضها من التضمينات ذات التشابه الدلالي المنخفض. ويؤدي هذا إلى إنشاء مقاطع مدركة للسياق.
  • التقسيم القائم على المستند: تقسيم النصوص اعتمادًا على هيكل المستند. يمكن لهذا الفاصل استخدام نصوص Markdown والصور والجداول، وحتى فئات ودوال كود Python كطرق لتحديد الهيكل. وبذلك، يمكن تقسيم المستندات الكبيرة ومعالجتها بواسطة النموذج اللغوي الكبير.
  • التقسيم القائم على الوكلاء: يستفيد من الذكاء الاصطناعي الوكيل من خلال تمكين النموذج اللغوي الكبير من تحديد طريقة تقسيم المستند بشكل مناسب بناءً على المعنى الدلالي وكذلك هيكل المحتوى مثل أنواع الفقرات ورؤوس الأقسام والتعليمات خطوة بخطوة وغير ذلك. هذا الفاصل تجريبي ويهدف إلى محاكاة التفكير البشري عند معالجة المستندات الطويلة. 

الخطوات

الخطوة 1. إعداد البيئة.

رغم توفُّر عدة أدوات للاختيار منها، يُرشدك هذا الدليل خلال خطوات إعداد حساب IBM لاستخدام Jupyter Notebook.

  1. سجِّل الدخول إلى watsonx.ai باستخدام حساب IBM Cloud الخاص بك.

  2. أنشئ مشروع watsonx.ai.

    يمكنك الحصول على معرِّف المشروع من داخل مشروعك. انقر فوق علامة التبويب الإدارة (Manage) ، ثم انسخ معرِّف المشروع من قسم التفاصيل (Details) في صفحة عام (General). ستحتاج إلى هذا المعرِّف في هذا البرنامج التعليمي.

  3. أنشئ Jupyter Notebook.

ستؤدي هذه الخطوة إلى فتح بيئة دفتر ملاحظات حيث يمكنك نسخ التعليمات البرمجية من هذا البرنامج التعليمي. أو يمكنك تنزيل هذا الدفتر إلى نظامك المحلي وتحميله إلى مشروع watsonx.ai كأصل. لمشاهدة المزيد من البرامج التعليمية حول Granite، تفضَّل بزيارة مجتمع IBM Granite. يمكن العثور على Jupyter Notebook هذا مع مجموعات البيانات المستخدمة على GitHub.

الخطوة 2. إعداد مثيل watsonx.ai Runtime ومفتاح واجهة برمجة التطبيقات.

  1. أنشئ مثيل خدمة watsonx.ai Runtime (اختَر المنطقة المناسبة لك، واختَر خطة Lite، وهي مثيل مجاني).

  2. أنشئ مفتاح واجهة برمجة التطبيقات.

  3. اربط مثيل خدمة watsonx.ai Runtime بالمشروع الذي أنشأته في watsonx.ai.

الخطوة 3. تثبيت واستيراد المكتبات ذات الصلة وإعداد بيانات الاعتماد الخاصة بك.

#installations
!pip install -q langchain langchain-ibm langchain_experimental langchain-text-splitters langchain_chroma transformers bs4 langchain_huggingface sentence-transformers
# imports
import getpass
from langchain_ibm import WatsonxLLM
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from ibm_watsonx_ai.metanames import GenTextParamsMetaNames as GenParams
from transformers import AutoTokenizer

لتعيين بيانات الاعتماد الخاصة بنا، نحتاج إلى WATSONX_APIKEY وWATSONX_PROJECT_ID التي أنشأتها في الخطوة 1. سنقوم أيضًا بتعيين عنوان URL الذي يعمل كنقطة نهاية لواجهة برمجة التطبيقات.

WATSONX_APIKEY = getpass.getpass("Please enter your watsonx.ai Runtime API key (hit enter): ")
WATSONX_PROJECT_ID = getpass.getpass("Please enter your project ID (hit enter): ")
URL = "https://us-south.ml.cloud.ibm.com"

الخطوة 4. تهيئة النموذج اللغوي الكبير

سنستخدم Granite 3.1 كنموذج لغوي كبير في هذا البرنامج التعليمي. لتهيئة النموذج اللغوي الكبير، نحتاج إلى تعيين مَعلمات النموذج. لمعرفة المزيد حول مَعلمات النموذج هذه، مثل الحد الأدنى والحد الأقصى للرموز المميزة، راجِع الوثائق.

llm = WatsonxLLM(
        model_id= "ibm/granite-3-8b-instruct",
        url=URL,
        apikey=WATSONX_APIKEY,
        project_id=WATSONX_PROJECT_ID,
        params={
            GenParams.DECODING_METHOD: "greedy",
            GenParams.TEMPERATURE: 0,
            GenParams.MIN_NEW_TOKENS: 5,
            GenParams.MAX_NEW_TOKENS: 2000,
            GenParams.REPETITION_PENALTY:1.2
        }
)

الخطوة 5. تحميل المستند.

السياق الذي نستخدمه لمسار RAG الخاص بنا هو إعلان IBM الرسمي لإصدار Granite 3.1. يمكننا تحميل المدونة إلى مستند مباشرةً من صفحة الويب باستخدام WebBaseLoader من LangChain.

url = "https://www.ibm.com/qa-ar/new/announcements/ibm-granite-3-1-powerful-performance-long-context-and-more"
doc = WebBaseLoader(url).load()

الخطوة 6. إجراء تقسيم النص.

لنقدِّم مثالًا برمجيًا لكيفية تطبيق كل استراتيجية من استراتيجيات التقسيم التي ناقشناها سابقًا في هذا البرنامج التعليمي والمتاحة عبر LangChain.

التقسيم ثابت الحجم

لتنفيذ التقسيم ثابت الحجم، يمكننا استخدام CharacterTextSplitter من LangChain وتعيين chunk_size بالإضافة إلى chunk_overlap. يتم قياس chunk_size بعدد الأحرف. لا تتردد في تجربة قيم مختلفة. سنعيِّن أيضًا الفاصل ليكون حرف السطر الجديد حتى نتمكن من التمييز بين الفقرات. بالنسبة إلى الترميز، يمكننا استخدام برنامج الترميز granite-3.1-8b-instruct . يعمل برنامج الترميز على تفكيك النص إلى رموز يمكن للنموذج اللغوي معالجتها.

from langchain_text_splitters import CharacterTextSplitter
tokenizer = AutoTokenizer.from_pretrained(“ibm-granite/granite-3.1-8b-instruct”)
text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
                    tokenizer,
                    separator=”\n”, #default: “\n\n”
                    chunk_size=1200, chunk_overlap=200)
fixed_size_chunks = text_splitter.create_documents([doc[0].page_content])

يمكننا طباعة أحد المقاطع للحصول على فهم أفضل لهيكلها.

fixed_size_chunks[1]

الإخراج: (مقتطع)

Document(metadata={}, page_content=’As always, IBM’s historical commitment to open source is reflected in the permissive and standard open source licensing for every offering discussed in this article.\n\r\n Granite 3.1 8B Instruct: raising the bar for lightweight enterprise models\r\n \nIBM’s efforts in the ongoing optimization the Granite series are most evident in the growth of its flagship 8B dense model. IBM Granite 3.1 8B Instruct now bests most open models in its weight class in average scores on the academic benchmarks evaluations included in the Hugging Face OpenLLM Leaderboard...’)

يمكننا أيضًا استخدام برنامج الترميز للتحقق من العملية ولمراجعة عدد الرموز المميزة في كل مقطع. هذه الخطوة اختيارية ولأغراض توضيحية.

for idx, val in enumerate(fixed_size_chunks):
    token_count = len(tokenizer.encode(val.page_content))
    print(f”The chunk at index {idx} contains {token_count} tokens.”)

المخرجات:

The chunk at index 0 contains 1106 tokens.
The chunk at index 1 contains 1102 tokens.
The chunk at index 2 contains 1183 tokens.
The chunk at index 3 contains 1010 tokens.

رائع! يبدو أن أحجام المقاطع لدينا قد تم تنفيذها بشكل مناسب.

التقسيم التكراري

بالنسبة إلى التقسيم التكراري، يمكننا استخدام RecursiveCharacterTextSplitter من LangChain. مثل مثال التقسيم ثابت الحجم، يمكننا تجربة أحجام مختلفة للمقاطع ودرجة التداخل بينها.

from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
recursive_chunks = text_splitter.create_documents([doc[0].page_content])
recursive_chunks[:5]

المخرجات:

[Document(metadata={}, page_content=’IBM Granite 3.1: powerful performance, longer context and more’),
Document(metadata={}, page_content=’IBM Granite 3.1: powerful performance, longer context, new embedding models and more’),
Document(metadata={}, page_content=’Artificial Intelligence’),
Document(metadata={}, page_content=’Compute and servers’),
Document(metadata={}, page_content=’IT automation’)]

نجح الفاصل في تقسيم النص باستخدام الفواصل الافتراضية: ["\n\n"، و"\n"، و" "، و""].

التقسيم الدلالي

يتطلب التقسيم الدلالي نموذج تضمين أو تشفير. نستخدم نموذج granite-embedding-30m-english كنموذج التضمين الخاص بنا. يمكننا أيضًا طباعة أحد المقاطع لفهم هيكلها بشكل أفضل.

from langchain_huggingface import HuggingFaceEmbeddings
from langchain_experimental.text_splitter import SemanticChunker

embeddings_model = HuggingFaceEmbeddings(model_name=”ibm-granite/granite-embedding-30m-english”)
text_splitter = SemanticChunker(embeddings_model)
semantic_chunks = text_splitter.create_documents([doc[0].page_content])
semantic_chunks[1]

الإخراج: (مقتطع)

Document(metadata={}, page_content=’Our latest dense models (Granite 3.1 8B, Granite 3.1 2B), MoE models (Granite 3.1 3B-A800M, Granite 3.1 1B-A400M) and guardrail models (Granite Guardian 3.1 8B, Granite Guardian 3.1 2B) all feature a 128K token context length.We’re releasing a family of all-new embedding models. The new retrieval-optimized Granite Embedding models are offered in four sizes, ranging from 30M–278M parameters. Like their generative counterparts, they offer multilingual support across 12 different languages: English, German, Spanish, French, Japanese, Portuguese, Arabic, Czech, Italian, Korean, Dutch and Chinese. Granite Guardian 3.1 8B and 2B feature a new function calling hallucination detection capability, allowing increased control over and observability for agents making tool calls...’)

التقسيم القائم على المستندات

تتوافق المستندات بمختلف أنواع الملفات مع فواصل النصوص القائمة على المستندات في LangChain. لأغراض هذا البرنامج التعليمي، سنستخدم ملف Markdown. للحصول على أمثلة على تقسيم JSON التكراري وتقسيم التعليمات البرمجية وتقسيم HTML، راجِع وثائق LangChain.

من الأمثلة على ملف Markdown الذي يمكننا تحميله ملف README لنموذج Granite 3.1 على GitHub من IBM.

url = “https://raw.githubusercontent.com/ibm-granite/granite-3.1-language-models/refs/heads/main/README.md”
markdown_doc = WebBaseLoader(url).load()
markdown_doc

المخرجات:

[Document(metadata={‘source’: ‘https://raw.githubusercontent.com/ibm-granite/granite-3.1-language-models/refs/heads/main/README.md’}, page_content=’\n\n\n\n :books: Paper (comming soon)\xa0 | :hugs: HuggingFace Collection\xa0 | \n :speech_balloon: Discussions Page\xa0 | 📘 IBM Granite Docs\n\n\n---\n## Introduction to Granite 3.1 Language Models\nGranite 3.1 language models are lightweight, state-of-the-art, open foundation models that natively support multilinguality, coding, reasoning, and tool usage, including the potential to be run on constrained compute resources. All the models are publicly released under an Apache 2.0 license for both research and commercial use. The models\’ data curation and training procedure were designed for enterprise usage and customization, with a process that evaluates datasets for governance, risk and compliance (GRC) criteria, in addition to IBM\’s standard data clearance process and document quality checks...’)]

الآن، يمكننا استخدام MarkdownHeaderTextSplitter من LangChain لتقسيم الملف حسب نوع العنوان، الذي نحدده في قائمة headers_to_split_on. سنطبع أيضًا أحد المقاطع كمثال.

#document based chunking
from langchain_text_splitters import MarkdownHeaderTextSplitter
headers_to_split_on = [
    (“#”, “Header 1”),
    (“##”, “Header 2”),
    (“###”, “Header 3”),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on)
document_based_chunks = markdown_splitter.split_text(markdown_doc[0].page_content)
document_based_chunks[3]

المخرجات:

Document(metadata={‘Header 2’: ‘How to Use our Models?’, ‘Header 3’: ‘Inference’}, page_content=’This is a simple example of how to use Granite-3.1-1B-A400M-Instruct model. \n```python\nimport torch\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\ndevice = “auto”\nmodel_path = “ibm-granite/granite-3.1-1b-a400m-instruct”\ntokenizer = AutoTokenizer.from_pretrained(model_path)\n# drop device_map if running on CPU\nmodel = AutoModelForCausalLM.from_pretrained(model_path, device_map=device)\nmodel.eval()\n# change input text as desired\nchat = [\n{ “role”: “user”, “content”: “Please list one IBM Research laboratory located in the United States. You should only output its name and location.” },\n]\nchat = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)\n# tokenize the text\ninput_tokens = tokenizer(chat, return_tensors=”pt”).to(device)\n# generate output tokens\noutput = model.generate(**input_tokens,\nmax_new_tokens=100)\n# decode output tokens into text\noutput = tokenizer.batch_decode(output)\n# print output\nprint(output)\n```’)

كما ترى في الناتج، نجح التقسيم في فصل النصوص حسب نوع العنوان.

الخطوة 7. إنشاء مخزن المتجهات.

والآن بعد أن جربنا استراتيجيات التقسيم المختلفة، دعنا ننتقل إلى تطبيق RAG. في هذا البرنامج التعليمي، سنختار المقاطع الناتجة عن التقسيم الدلالي ونحوِّلها إلى تضمينات متجهية. مخزن المتجهات مفتوح المصدر الذي يمكننا استخدامه هو Chroma DB. يمكننا الوصول بسهولة إلى وظائف Chroma من خلال حزمة langchain_chroma.

لنبدأ بتهيئة قاعدة بيانات المتجهات Chroma، وتزويدها بنموذج التمثيلات المضمَّنة، ثم إضافة المستندات الناتجة عن التقسيم الدلالي.

vector_db = Chroma(
    collection_name=”example_collection”,
    embedding_function=embeddings_model,
    persist_directory=”./chroma_langchain_db”, # Where to save data locally
)
vector_db.add_documents(semantic_chunks)

المخرجات:

[‘84fcc1f6-45bb-4031-b12e-031139450cf8’,
‘433da718-0fce-4ae8-a04a-e62f9aa0590d’,
‘4bd97cd3-526a-4f70-abe3-b95b8b47661e’,
‘342c7609-b1df-45f3-ae25-9d9833829105’,
‘46a452f6-2f02-4120-a408-9382c240a26e’]

الخطوة 8. هيكلة قالب المطالبة.

بعد ذلك، يمكننا الانتقال إلى إنشاء قالب طلب مطالبة لتطبيق النموذج اللغوي الكبير الخاص بنا. يُتيح لنا قالب المطالبة هذا طرح أسئلة متعددة دون تغيير هيكل المطالبة الأولية. يمكننا أيضًا تقديم مخزن المتجهات الخاص بنا كأداة استرجاع. بهذه الخطوة، يتم الانتهاء من إعداد هيكل RAG.

from langchain.chains import create_retrieval_chain
from langchain.prompts import PromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain

prompt_template = """<|start_of_role|>user<|end_of_role|>Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.
{context}
Question: {input}<|end_of_text|>
<|start_of_role|>assistant<|end_of_role|>"""

qa_chain_prompt = PromptTemplate.from_template(prompt_template)
combine_docs_chain = create_stuff_documents_chain(llm, qa_chain_prompt)
rag_chain = create_retrieval_chain(vector_db.as_retriever(), combine_docs_chain)

الخطوة 9. مطالبة سلسلة RAG.

باستخدام سير عمل RAG المكتمل لدينا، دعنا نستدعي استعلام المستخدم. أولًا، يمكننا توجيه النموذج بطريقة استراتيجية دون أي سياق إضافي من مخزن المتجهات الذي أنشأناه؛ لاختبار إذا ما كان النموذج يستخدم معرفته المدمجة أم يعتمد فعليًا على سياق RAG. تُشير مدونة الإعلان عن Granite 3.1 إلى Docling، أداة IBM المخصصة لتحليل أنواع متعددة من المستندات وتحويلها إلى Markdown أو JSON. دعنا نسأل النموذج اللغوي الكبير عن Docling.

output = llm.invoke(“What is Docling?”)
output

المخرجات:

‘?\n\n”Docling” does not appear to be a standard term in English. It might be a typo or a slang term specific to certain contexts. If you meant “documenting,” it refers to the process of creating and maintaining records, reports, or other written materials that provide information about an activity, event, or situation. Please check your spelling or context for clarification.’

من الواضح أن النموذج لم يتم تدريبه على أي معلومات حول Docling، ودون أدوات أو مصادر خارجية لا يمكنه تزويدنا بهذه المعلومات. الآن، دعنا نحاول تقديم الاستعلام نفسه لسلسلة RAG التي أنشأناها.

rag_output = rag_chain.invoke({“input”: “What is Docling?”})
rag_output[‘answer’]

المخرجات:

‘Docling is a powerful tool developed by IBM Deep Search for parsing documents in various formats such as PDF, DOCX, images, PPTX, XLSX, HTML, and AsciiDoc, and converting them into model-friendly formats like Markdown or JSON. This enables easier access to the information within these documents for models like Granite for tasks such as RAG and other workflows. Docling is designed to integrate seamlessly with agentic frameworks like LlamaIndex, LangChain, and Bee, providing developers with the flexibility to incorporate its assistance into their preferred ecosystem. It surpasses basic optical character recognition (OCR) and text extraction methods by employing advanced contextual and element-based preprocessing techniques. Currently, Docling is open-sourced under the permissive MIT License, and the team continues to develop additional features, including equation and code extraction, as well as metadata extraction.’

رائع! استخدَم نموذج Granite سياق RAG بشكل صحيح لتزويدنا بمعلومات دقيقة حول Docling مع الحفاظ على الاتساق الدلالي. لقد أثبتنا أن النتيجة نفسها لم تكن ممكنة دون استخدام RAG.

ملخص

في هذا البرنامج التعليمي، أنشأتَ مسار RAG وجرّبت العديد من استراتيجيات التقسيم لتحسين دقة استرجاع النظام. باستخدام نموذج Granite 3.1، نجحنا في توليد استجابات دقيقة وملائمة لاستفسار المستخدم اعتمادًا على المستندات المقدَّمة كسياق. تم تحميل النص المستخدم في تطبيق RAG هذا من مدونة على موقع ibm.com تُعلن عن إصدار Granite 3.1. قدَّم لنا النموذج معلومات لا يمكن الوصول إليها إلا من خلال السياق المقدَّم، لأنها لم تكن ضمن قاعدة معارفه الأساسية.

للذين يريدون التعمّق أكثر، يمكن الاطِّلاع على نتائج مشروع يقارن أداء النماذج اللغوية الكبيرة باستخدام تقسيم HTML المنظم مقارنةً بتقسيم watsonx.

حلول ذات صلة
IBM watsonx.ai

تدريب الذكاء الاصطناعي التوليدي والتحقق من صحته وضبطه ونشره، وكذلك قدرات نماذج الأساس والتعلم الآلي باستخدام IBM watsonx.ai، وهو استوديو الجيل التالي من المؤسسات لمنشئي الذكاء الاصطناعي. أنشئ تطبيقات الذكاء الاصطناعي بسرعة أكبر وببيانات أقل.

اكتشف watsonx.ai
حلول الذكاء الاصطناعي

استفد من الذكاء الاصطناعي في عملك بالاستعانة بخبرة IBM الرائدة في مجال الذكاء الاصطناعي ومحفظة حلولها المتوفرة لك.

استكشف حلول الذكاء الاصطناعي
خدمات الذكاء الاصطناعي

أعدّ ابتكار عمليات ومهام سير العمل الحساسة بإضافة الذكاء الاصطناعي لتعزيز التجارب وصنع القرارات في الوقت الفعلي والقيمة التجارية.

استكشف خدمات الذكاء الاصطناعي
اتخِذ الخطوة التالية

احصل على وصول شامل إلى القدرات التي تغطي دورة حياة تطوير الذكاء الاصطناعي. تمكَّن من إنتاج حلول ذكاء اصطناعي قوية بفضل الواجهات سهلة الاستخدام وعمليات سير العمل السلسة وإمكانية الوصول إلى واجهات برمجة التطبيقات ومجموعات تطوير البرامج القياسية في الصناعة.

استكشف watsonx.ai احجز عرضًا توضيحيًا مباشرًا