كيفية استخدام أولاما للاستجابة المتدفقة واستدعاء الأدوات

Mark Ponomarev

Mark Ponomarev

29 مايو 2025

كيفية استخدام أولاما للاستجابة المتدفقة واستدعاء الأدوات

سيرشدك هذا الدليل خلال كيفية استخدام إحدى ميزات Ollama الجديدة والقوية: القدرة على بث الاستجابات واستدعاء الأدوات (مثل الدوال أو واجهات برمجة التطبيقات) في الوقت الفعلي. هذه الميزة هي تغيير جذري في بناء تطبيقات الدردشة التي تبدو حية ويمكنها التفاعل مع العالم من حولها.

ما ستتعلمه في هذا البرنامج التعليمي:

💡
هل تبحث عن أداة رائعة لاختبار واجهات برمجة التطبيقات تولد توثيقًا جميلًا لواجهات برمجة التطبيقات؟

هل تبحث عن منصة متكاملة وشاملة لفريق المطورين لديك للعمل معًا بأقصى قدر من الإنتاجية؟

Apidog يلبي جميع مطالبك، ويحل محل Postman بسعر معقول أكثر بكثير!
button

البدء: ما ستحتاجه

لمتابعة الدليل، ستحتاج إلى بعض الأشياء:

فهم الأفكار الرئيسية: البث واستدعاء الأدوات

دعنا نفصل ما نعنيه بـ "بث الاستجابات" و"استدعاء الأدوات".

ما هو بث الاستجابات؟

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

مع Ollama، عندما تقوم بتمكين البث ("stream": true)، تحصل على هذه التحديثات المتزايدة.

كيف يعمل استدعاء الأدوات؟

يسمح استدعاء الأدوات لنماذج الذكاء الاصطناعي الخاصة بك بالقيام بأكثر من مجرد توليد النصوص. يمكنك تحديد "أدوات" - وهي في الأساس دوال أو واجهات برمجة تطبيقات خارجية - يمكن للذكاء الاصطناعي أن يقرر استخدامها للحصول على معلومات أو تنفيذ إجراءات.

على سبيل المثال، يمكن أن تكون الأداة:

تقوم بوصف هذه الأدوات لـ Ollama، وعندما يقرر الذكاء الاصطناعي أن استخدام أداة سيساعد في الإجابة على استعلام المستخدم، فإنه يشير إلى نيته لاستدعاء تلك الأداة بحجج محددة. يقوم تطبيقك بعد ذلك بتنفيذ الأداة ويمكنه إرسال النتائج مرة أخرى إلى الذكاء الاصطناعي لمواصلة المحادثة.

لماذا نجمع بين البث واستدعاء الأدوات؟

الترقية الكبيرة في Ollama هي أنه يمكنه الآن التعامل مع استدعاء الأدوات أثناء بث الاستجابات. هذا يعني أن تطبيقك يمكنه:

  1. استلام النص الأولي من النموذج (مبثوثًا).
  2. فجأة، قد يشير البث إلى الحاجة إلى استدعاء أداة.
  3. يقوم تطبيقك بمعالجة استدعاء الأداة.
  4. في هذه الأثناء، قد يستمر النموذج في بث المزيد من النصوص (على سبيل المثال، "حسنًا، سأحضر لك الطقس لـ...").
  5. بمجرد أن يحصل تطبيقك على نتيجة الأداة، يمكنك إرسالها مرة أخرى إلى النموذج، وسوف يستمر في بث استجابته، مستفيدًا الآن من مخرجات الأداة.

هذا يخلق تطبيقات ذكاء اصطناعي سريعة الاستجابة وقادرة للغاية.

ما هي النماذج التي تدعم هذه الميزات؟

لقد مكن Ollama هذه الميزة لعدة نماذج شائعة، بما في ذلك:

كيف تقوم بأول استدعاء أداة مبثوث باستخدام cURL

cURL هو طريقة رائعة لاختبار واجهة برمجة تطبيقات Ollama بسرعة. دعنا نطلب الطقس في تورنتو.

الخطوة 1: تصور أداتك

أداتنا ستكون get_current_weather. تحتاج إلى:

الخطوة 2: بناء أمر cURL

