Skip to main content
Text generation

Multi-turn conversations

The Qwen API is stateless. To implement multi-turn conversations, pass conversation history in each request. Use truncation, summarization, or retrieval to manage context and reduce token consumption.

This topic covers OpenAI-compatible Chat Completion and DashScope interfaces. For a simpler alternative, see OpenAI-compatible - Responses.

How it works

To implement multi-turn conversations, maintain a messages array. After each round, append the user's question and model's response and then use the updated array for the next request. The following example shows how the state of the messages array changes during a multi-turn conversation:
  1. First round Add the user's question to the messages array.
// Use a text model
[
    {"role": "user", "content": "Recommend a sci-fi movie about space exploration."}
]

// Use a multimodal model, for example, Qwen-VL
// {"role": "user",
//       "content": [{"type": "image_url","image_url": {"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251031/ownrof/f26d201b1e3f4e62ab4a1fc82dd5c9bb.png"}},
//                   {"type": "text", "text": "What products are shown in the image?"}]
// }
  1. Second round Add the model's response and the user's latest question to the messages array.
// Use a text model
[
    {"role": "user", "content": "Recommend a sci-fi movie about space exploration."},
    {"role": "assistant", "content": "I recommend 'XXX'. It is a classic sci-fi work."},
    {"role": "user", "content": "Who is the director of this movie?"}
]

// Use a multimodal model, for example, Qwen-VL
//[
//    {"role": "user", "content": [
//                    {"type": "image_url","image_url": {"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251031/ownrof/f26d201b1e3f4e62ab4a1fc82dd5c9bb.png"}},
//                   {"type": "text", "text": "What products are shown in the image?"}]},
//    {"role": "assistant", "content": "The image shows three items: a pair of light blue overalls, a blue and white striped short-sleeve shirt, and a pair of white sneakers."},
//    {"role": "user", "content": "What style are they?"}
//]

Getting started

  • OpenAI compatible
  • DashScope
Python
import os
from openai import OpenAI

def get_response(messages):
    client = OpenAI(
        # API keys vary by region. To obtain an API key, see https://www.alibabacloud.com/help/en/model-studio/get-api-key
        # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx",
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        # Singapore region URL. Replace {WorkspaceId} with your actual workspace ID. URLs vary by region.

        base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1",
    )
    # For a list of models, see https://www.alibabacloud.com/help/en/model-studio/getting-started/models
    completion = client.chat.completions.create(model="qwen3.8-max", messages=messages)
    return completion

# Initialize a messages array
messages = [
    {
        "role": "system",
        "content": """You are a salesperson at the Bailian phone store. You are responsible for recommending phones to users. The phones have two parameters: screen size (including 6.1-inch, 6.5-inch, and 6.7-inch) and resolution (including 2K and 4K).
        You can only ask the user for one parameter at a time. If the user does not provide complete information, you need to ask a follow-up question to get the missing parameter. When all parameters are collected, you must say: I have understood your purchase intention. Please wait.""",
    }
]
assistant_output = "Welcome to the Bailian phone store. What screen size are you looking for?"
print(f"Model output: {assistant_output}\n")
while "I have understood your purchase intention" not in assistant_output:
    user_input = input("Please enter: ")
    # Add the user's question to the messages list
    messages.append({"role": "user", "content": user_input})
    assistant_output = get_response(messages).choices[0].message.content
    # Add the model's response to the messages list
    messages.append({"role": "assistant", "content": assistant_output})
    print(f"Model output: {assistant_output}")
    print("\n")

For multimodal models

Multimodal models support images and audio in conversations. Implementation differs from text models as follows:
  • Construction of user messages: User messages for multimodal models can contain multimodal information, such as images and audio, in addition to text.
  • DashScope SDK interface: When you use the DashScope Python SDK, call the MultiModalConversation interface. When you use the DashScope Java SDK, call the MultiModalConversation class.
For multimodal models, see: Image and video understanding, and Kimi. For Qwen-Omni, see Non-real-time (Qwen-Omni). Qwen-VL-OCR and Qwen3-Omni-Captioner are designed for specific single-turn tasks and do not support multi-turn conversations.
  • OpenAI compatible
  • DashScope
Python
from openai import OpenAI
import os

client = OpenAI(
    # API keys vary by region. To obtain an API key, see https://www.alibabacloud.com/help/en/model-studio/get-api-key
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx"
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    # Singapore region URL. Replace {WorkspaceId} with your actual workspace ID. URLs vary by region.

    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
)
messages = [
        {"role": "user",
         "content": [
            {
                "type": "image_url",
                "image_url": {
                    "url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251031/ownrof/f26d201b1e3f4e62ab4a1fc82dd5c9bb.png"
                },
            },
            {"type": "text", "text": "What products are shown in the image?"},
        ],
    }
]
completion = client.chat.completions.create(
    model="qwen3-vl-plus",  #  You can replace this with other multimodal models and modify the messages as needed
    messages=messages,
    )
print(f"First round output: {completion.choices[0].message.content}")

assistant_message = completion.choices[0].message
messages.append(assistant_message.model_dump())
messages.append({
        "role": "user",
        "content": [
        {
            "type": "text",
            "text": "What style are they?"
        }
        ]
    })
completion = client.chat.completions.create(
    model="qwen3-vl-plus",
    messages=messages,
    )

print(f"Second round output: {completion.choices[0].message.content}")

For thinking models

Thinking models return reasoning_content (thinking process) and content (response). When updating messages, retain only content and ignore reasoning_content.
[
    {"role": "user", "content": "Recommend a sci-fi movie about space exploration."},
    {"role": "assistant", "content": "I recommend 'XXX'. It is a classic sci-fi work."}, # Do not add the reasoning_content field when you add to the context
    {"role": "user", "content": "Who is the director of this movie?"}
]
For more information about thinking models, see Deep thinking, Image and video understanding, and Visual reasoning.
For more information about implementing multi-turn conversations with Qwen3-Omni-Flash (thinking mode), see omni-modal.
  • OpenAI compatible
  • DashScope
  • Python
  • Node.js
  • HTTP

Sample code

from openai import OpenAI
import os

# Initialize the OpenAI client
client = OpenAI(
    # API keys vary by region. To obtain an API key, see https://www.alibabacloud.com/help/en/model-studio/get-api-key
    # If you have not configured the environment variable, replace the following line with your Model Studio API key: api_key="sk-xxx"
    api_key = os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
)

messages = []
conversation_idx = 1
while True:
    reasoning_content = ""  # Define the complete thinking process
    answer_content = ""     # Define the complete response
    is_answering = False   # Determine whether to end the thinking process and start responding
    print("="*20+f"Conversation Round {conversation_idx}"+"="*20)
    conversation_idx += 1
    user_input = input("Enter your message (type 'exit' to end): ")
    # Enter 'exit' to end the multi-turn conversation and avoid an endless loop
    if user_input.strip().lower() == "exit":
        print("Conversation ended.")
        break
    user_msg = {"role": "user", "content": user_input}
    messages.append(user_msg)
    # Create a chat completion request
    completion = client.chat.completions.create(
        # You can replace this with other deep thinking models as needed
        model="qwen3.8-max",
        messages=messages,
        extra_body={"enable_thinking": True},
        stream=True,
        # stream_options={
        #     "include_usage": True
        # }
    )
    print("\n" + "=" * 20 + "Thinking Process" + "=" * 20 + "\n")
    for chunk in completion:
        # If chunk.choices is empty, print usage
        if not chunk.choices:
            print("\nUsage:")
            print(chunk.usage)
        else:
            delta = chunk.choices[0].delta
            # Print the thinking process
            if hasattr(delta, 'reasoning_content') and delta.reasoning_content != None:
                print(delta.reasoning_content, end='', flush=True)
                reasoning_content += delta.reasoning_content
            else:
                # Start responding
                if delta.content != "" and is_answering is False:
                    print("\n" + "=" * 20 + "Complete Response" + "=" * 20 + "\n")
                    is_answering = True
                # Print the response process
                print(delta.content, end='', flush=True)
                answer_content += delta.content
    # Add the content of the model's response to the context
    messages.append({"role": "assistant", "content": answer_content})
    print("\n")

Going live

Multi-turn conversations can consume many tokens and exceed the model's context length, causing errors. Use these strategies to manage context and control costs.

1. Context management

The messages array grows with each round and may exceed the model's token limit. Use these methods to manage context length:

1.1. Context truncation

Keep only the most recent N rounds when history becomes too long. This is simple to implement but loses earlier conversation information.

1.2. Rolling summary

Summarize context as the conversation progresses to compress history and control length without losing core information: a. When history reaches 70% of max context length, extract an earlier part (such as the first half) and make a separate API call to generate a "memory summary". b. In the next request, replace the lengthy history with the "memory summary" and append recent rounds.

1.3. Vectorized retrieval

Rolling summaries can lose some information. To let the model recall relevant information from large conversation histories, use on-demand retrieval instead of linear context passing: a. After each conversation round, store the conversation in a vector database. b. When a user asks a question, retrieve relevant conversation records based on similarity. c. Combine the retrieved conversation records with the most recent user input and send the combined content to the model.

2. Cost control

Input tokens increase with each round, significantly raising costs. Use these cost management strategies:

2.1. Reduce input tokens

Use the context management strategies described previously to reduce input tokens and lower costs.

2.2. Use models that support context cache

In multi-turn requests, the messages array is repeatedly processed and billed. Model Studio provides context cache for models like qwen-max and qwen-plus, which reduces costs and improves response speed. Prioritize models that support context cache.
Context cache is enabled automatically—no code changes required.

Error codes

If the model call fails and returns an error message, see Error codes for resolution.
Token Plan
Model Playground
Statistics and Monitoring
Support