افتح الطرفية وجهز الأمر التالي. سنقوم بتفصيله:

curl <http://localhost:11434/api/chat> -d '{
  "model": "qwen3",
  "messages": [
    {
      "role": "user",
      "content": "What is the weather today in Toronto?"
    }
  ],
  "stream": true,
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_current_weather",
        "description": "Get the current weather for a location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The location to get the weather for, e.g. San Francisco, CA"
            },
            "format": {
              "type": "string",
              "description": "The format to return the weather in, e.g. \\\\\\\\'celsius\\\\\\\\' or \\\\\\\\'fahrenheit\\\\\\\\'",
              "enum": ["celsius", "fahrenheit"]
            }
          },
          "required": ["location", "format"]
        }
      }
    }
  ]
}'

التفصيل:

الخطوة 3: التنفيذ ومراقبة المخرجات

اضغط Enter. سترى سلسلة من كائنات JSON تظهر واحدة تلو الأخرى. هذا هو البث!

أمثلة مقتطفات من البث:

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "Okay, " }, "done": false
}

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "I will " }, "done": false
}

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "try to get that for you." }, "done": false
}

(قد يقوم النموذج بإخراج بعض رموز "التفكير" مثل <think>...celsius...</think> اعتمادًا على عمليته الداخلية، هذه أيضًا جزء من البث)

ثم، بشكل حاسم، قد ترى شيئًا كهذا:

{
  "model": "qwen3",
  "created_at": "2025-05-27T22:54:58.100509Z",
  "message": {
    "role": "assistant",
    "content": "", // Content might be empty when a tool call is made
    "tool_calls": [
      {
        "function": {
          "name": "get_current_weather",
          "arguments": { // The arguments the model decided on!
            "format": "celsius",
            "location": "Toronto"
          }
        }
      }
    ]
  },
  "done": false // Still not done, awaiting tool result
}

ما يجب ملاحظته:

في تطبيق حقيقي، عندما ترى جزء tool_calls، سيقوم الكود الخاص بك بما يلي:

  1. إيقاف معالجة البث مؤقتًا (أو التعامل معه بشكل غير متزامن).
  2. تنفيذ الدالة/واجهة برمجة التطبيقات الفعلية get_current_weather مع "تورنتو" و"مئوي".
  3. الحصول على النتيجة (على سبيل المثال، "20 درجة مئوية").
  4. إرسال هذه النتيجة مرة أخرى إلى Ollama في رسالة جديدة بـ role: "tool".
  5. سيستخدم النموذج بعد ذلك هذه المعلومات لمواصلة توليد استجابته، والتي سيتم بثها أيضًا.

كيفية بث استدعاءات الأدوات باستخدام Python

دعنا نطبق فكرة مشابهة في Python باستخدام مكتبة Ollama الرسمية.

الخطوة 1: تثبيت مكتبة Ollama Python

إذا لم تكن قد قمت بذلك بالفعل، قم بتثبيت أو ترقية المكتبة:

pip install -U ollama

الخطوة 2: تعريف أداتك والبرمجة في Python

تسمح لك حزمة تطوير برامج Ollama Python بذكاء بتمرير دوال Python مباشرة كأدوات. تقوم بفحص توقيع الدالة وسلسلة التوثيق (docstring) لإنشاء المخطط للذكاء الاصطناعي.

دعنا ننشئ مثالًا بسيطًا لأداة حسابية (الإدخال يستخدم add_two_numbers ولكن مثال الإخراج يظهر استدعاء subtract_two_numbers بواسطة النموذج. سنلتزم بـ add_two_numbers المقدمة للتعريف وندع النموذج يقرر ما يفعله بناءً على المطالبة).

import ollama

# Define the python function that can be used as a tool
def add_two_numbers(a: int, b: int) -> int:
  """
  Add two numbers.

  Args:
    a (int): The first number as an int.
    b (int): The second number as an int.

  Returns:
    int: The sum of the two numbers.
  """
  print(f"--- Tool 'add_two_numbers' called with a={a}, b={b} ---")
  return a + b

# --- Main conversation logic ---
messages = [{'role': 'user', 'content': 'What is three plus one?'}]
# Or, for the subtraction example in the original output:
# messages = [{'role': 'user', 'content': 'what is three minus one?'}]

print(f"User: {messages[0]['content']}")

# Make the chat request with streaming and the tool
# Note: ChatResponse type hint might be ollama.ChatResponse or similar depending on library version
response_stream = ollama.chat(
  model='qwen3', # Or another capable model
  messages=messages,
  tools=[
      { # You can also define the tool explicitly if needed, or pass the function directly
          'type': 'function',
          'function': {
              'name': 'add_two_numbers', # Must match the Python function name if you want it to be called directly by your code later
              'description': 'Add two integer numbers together.',
              'parameters': {
                  'type': 'object',
                  'properties': {
                      'a': {'type': 'integer', 'description': 'The first number'},
                      'b': {'type': 'integer', 'description': 'The second number'}
                  },
                  'required': ['a', 'b']
              }
          }
      }
      # Simpler way for Python: pass the function directly if the library supports easy schema generation from it
      # tools=[add_two_numbers] # The SDK can often create the schema from this
  ],
  stream=True
)

print("Assistant (streaming):")
full_response_content = ""
tool_call_info = None

for chunk in response_stream:
  # Print the streamed content part
  if chunk['message']['content']:
    print(chunk['message']['content'], end='', flush=True)
    full_response_content += chunk['message']['content']

  # Check for tool calls in the chunk
  if 'tool_calls' in chunk['message'] and chunk['message']['tool_calls']:
    tool_call_info = chunk['message']['tool_calls'][0] # Assuming one tool call for simplicity
    print(f"\\\\n--- Detected Tool Call: {tool_call_info['function']['name']} ---")
    break # Stop processing stream for now, handle tool call

  if chunk.get('done'):
      print("\\\\n--- Stream finished ---")
      if not tool_call_info:
          print("No tool call was made.")

# --- If a tool call was detected, handle it ---
if tool_call_info:
  tool_name = tool_call_info['function']['name']
  tool_args = tool_call_info['function']['arguments']

  print(f"Arguments for the tool: {tool_args}")

  # Here, you'd actually call your Python tool function
  if tool_name == "add_two_numbers":
    # For safety, ensure arguments are of correct type if necessary
    try:
        arg_a = int(tool_args.get('a'))
        arg_b = int(tool_args.get('b'))
        tool_result = add_two_numbers(a=arg_a, b=arg_b)
        print(f"--- Tool execution result: {tool_result} ---")

        # Now, send this result back to Ollama to continue the conversation
        messages.append({'role': 'assistant', 'content': full_response_content, 'tool_calls': [tool_call_info]})
        messages.append({
            'role': 'tool',
            'content': str(tool_result), # Result must be a string
            'tool_call_id': tool_call_info.get('id', '') # If your library/model provides a tool_call_id
        })

        print("\\\\n--- Sending tool result back to model ---")

        follow_up_response_stream = ollama.chat(
            model='qwen3',
            messages=messages,
            stream=True
            # No tools needed here unless you expect another tool call
        )

        print("Assistant (after tool call):")
        for follow_up_chunk in follow_up_response_stream:
            if follow_up_chunk['message']['content']:
                print(follow_up_chunk['message']['content'], end='', flush=True)
            if follow_up_chunk.get('done'):
                print("\\\\n--- Follow-up stream finished ---")
                break
    except ValueError:
        print("Error: Could not parse tool arguments as integers.")
    except Exception as e:
        print(f"An error occurred during tool execution or follow-up: {e}")
  else:
    print(f"Error: Unknown tool '{tool_name}' requested by the model.")

شرح كود Python:

  1. استيراد ollama.
  2. دالة add_two_numbers: هذه هي أداتنا. تساعد سلسلة التوثيق وتلميحات النوع (type hints) Ollama على فهم الغرض منها ومعاملاتها.
  3. messages: نبدأ المحادثة باستعلام المستخدم.
  4. ollama.chat(...):
  1. التكرار عبر response_stream:
  1. التعامل مع استدعاء الأداة:

تدفق المخرجات المتوقع:سترى سؤال المستخدم الأولي، ثم استجابة المساعد تتدفق. إذا قرر استدعاء add_two_numbers (أو subtract_two_numbers كما في مثال الإخراج الأصلي إذا كانت المطالبة للطرح)، سترى رسالة "Detected Tool Call"، الحجج، نتيجة دالة Python الخاصة بك، ثم المساعد يواصل استجابته باستخدام تلك النتيجة.

(أظهر مثال المخرجات الأصلي:

<think>
Okay, the user is asking ...
</think>

[ToolCall(function=Function(name='subtract_two_numbers', arguments={'a': 3, 'b': 1}))]

يشير هذا إلى عملية "التفكير" الداخلية للذكاء الاصطناعي ثم كائن استدعاء الأداة المهيكل الذي توفره حزمة تطوير برامج Python.)

كيفية بث استدعاءات الأدوات باستخدام JavaScript (Node.js)

الآن، دعنا نفعل الشيء نفسه باستخدام JavaScript، عادةً للواجهة الخلفية Node.js أو تطبيق ويب.

الخطوة 1: تثبيت مكتبة Ollama JavaScript

في دليل مشروعك، قم بتشغيل:

npm i ollama

الخطوة 2: تعريف مخطط الأداة والبرمجة في JavaScript

في JavaScript، عادةً ما تقوم بتعريف مخطط الأداة ككائن JSON.

import ollama from 'ollama';

// Describe the tool schema (e.g., for adding two numbers)
const addTool = {
    type: 'function',
    function: {
        name: 'addTwoNumbers',
        description: 'Add two numbers together',
        parameters: {
            type: 'object',
            required: ['a', 'b'],
            properties: {
                a: { type: 'number', description: 'The first number' },
                b: { type: 'number', description: 'The second number' }
            }
        }
    }
};

// Your actual JavaScript function that implements the tool
function executeAddTwoNumbers(a, b) {
    console.log(`--- Tool 'addTwoNumbers' called with a=${a}, b=${b} ---`);
    return a + b;
}

async function main() {
    const messages = [{ role: 'user', content: 'What is 2 plus 3?' }];
    console.log('User:', messages[0].content);

    console.log('Assistant (streaming):');
    let assistantResponseContent = "";
    let toolToCallInfo = null;

    try {
        const responseStream = await ollama.chat({
            model: 'qwen3', // Or another capable model
            messages: messages,
            tools: [addTool],
            stream: true
        });

        for await (const chunk of responseStream) {
            if (chunk.message.content) {
                process.stdout.write(chunk.message.content);
                assistantResponseContent += chunk.message.content;
            }
            if (chunk.message.tool_calls && chunk.message.tool_calls.length > 0) {
                toolToCallInfo = chunk.message.tool_calls[0]; // Assuming one tool call
                process.stdout.write(`\\\\n--- Detected Tool Call: ${toolToCallInfo.function.name} ---\\\\n`);
                break; // Stop processing stream to handle tool call
            }
            if (chunk.done) {
                process.stdout.write('\\\\n--- Stream finished ---\\\\n');
                if (!toolToCallInfo) {
                    console.log("No tool call was made.");
                }
                break;
            }
        }

        // --- If a tool call was detected, handle it ---
        if (toolToCallInfo) {
            const toolName = toolToCallInfo.function.name;
            const toolArgs = toolToCallInfo.function.arguments;

            console.log(`Arguments for the tool:`, toolArgs);

            let toolResult;
            if (toolName === 'addTwoNumbers') {
                toolResult = executeAddTwoNumbers(toolArgs.a, toolArgs.b);
                console.log(`--- Tool execution result: ${toolResult} ---`);

                // Append assistant's partial message and the tool message
                messages.push({
                    role: 'assistant',
                    content: assistantResponseContent, // Include content leading up to tool call
                    tool_calls: [toolToCallInfo]
                });
                messages.push({
                    role: 'tool',
                    content: toolResult.toString(), // Result must be a string
                    // tool_call_id: toolToCallInfo.id // If available and needed
                });

                console.log("\\\\n--- Sending tool result back to model ---");
                const followUpStream = await ollama.chat({
                    model: 'qwen3',
                    messages: messages,
                    stream: true
                });

                console.log("Assistant (after tool call):");
                for await (const followUpChunk of followUpStream) {
                    if (followUpChunk.message.content) {
                        process.stdout.write(followUpChunk.message.content);
                    }
                     if (followUpChunk.done) {
                        process.stdout.write('\\\\n--- Follow-up stream finished ---\\\\n');
                        break;
                    }
                }
            } else {
                console.error(`Error: Unknown tool '${toolName}' requested.`);
            }
        }

    } catch (error) {
        console.error('Error during Ollama chat:', error);
    }
}

main().catch(console.error);

شرح كود JavaScript:

  1. استيراد ollama.
  2. كائن addTool: هذا هو مخطط JSON الذي يصف أداتنا لـ Ollama.
  3. دالة executeAddTwoNumbers: دالة JavaScript الفعلية لأداتنا.
  4. دالة main غير المتزامنة:
  1. التعامل مع استدعاء الأداة: مشابه لـ Python، إذا تم تعيين toolToCallInfo:

تدفق المخرجات المتوقع (مشابه لأمثلة cURL و Python):سترى سؤال المستخدم، ثم استجابة المساعد تتدفق. عندما يقرر استدعاء addTwoNumbers، سيتم طباعة معلومات استدعاء الأداة، النتيجة من دالة JavaScript الخاصة بك، ثم الاستمرار في بث إجابة الذكاء الاصطناعي بناءً على تلك النتيجة.

كان مثال المخرجات الأصلي لـ JS يبدو كالتالي:

Question: What is 2 plus 3?
<think>
Okay, the user is asking...
</think>
Tool call: {
  function: {
    name: "addTwoNumbers",
    arguments: { a: 2, b: 3 },
  },
}

كيف يتعامل Ollama مع تحليل الأدوات أثناء البث

قد تتساءل كيف يدير Ollama بث النصوص وتحديد استدعاءات الأدوات بهذه السلاسة. يستخدم محللًا جديدًا ذكيًا تزايديًا.

لماذا هذا أفضل؟

نصيحة: تحسين الأداء باستخدام نافذة السياق

للتفاعلات الأكثر تعقيدًا، خاصة مع استدعاء الأدوات، يمكن أن يكون حجم "نافذة السياق" التي يستخدمها النموذج مهمًا. نافذة السياق الأكبر تعني أن النموذج يتذكر المزيد من المحادثة الحالية.

مثال: تعيين نافذة السياق باستخدام cURL(استخدم نموذجًا يدعم سياقات أكبر، مثل llama3.1 أو llama4 كما هو مقترح في المادة الأصلية - على الرغم من أن المثال يستخدم llama3.2)

curl -X POST "<http://localhost:11434/api/chat>" -d '{
  "model": "llama3.1",
  "messages": [
    {
      "role": "user",
      "content": "why is the sky blue?"
    }
  ],
  "options": {
    "num_ctx": 32000
  }
}'

جرب هذا الإعداد إذا وجدت أن استدعاء الأدوات ليس موثوقًا بالقدر الذي تريده.

إلى أين تذهب من هنا؟

لديك الآن الأساسيات لبناء تطبيقات ذكاء اصطناعي متطورة وفي الوقت الفعلي باستخدام Ollama من خلال بث الاستجابات واستدعاء الأدوات!

أفكار لاستكشافها:

ارجع إلى التوثيق الرسمي لـ Ollama ومستودع GitHub الخاص به (بما في ذلك "Tool Streaming Pull Request" المذكور في المصدر الأصلي للتعمق التقني) للحصول على أحدث التحديثات والمزيد من الأمثلة المتقدمة.

💡
هل تبحث عن أداة رائعة لاختبار واجهات برمجة التطبيقات تولد توثيقًا جميلًا لواجهات برمجة التطبيقات؟

هل تبحث عن منصة متكاملة وشاملة لفريق المطورين لديك للعمل معًا بأقصى قدر من الإنتاجية؟

Apidog يلبي جميع مطالبك، ويحل محل Postman بسعر معقول أكثر بكثير!
button

ممارسة تصميم API في Apidog

اكتشف طريقة أسهل لبناء واستخدام واجهات برمجة التطبيقات