Documentation for qwen 2.5 models Alibaba <article> #quickstart. md # Quickstart This guide helps you quickly start using Qwen2.5. We provide examples of [Hugging Face Transformers](https://github. com /huggingface/transformers) as well as [ModelScope](https://github. com /modelscope/modelscope), and [vLLM](https://github. com /vllm-project/vllm) for deployment. You can find Qwen2.5 models in the [Qwen2.5 collection](https://huggingface. co /collections/Qwen/qwen25-66e81a666513e518adb90d9e) at Hugging Face Hub. ## Hugging Face Transformers & ModelScope To get a quick start with Qwen2.5 , we advise you to try with the inference with `transformers` first. Make sure that you have installed `transformers>=4.37.0`. We advise you to use Python 3.10 or higher, and PyTorch 2.3 or higher. :::{dropdown} Install `transformers` * Install with `pip`: ```bash pip install transformers -U ``` * Install with `conda`: ```bash conda install conda-forge::transformers ``` * Install from source: ```bash pip install git+https://github. com /huggingface/transformers ``` ::: The following is a very simple code snippet showing how to run Qwen2.5 -7B-Instruct: ```python from transformers import AutoModelForCausalLM, AutoTokenizer model_name = "Qwen/Qwen2.5 -7B-Instruct" model = AutoModelForCausalLM. from_pretrained ( model_name, torch_dtype="auto", device_map="auto" ) tokenizer = AutoTokenizer. from_pretrained (model_name) prompt = "Give me a short introduction to large language model." messages = [ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, {"role": "user", "content": prompt}, ] text = tokenizer. apply_chat_template ( messages, tokenize=False, add_generation_prompt=True, ) model_inputs = tokenizer([text], return_tensors="pt").to(model. device ) generated_ids = model. generate ( **model_inputs, max_new_tokens=512, ) generated_ids = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs. input_ids , generated_ids) ] response = tokenizer. batch_decode (generated_ids, skip_special_tokens=True)[0] ``` As you can see, it's just standard usage for casual LMs in `transformers`! ### Streaming Generation Streaming mode for model chat is simple with the help of `TextStreamer`. Below we show you an example of how to use it: ```python... # Reuse the code before `model. generate ()` in the last code snippet from transformers import TextStreamer streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) generated_ids = model. generate ( **model_inputs, max_new_tokens=512, streamer=streamer, ) ``` It will print the text to the console or the terminal as being generated. ### ModelScope To tackle with downloading issues, we advise you to try [ModelScope](https://github. com /modelscope/modelscope). Before starting, you need to install `modelscope` with `pip`. `modelscope` adopts a programmatic interface similar (but not identical) to `transformers`. For basic usage, you can simply change the first line of code above to the following: ```python from modelscope import AutoModelForCausalLM, AutoTokenizer ``` For more information, please refer to [the documentation of `modelscope`](https://www. modelscope. cn/docs). ## vLLM for Deployment To deploy Qwen2.5 , we advise you to use vLLM. vLLM is a fast and easy-to-use framework for LLM inference and serving. In the following, we demonstrate how to build a OpenAI-API compatible API service with vLLM. First, make sure you have installed `vllm>=0.4.0`: ```bash pip install vllm ``` Run the following code to build up a vLLM service. Here we take Qwen2.5 -7B-Instruct as an example: ```bash python -m vllm. entrypoints. openai. api_server --model Qwen/Qwen2.5 -7B-Instruct ``` with `vllm>=0.5.3`, you can also use ```bash vllm serve Qwen/Qwen2.5 -7B-Instruct ``` Then, you can use the [create chat interface](https://platform. openai. com/docs/api-reference/chat/completions/create) to communicate with Qwen: ```bash curl http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{ "model": "Qwen/Qwen2.5 -7B-Instruct", "messages": [ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, {"role": "user", "content": "Tell me something about large language models."} ], "temperature": 0.7 , "top_p": 0.8 , "repetition_penalty": 1.05 , "max_tokens": 512 }' ``` or you can use Python client with `openai` Python package as shown below: ```python from openai import OpenAI # Set OpenAI's API key and API base to use vLLM's API server. openai_api_key = "EMPTY" openai_api_base = "http://localhost:8000/v1" client = OpenAI( api_key=openai_api_key, base_url=openai_api_base, ) chat_response = client. chat. completions. create ( model="Qwen/Qwen2.5 -7B-Instruct", messages=[ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, {"role": "user", "content": "Tell me something about large language models."}, ], temperature=0.7 , top_p=0.8 , max_tokens=512, extra_body={ "repetition_penalty": 1.05 , }, ) print("Chat response:", chat_response) ``` For more information, please refer to [the documentation of `vllm`](https://docs. vllm. ai/en/stable/). ## Next Step Now, you can have fun with Qwen2.5 models. Would love to know more about its usages? Feel free to check other documents in this documentation. <article> #concepts. md # Key Concepts ## Qwen Qwen (Chinese: 通义千问; pinyin: _Tongyi Qianwen_) is the large language model and large multimodal model series of the Qwen Team, Alibaba Group. Qwen is capable of natural language understanding, text generation, vision understanding, audio understanding, tool use, role play, playing as AI agent, etc. Both language models and multimodal models are pre-trained on large-scale multilingual and multimodal data and post-trained on quality data for aligning to human preferences. There is the proprietary version hosted exclusively at [Alibaba Cloud \[zh\]](https://help. aliyun. com/zh/model-studio/developer-reference/tongyi-qianwen-llm/) and the open-weight version. The spectrum for the open-weight models spans over - Qwen: the language models - [Qwen](https://github. com /QwenLM/Qwen): 1.8B , 7B, 14B, and 72B models - [Qwen1.5 ](https://github. com /QwenLM/Qwen1.5 /tree/v1.5 ): 0.5B , 1.8B , 4B, 14BA2.7B , 7B, 14B, 32B, 72B, and 110B models - [Qwen2](https://github. com /QwenLM/Qwen2/tree/v2.0 ): 0.5B , 1.5B , 7B, 57A14B, and 72B models - [Qwen2.5 ](https://github. com /QwenLM/Qwen2.5 /): 0.5B , 1.5B , 3B, 7B, 14B, 32B, and 72B models - Qwen-VL: the vision-language models - [Qwen-VL](https://github. com /QwenLM/Qwen-VL): 7B-based models - [Qwen2-VL](https://github. com /QwenLM/Qwen2-VL): 2B, 7B, and 72B-based models - Qwen-Audio: the audio-language models - [Qwen-Audio](https://github. com /QwenLM/Qwen-Audio): 7B-based model - [Qwen2-Audio](https://github. com /QwenLM/Qwen2-Audio): 7B-based models - CodeQwen/Qwen-Coder: the language models for coding - [CodeQwen1.5 ](https://github. com /QwenLM/CodeQwen1.5 ): 7B models - [Qwen2.5 -Coder](https://github. com /QwenLM/Qwen2-Coder): 7B models - Qwen-Math: the language models for mathematics - [Qwen2-Math](https://github. com /QwenLM/Qwen2-Math): 1.5B , 7B, and 72B models - [Qwen2.5 -Math](https://github. com /QwenLM/Qwen2.5 -Math): 1.5B , 7B, and 72B models **In this document, our focus is Qwen, the language models.** ## Causal Language Models Causal language models, also known as autoregressive language models or decoder-only language models, are a type of machine learning model designed to predict the next token in a sequence based on the preceding tokens. In other words, they generate text one token at a time, using the previously generated tokens as context. The "causal" aspect refers to the fact that the model only considers the past context (the already generated tokens) when predicting the next token, not any future tokens. Causal language models are widely used for various natural language processing tasks involving text completion and generation. They have been particularly successful in generating coherent and contextually relevant text, making them a cornerstone of modern natural language understanding and generation systems. **Takeaway: Qwen models are causal language models suitable for text completion.** :::{dropdown} Learn more about language models They are three main kinds of models that are commonly referred to as language models in deep learning: - Sequence-to-sequence models: T5 and the likes Sequence-to-sequence models use both an encoder to capture the entire input sequence and a decoder to generate an output sequence. They are widely used for tasks like machine translation, text summarization, etc. - Bidirectional models or encoder-only models: BERT and the likes Bidirectional models can access both past and future context in a sequence during training. They cannot generate sequential outputs in real-time due to the need for future context. They are widely used as embedding models and subsequently used for text classification. - Casual language models or decoder-only models: GPT and the likes Causal language models operate unidirectionally in a strictly forward direction, predicting each subsequent word based only on the previous words in the sequence. This unidirectional nature ensures that the model's predictions do not rely on future context, making them suitable for tasks like text completion and generation. ::: ### Pre-training & Base models Base language models are foundational models trained on extensive corpora of text to predict the next word in a sequence. Their main goal is to capture the statistical patterns and structures of language, enabling them to generate coherent and contextually relevant text. These models are versatile and can be adapted to various natural language processing tasks through fine-tuning. While adept at producing fluent text, they may require in-context learning or additional training to follow specific instructions or perform complex reasoning tasks effectively. For Qwen models, the base models are those without "-Instruct" indicators, such as Qwen2.5 -7B and Qwen2.5 -72B. **Takeaway: Use base models for in-context learning, downstream fine-tuning, etc.** ### Post-training & Instruction-tuned models Instruction-tuned language models are specialized models designed to understand and execute specific instructions in conversational styles. These models are fine-tuned to interpret user commands accurately and can perform tasks such as summarization, translation, and question answering with improved accuracy and consistency. Unlike base models, which are trained on large corpora of text, instruction-tuned models undergo additional training using datasets that contain examples of instructions and their desired outcomes, often in multiple turns. This kind of training makes them ideal for applications requiring targeted functionalities while maintaining the ability to generate fluent and coherent text. For Qwen models, the instruction-tuned models are those with the "-Instruct" suffix, such as Qwen2.5 -7B-Instruct and Qwen2.5 -72B-Instruct. [^instruct-chat] **Takeaway: Use instruction-tuned models for conducting tasks in conversations, downstream fine-tuning, etc.** [^instruct-chat]: Previously, they are known as the chat models and with the "-Chat" suffix. Starting from Qwen2, the name is changed to follow the common practice. For Qwen, "-Instruct" and "-Chat" should be regarded as synonymous. ## Tokens & Tokenization Tokens represent the fundamental units that models process and generate. They can represent texts in human languages (regular tokens) or represent specific functionality like keywords in programming languages (control tokens [^special]). Typically, a tokenizer is used to split text into regular tokens, which can be words, subwords, or characters depending on the specific tokenization scheme employed, and furnish the token sequence with control tokens as needed. The vocabulary size, or the total number of unique tokens a model recognizes, significantly impacts its performance and versatility. Larger language models often use sophisticated tokenization methods to handle the vast diversity of human language while keeping the vocabulary size manageable. Qwen use a relatively large vocabulary of 151,646 tokens in total. [^special]: Control tokens can be called special tokens. However, the meaning of special tokens need to be interpreted based on the contexts: special tokens may contain extra regular tokens. **Takeaway: Tokenization method and vocabulary size is important.** ### Byte-level Byte Pair Encoding Qwen adopts a subword tokenization method called Byte Pair Encoding (BPE), which attempts to learn the composition of tokens that can represent the text with the fewest tokens. For example, the string " tokenization" is decomposed as " token" and "ization" (note that the space is part of the token). Especially, the tokenization of Qwen ensures that there is no unknown words and all texts can be transformed to token sequences. There are 151,643 tokens as a result of BPE in the vocabulary of Qwen, which is a large vocabulary efficient for diverse languages. As a rule of thumb, 1 token is 3~4 characters for English texts and 1.5 ~1.8 characters for Chinese texts. **Takeaway: Qwen processes texts in subwords and there are no unknown words.** :::{dropdown} Learn more about tokenization in Qwen Qwen uses byte-level BPE (BBPE) on UTF-8 encoded texts. It starts by treating each byte as a token and then iteratively merges the most frequent pairs of tokens occurring the texts into larger tokens until the desired vocabulary size is met. In byte-level BPE, minimum 256 tokens are needed to tokenize every piece of text and avoid the out of vocabulary (OOV) problem. In comparison, character-level BPE needs every Unicode character in its vocabulary to avoid OOV and the Unicode Standard contains 154,998 characters as of Unicode Version 16.0. One limitation to keep in mind for byte-level BPE is that the individual tokens in the vocabulary may not be seemingly semantically meaningful or even valid UTF-8 byte sequences, and in certain aspects, they should be viewed as a text compression scheme. ::: ### Control Tokens & Chat Template Control tokens and chat templates both serve as mechanisms to guide the model's behavior and outputs. Control tokens are special tokens inserted into the sequence that signifies meta information. For example, in pre-training, multiple documents may be packed into a single sequence. For Qwen, the control token "<|endoftext|>" is inserted after each document to signify that the document has ended and a new document will proceed. Chat templates provide a structured format for conversational interactions, where predefined placeholders or prompts are used to elicit responses from the model that adhere to a desired dialogue flow or context. Different models may use different kinds of chat template to format the conversations. It is crucial to use the designated one to ensure the precise control over the LLM's generation process. Qwen uses the following format (ChatML[^chatml]), making use of control tokens to format each turn in the conversations ```text <|im_start|>{{role}} {{content}}<|im_end|> ``` The user input take the role of `user` and the model generation takes the role of `assistant`. Qwen also supports the meta message that instruct the model to perform specific actions or generate text with certain characteristics, such as altering tone, style, or content, which takes the role of `system` and the content defaults to "You are Qwen, created by Alibaba Cloud. You are a helpful assistant." The following is a full example: ```text <|im_start|>system You are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|> <|im_start|>user hello<|im_end|> <|im_start|>assistant Hello! How can I assist you today? Let me know if you have any questions or need help with something. I'm here to help!<|im_end|> <|im_start|>user Explain large language models like I'm 5.<|im_end|> <|im_start|>assistant Sure, I'd be happy to explain large language models in a simple way! Imagine that you have a friend who is really good at playing with words. This friend has read so many books and talked to so many people that they know how to use words in all sorts of different ways. They can tell stories, answer questions, and even make up new words if they need to. Now imagine that this friend is actually a computer program, called a "large language model". It's been trained on lots and lots of text, like books, articles, and conversations, so it knows how to use words just like your word-loving friend does. When you ask the model a question or give it a task, it uses all the knowledge it's learned to come up with a response that makes sense. Just like your friend might sometimes make mistakes or say things in a funny way, the large language model isn't perfect either. But it's still really impressive, because it can understand and generate human language in a way that was once thought impossible for machines!<|im_end|><|endoftext|> ``` Starting from Qwen2.5 , the Qwen model family including multimodal and specialized models will use a unified vocabulary, which contains control tokens from all subfamilies. There are 22 control tokens in the vocabulary of Qwen2.5 , making the vocabulary size totaling 151,665: - 1 general: `<|endoftext|>` - 2 for chat: `<|im_start|>` and `<|im_end|>` - 2 for tool use: `<tool_call>` and `</tool_call>` - 11 for vision - 6 for coding **Takeaway: Qwen uses ChatML with control tokens for chat template.** [^chatml]: For historical reference only, ChatML is first described by the OpenAI Python SDK. The last available version is [this](https://github. com /openai/openai-python/blob/v0.28.1/chatml. md ). Please also be aware that that document lists use cases intended for OpenAI models. For Qwen2.5 models, please only use as in our guide. ## Length Limit As Qwen models are causal language models, in theory there is only one length limit of the entire sequence. However, since there is often packing in training and each sequence may contain multiple individual pieces of texts. **How long the model can generate or complete ultimately depends on the use case and in that case how long each document (for pre-training) or each turn (for post-training) is in training.** For Qwen2.5 , the packed sequence length in training is 32,768 tokens.[^yarn] The maximum document length in pre-training is this length. The maximum message length for user and assistant is different in post-training. In general, the assistant message could be up to 8192 tokens. [^yarn]: The sequence length can be extended to 131,072 tokens for Qwen2.5 -7B, Qwen2.5 -14B, Qwen2.5 -32B, and Qwen2.5 -72B models with YaRN. Please refer to the model card on how to enable YaRN in vLLM. **Takeaway: Qwen2.5 models can process texts of 32K or 128K tokens and up to 8K tokens can be assistant output.** <article> #function_call. md --- myst: number_code_blocks: ["python3"] --- # Function Calling ## Preface Function calling with large language models is a huge and evolving topic. It is particularly important for AI applications: - either for AI-native applications that strive to work around the shortcomings of current AI technology, - or for existing applications that seeks the integration of AI technology to improve performance, user interaction and experience, or efficiency. This guide will not delve into those discussions or which role an LLM should play in an application and the related best practice. Those views are reflected in the design of AI application frameworks: from LangChain to LlamaIndex to QwenAgent. Instead, we will talk about how Qwen2.5 can be used to support function calling and how it can be used to achieve your goals, from the inference usage for developing application to the inner workings for hardcore customizations. In this guide, - We will first demonstrate how to use function calling with Qwen2.5. - Then, we will introduce the technical details on functional calling with Qwen2.5 , which are mainly about the templates. Before starting, there is one thing we have not yet introduced, that is... ## What is function calling? :::{Note} There is another term "tool use" that may be used to refer to the same concept. While some may argue that tools are a generalized form of functions, at present, their difference exists only technically as different I/O types of programming interfaces. ::: Large language models (LLMs) are powerful things. However, sometimes LLMs by themselves are simply not capable enough. - On the one hand, LLMs have inherent modeling limitations. For one, they do not know things that are not in their training data, which include those happened after their training ended. In addition, they learn things in the way of likelihood, which suggests that they may not be precise enough for tasks with fixed rule sets, e. g., mathematical computation. - On the other hand, it is not easy to use LLMs as a Plug-and-Play service programmatically with other things. LLMs mostly talk in words that are open to interpretation and thus ambiguous, while other software or applications or systems talk in code and through programming interfaces that are pre-defined and fixed and structured. To this end, function calling establishes a common protocol that specifies how LLMs should interact with the other things. The procedure is mainly as follows: 1. The application provides a set of functions and the instructions of the functions to an LLM. 2. The LLM choose to or not to, or is forced to use one or many of the functions, in response to user queries. 3. If the LLM chooses to use the functions, it states how the functions should be used based on the function instructions. 4. The chosen functions are used as such by the application and the results are obtained, which are then given to the LLM if further interaction is needed. They are many ways for LLMs to understand and follow this protocol. As always, the key is prompt engineering or an internalized template known by the model. Qwen2.5 were pre-trained with various types of templates that could support function calling, so that users can directly make use of this procedure. ## Inference with Function Calling :::{note} Please be aware that the inference usage is subject to change as the frameworks and the Qwen models evolve. ::: As function calling is essentially implemented using prompt engineering, you could manually construct the model inputs for Qwen2 models. However, frameworks with function calling support can help you with all that laborious work. In the following, we will introduce the usage (via dedicated function calling chat template) with - **Qwen-Agent**, - **Hugging Face transformers**, - **Ollama**, and - **vLLM**. If you are familiar with the usage of OpenAI API, you could also directly use the OpenAI-compatible API services for Qwen2.5. However, not all of them support function calling for Qwen2.5. Currently, supported solutions include the self-hosted service by [Ollama](https://github. com /ollama/ollama/blob/main/docs/openai. md ) or [vLLM](https://docs. vllm. ai/en/stable/serving/openai_compatible_server. html #tool-calling-in-the-chat-completion-api) and the cloud service of [ModelStudio \[zh\]](https://help. aliyun. com/zh/model-studio/developer-reference/compatibility-of-openai-with-dashscope#97e2b45391x08). If you are familiar with application frameworks, e. g., LangChain, you can also use function calling abilities in Qwen2.5 via ReAct Prompting. ### The Example Case Let's also use an example to demonstrate the inference usage. We assume **Python 3.11 ** is used as the programming language. **Scenario**: Suppose we would like to ask the model about the temperature of a location. Normally, the model would reply that it cannot provide real-time information. But we have two tools that can be used to obtain the current temperature of and the temperature at a given date of a city respectively, and we would like the model to make use of them. To set up the example case, you can use the following code: :::{dropdown} Preparation Code :name: prepcode ```python import json def get_current_temperature(location: str, unit: str = "celsius"): """Get current temperature at a location. Args: location: The location to get the temperature for, in the format "City, State, Country". unit: The unit to return the temperature in. Defaults to "celsius". (choices: ["celsius", "fahrenheit"]) Returns: the temperature, the location, and the unit in a dict """ return { "temperature": 26.1 , "location": location, "unit": unit, } def get_temperature_date(location: str, date: str, unit: str = "celsius"): """Get temperature at a location and date. Args: location: The location to get the temperature for, in the format "City, State, Country". date: The date to get the temperature for, in the format "Year-Month-Day". unit: The unit to return the temperature in. Defaults to "celsius". (choices: ["celsius", "fahrenheit"]) Returns: the temperature, the location, the date and the unit in a dict """ return { "temperature": 25.9 , "location": location, "date": date, "unit": unit, } def get_function_by_name(name): if name == "get_current_temperature": return get_current_temperature if name == "get_temperature_date": return get_temperature_date TOOLS = [ { "type": "function", "function": { "name": "get_current_temperature", "description": "Get current temperature at a location.", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": 'The location to get the temperature for, in the format "City, State, Country".', }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": 'The unit to return the temperature in. Defaults to "celsius".', }, }, "required": ["location"], }, }, }, { "type": "function", "function": { "name": "get_temperature_date", "description": "Get temperature at a location and date.", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": 'The location to get the temperature for, in the format "City, State, Country".', }, "date": { "type": "string", "description": 'The date to get the temperature for, in the format "Year-Month-Day".', }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": 'The unit to return the temperature in. Defaults to "celsius".', }, }, "required": ["location", "date"], }, }, }, ] MESSAGES = [ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30"}, {"role": "user", "content": "What's the temperature in San Francisco now? How about tomorrow?"}, ] ``` ::: In particular, the tools should be described using JSON Schema and the messages should contain as much available information as possible. You can find the explanations of the tools and messages below: :::{dropdown} Example Tools The tools should be described using the following JSON: ```json [ { "type": "function", "function": { "name": "get_current_temperature", "description": "Get current temperature at a location.", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "The location to get the temperature for, in the format \"City, State, Country\"." }, "unit": { "type": "string", "enum": [ "celsius", "fahrenheit" ], "description": "The unit to return the temperature in. Defaults to \"celsius\"." } }, "required": [ "location" ] } } }, { "type": "function", "function": { "name": "get_temperature_date", "description": "Get temperature at a location and date.", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "The location to get the temperature for, in the format \"City, State, Country\"." }, "date": { "type": "string", "description": "The date to get the temperature for, in the format \"Year-Month-Day\"." }, "unit": { "type": "string", "enum": [ "celsius", "fahrenheit" ], "description": "The unit to return the temperature in. Defaults to \"celsius\"." } }, "required": [ "location", "date" ] } } } ] ``` For each **tool**, it is a JSON object with two fields: - `type`: a string specifying the type of the tool, currently only `"function"` is valid - `function`: an object detailing the instructions to use the function For each **function**, it is a JSON object with three fields: - `name`: a string indicating the name of the function - `description`: a string describing what the function is used for - `parameters`: [a JSON Schema](https://json-schema. org /learn/getting-started-step-by-step) that specifies the parameters the function accepts. Please refer to the linked documentation for how to compose a JSON Schema. Notable fields include `type`, `required`, and `enum`. Most frameworks use the tool format and some may use the function format. Which one to use should be obvious according to the naming. ::: :::{dropdown} Example Messages Our query is `What's the temperature in San Francisco now? How about tomorrow?`. Since the model does not know what the current date is, let alone tomorrow, we should provide the date in the inputs. Here, we decide to supply that information in the system message after the default system message `You are Qwen, created by Alibaba Cloud. You are a helpful assistant.`. You could append the date to user message in your application code. ```json [ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30"}, {"role": "user", "content": "What's the temperature in San Francisco now? How about tomorrow?"} ] ``` ::: ### Qwen-Agent [Qwen-Agent](https://github. com /QwenLM/Qwen-Agent) is actually a Python Agent framework for developing AI applications. Although its intended use cases are higher-level than efficient inference, it does contain the **canonical implementation** of function calling for Qwen2.5. It provides the function calling ability for Qwen2.5 to an OpenAI-compatible API through templates that is transparent to users. {#note-official-template} It's worth noting that since a lot of stuff can be done under the scene with application frameworks, currently the official function calling implementation for Qwen2.5 is very flexible and beyond simple templating, making it hard to adapt it other frameworks that use less capable templating engines. Before starting, let's make sure the latest library is installed: ```bash pip install -U qwen-agent ``` For this guide, we are at version v0.0.10. #### Preparing Qwen-Agent can wrap an OpenAI-compatible API that does not support function calling. You can serve such an API with most inference frameworks or obtain one from cloud providers like DashScope or Together. Assuming there is an OpenAI-compatible API at `http://localhost:8000/v1`, Qwen-Agent provides a shortcut function `get_chat_model` to obtain a model inference class with function calling support: ```python from qwen_agent. llm import get_chat_model llm = get_chat_model({ "model": "Qwen/Qwen2.5 -7B-Instruct", "model_server": "http://localhost:8000/v1", "api_key": "EMPTY", }) ``` In the above, `model_server` is the `api_base` common used in other OpenAI-compatible API clients. It is advised to provide the `api_key` (but not via plaintext in the code), even if the API server does not check it, in which case, you can set it to anything. For model inputs, the common message structure for system, user, and assistant history should be used: ```python messages = MESSAGES[:] # [ # {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30"}, # {"role": "user", "content": "What's the temperature in San Francisco now? How about tomorrow?"} # ] ``` We add the current date to the system message so that the "tomorrow" in the user message is anchored. It can also be added to the user message if one desires. At the time, Qwen-Agent works with functions instead of tools. This requires a small change to our tool descriptions, that is, extracting the function fields: ```python functions = [tool["function"] for tool in TOOLS] ``` #### Tool Calls and Tool Results To interact with the model, the `chat` method should be used: ```python for responses in llm. chat ( messages=messages, functions=functions, extra_generate_cfg=dict(parallel_function_calls=True), ): pass messages. extend (responses) ``` In the above code, the `chat` method receives the `messages`, the `functions`, and an `extra_generate_cfg` parameter. You can put sampling parameters, such as `temperature`, and `top_p`, in the `extra_generate_cfg`. Here, we add to it a special control `parallel_function_calls` provided by Qwen-Agent. As its name suggests, it will enable parallel function calls, which means that the model may generate multiple function calls for a single turn as it deems fit. The `chat` method returns a generator of list, each of which may contain multiple messages. Since we enable `parallel_function_calls`, we should get two messages in the responses: ```python [ {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_current_temperature', 'arguments': '{"location": "San Francisco, CA, USA", "unit": "celsius"}'}}, {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_temperature_date', 'arguments': '{"location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'}}, ] ``` As we can see, Qwen-Agent attempts to parse the model generation in an easier to use structural format. The details related to function calls are placed in the `function_call` field of the messages: - `name`: a string representing the function to call - `arguments`: a JSON-formatted string representing the arguments the function should be called with Note that Qwen2.5 -7B-Instruct is quite capable: - It has followed the function instructions to add the state and the country to the location. - It has correctly induced the date of tomorrow and given in the format required by the function. Then comes the critical part -- checking and applying the function call: ```python3 for message in responses: if fn_call := message. get ("function_call", None): fn_name: str = fn_call['name'] fn_args: dict = json. loads (fn_call["arguments"]) fn_res: str = json. dumps (get_function_by_name(fn_name)(**fn_args)) messages. append ({ "role": "function", "name": fn_name, "content": fn_res, }) ``` To get tool results: - line 1: We should iterate the function calls in the order the model generates them. - line 2: We can check if a function call is needed as deemed by the model by checking the `function_call` field of the generated messages. - line 3-4: The related details including the name and the arguments of the function can also be found there, which are `name` and `arguments` respectively. - line 6: With the details, one should call the function and obtain the results. Here, we assume there is a function named [`get_function_by_name`](#prepcode) to help us get the related function by its name. - line 8-12: With the result obtained, add the function result to the messages as `content` and with `role` as `"function"`. Now the messages are ```python [ {'role': 'system', 'content': 'You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30'}, {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"}, {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_current_temperature', 'arguments': '{"location": "San Francisco, CA, USA", "unit": "celsius"}'}}, {'role': 'assistant', 'content': '', 'function_call': {'name': 'get_temperature_date', 'arguments': '{"location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'}}, {'role': 'function', 'name': 'get_current_temperature', 'content': '{"temperature": 26.1 , "location": "San Francisco, CA, USA", "unit": "celsius"}'}, {'role': 'function', 'name': 'get_temperature_date', 'content': '{"temperature": 25.9 , "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'}, ] ``` #### Final Response Finally, run the model again to get the final model results: ```python for responses in llm. chat (messages=messages, functions=functions): pass messages. extend (responses) ``` The final response should be like ```python {'role': 'assistant', 'content': 'Currently, the temperature in San Francisco is approximately 26.1 °C. Tomorrow, on 2024-10-01, the temperature is forecasted to be around 25.9 °C.'} ``` ### Hugging Face transformers Since function calling is based on prompt engineering and templates, `transformers` supports it with its tokenizer utilities, in particular, the `tokenizer. apply_chat_template ` method, which hides the sophistication of constructing the model inputs, using the Jinja templating engine. However, it means that users should handle the model output part on their own, which includes parsing the generated function call message. The blog piece [_Tool Use, Unified_](https://huggingface. co /blog/unified-tool-use) is very helpful in understanding its design. Be sure to take a look. Tool use API is available in transformers since v4.42.0. Before starting, let's check that: ```bash pip install "transformers>4.42.0" ``` For this guide, we are at version v4.44.2. #### Preparing For Qwen2.5 , the chat template in `tokenizer_config. json ` has already included support for the Hermes-style tool use. We simply need to load the model and the tokenizer: ```python from transformers import AutoModelForCausalLM, AutoTokenizer model_name_or_path = "Qwen/Qwen2.5 -7B-Instruct" tokenizer = AutoTokenizer. from_pretrained (model_name_or_path) model = AutoModelForCausalLM. from_pretrained ( model_name_or_path, torch_dtype="auto", device_map="auto", ) ``` The inputs are the same with those in [the preparation code](#prepcode): ```python tools = TOOLS messages = MESSAGES[:] ``` In `transformers`, you can also directly use Python functions as tools with certain constraints[^get_json_schema_note]: ```python tools = [get_current_temperature, get_temperature_date] ``` [^get_json_schema_note]: `transformers` will use `transformers. utils. get_json_schema` to generate the tool descriptions from Python functions. There are some gotchas with `get_json_schema`, and it is advised to check [its doc \[v4.44.2\]](https://github. com /huggingface/transformers/blob/v4.44.2/src/transformers/utils/chat_template_utils. py #L183-L288) before relying on it. - The function should use Python type hints for parameter types and has a Google-style docstring for function description and parameter descriptions. - Supported types are limited, since the types needs to be mapped to JSON Schema. In particular, `typing. Literal ` is not supported. You can instead add `(choices:...)` at the end of a parameter description, which will be mapped to a `enum` type in JSON Schema. Please be aware that all the returned results in the examples in the linked docstring are actually the content of the `function` field in the actual returned results. #### Tool Calls and Tool Results To construct the input sequence, we should use the `apply_chat_template` method and then let the model continue the texts: ```python text = tokenizer. apply_chat_template (messages, tools=tools, add_generation_prompt=True, tokenize=False) inputs = tokenizer(text, return_tensors="pt").to(model. device ) outputs = model. generate (**inputs, max_new_tokens=512) output_text = tokenizer. batch_decode (outputs)[0][len(text):] ``` The output texts should be like ```text <tool_call> {"name": "get_current_temperature", "arguments": {"location": "San Francisco, CA, USA"}} </tool_call> <tool_call> {"name": "get_temperature_date", "arguments": {"location": "San Francisco, CA, USA", "date": "2024-10-01"}} </tool_call><|im_end|> ``` Now we need to do two things: 1. Parse the generated tool calls to a message and add them to the messages, so that the model knows which tools are used. 2. Obtain the results of the tools and add them to the messages, so that the model knows the results of the tool calls. In `transformers`, the tool calls should be a field of assistant messages. Let's use a simple function called `try_parse_tool_calls` to parse the tool calls: {#parse-function} ```python import re def try_parse_tool_calls(content: str): """Try parse the tool calls.""" tool_calls = [] offset = 0 for i, m in enumerate(re. finditer (r"<tool_call>\n(.+)?\n</tool_call>", content)): if i == 0: offset = m. start () try: func = json. loads (m. group (1)) tool_calls. append ({"type": "function", "function": func}) if isinstance(func["arguments"], str): func["arguments"] = json. loads (func["arguments"]) except json. JSONDecodeError as e: print(f"Failed to parse tool calls: the content is {m. group (1)} and {e}") pass if tool_calls: if offset > 0 and content[:offset].strip(): c = content[:offset] else: c = "" return {"role": "assistant", "content": c, "tool_calls": tool_calls} return {"role": "assistant", "content": re. sub (r"<\|im_end\|>$", "", content)} ``` This function does not cover all possible scenarios and thus is prone to errors. But it should suffice for the purpose of this guide. :::{note} The template in the `tokenizer_config. json ` assumes that the generated content alongside tool calls is in the same message instead of separate assistant messages, e. g., ```json { "role": "assistant", "content": "To obtain the current temperature, I should call the functions `get_current_temperate`.", "tool_calls": [ {"type": "function", "function": {"name": "get_current_temperature", "arguments": {"location": "San Francisco, CA, USA", "unit": "celsius"}}} ] } ``` instead of ```json [ { "role": "assistant", "content": "To obtain the current temperature, I should call the functions `get_current_temperate`.", }, { "role": "assistant", "content": "", "tool_calls": [ {"type": "function", "function": {"name": "get_current_temperature", "arguments": {"location": "San Francisco, CA, USA", "unit": "celsius"}}} ] } ] ``` This is implemented roughly in `try_parse_tool_calls` but keep that in mind if you are writing your own tool call parser. ::: ```python messages. append (try_parse_tool_calls(output_text)) if tool_calls := messages[-1].get("tool_calls", None): for tool_call in tool_calls: if fn_call := tool_call. get ("function"): fn_name: str = fn_call["name"] fn_args: dict = fn_call["arguments"] fn_res: str = json. dumps (get_function_by_name(fn_name)(**fn_args)) messages. append ({ "role": "tool", "name": fn_name, "content": fn_res, }) ``` The messages now should be like ```python [ {'role': 'system', 'content': 'You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30'}, {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"}, {'role': 'assistant', 'content': '', 'tool_calls': [ {'type': 'function', 'function': {'name': 'get_current_temperature', 'arguments': {'location': 'San Francisco, CA, USA'}}}, {'type': 'function', 'function': {'name': 'get_temperature_date', 'arguments': {'location': 'San Francisco, CA, USA', 'date': '2024-10-01'}}}, ]}, {'role': 'tool', 'name': 'get_current_temperature', 'content': '{"temperature": 26.1 , "location": "San Francisco, CA, USA", "unit": "celsius"}'}, {'role': 'tool', 'name': 'get_temperature_date', 'content': '{"temperature": 25.9 , "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'}, ] ``` The messages are similar to those of Qwen-Agent, but there are some major differences: - Tools instead of functions - Parallel calls are by default - Multiple tool calls as a list in a single assistant message, instead of multiple messages. - The function arguments are parsed into a dict if it is a valid JSON-formatted string. #### Final Response Then it's time for the model to generate the actual response for us based on the tool results. Let's query the model again: ```python text = tokenizer. apply_chat_template (messages, tools=tools, add_generation_prompt=True, tokenize=False) inputs = tokenizer(text, return_tensors="pt").to(model. device ) outputs = model. generate (**inputs, max_new_tokens=512) output_text = tokenizer. batch_decode (outputs)[0][len(text):] ``` The output_text should be like ``` The current temperature in San Francisco is approximately 26.1 °C. Tomorrow, on October 1, 2024, the temperature is expected to be around 25.9 °C.<|im_end|> ``` Add the result text as an assistant message and the final messages should be ready for further interaction: ```python messages. append (try_parse_tool_calls(output_text)) ``` ### Ollama Ollama is a set of tools for serving LLMs locally. It also relies on its template implementation to support function calling. Different from transformers, which is written in Python and uses the Jinja template whose syntax is heavily inspired by Django and Python, Ollama, which is mostly written in Go, uses Go's [text/template](https://pkg. go. dev/text/template) packages. In addition, Ollama implements internally a helper function so that it can automatically parse the generated tool calls in texts to structured messages if the format supported. You could check the [Tool support](https://ollama. com /blog/tool-support) blog post first. Tool support has been available in Ollama since v0.3.0. You can run the following to check the Ollama version: ```bash ollama -v ``` If lower than expected, follow [the official instructions](https://ollama. com /download) to install the latest version. In this guide, we will aslo use [ollama-python](https://github. com /ollama/ollama-python), before starting, make sure it is available in your environment: ```bash pip install ollama ``` For this guide, the `ollama` binary is at v0.3.9 and the `ollama` Python library is at v0.3.2. #### Preparing The messages structure used in Ollama is the same with that in `transformers` and the template in [Qwen2.5 Ollama models](https://ollama. com /library/qwen2.5 ) has supported tool use. The inputs are the same with those in [the preparation code](#prepcode): ```python tools = TOOLS messages = MESSAGES[:] model_name = "qwen2.5 :7b" ``` Note that you cannot pass Python functions as tools directly and `tools` has to be a `dict`. #### Tool Calls and Tool Results We can use the `ollama. chat ` method to directly query the underlying API: ```python import ollama response = ollama. chat ( model=model_name, messages=messages, tools=tools, ) ``` The main fields in the response could be: ```python { 'model': 'qwen2.5 :7b', 'message': { 'role': 'assistant', 'content': '', 'tool_calls': [ {'function': {'name': 'get_current_temperature', 'arguments': {'location': 'San Francisco, CA, USA'}}}, {'function': {'name': 'get_temperature_date', 'arguments': {'date': '2024-10-01', 'location': 'San Francisco, CA, USA'}}}, ], }, } ``` Ollama's tool call parser has succeeded in parsing the tool results. If not, you may refine [the `try_parse_tool_calls` function above](#parse-function). Then, we can obtain the tool results and add them to the messages. The following is basically the same with `transformers`: ```python messages. append (response["message"]) if tool_calls := messages[-1].get("tool_calls", None): for tool_call in tool_calls: if fn_call := tool_call. get ("function"): fn_name: str = fn_call["name"] fn_args: dict = fn_call["arguments"] fn_res: str = json. dumps (get_function_by_name(fn_name)(**fn_args)) messages. append ({ "role": "tool", "name": fn_name, "content": fn_res, }) ``` The messages are now like ```python [ {'role': 'system', 'content': 'You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30'}, {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"}, {'role': 'assistant', 'content': '', 'tool_calls': [ {'function': {'name': 'get_current_temperature', 'arguments': {'location': 'San Francisco, CA, USA'}}}, {'function': {'name': 'get_temperature_date', 'arguments': {'date': '2024-10-01', 'location': 'San Francisco, CA, USA'}}}, ]}, {'role': 'tool', 'name': 'get_current_temperature', 'content': '{"temperature": 26.1 , "location": "San Francisco, CA, USA", "unit": "celsius"}'}, {'role': 'tool', 'name': 'get_temperature_date', 'content': '{"temperature": 25.9 , "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}'}, ] ``` #### Final Response The rest are easy: ```python response = ollama. chat ( model=model_name, messages=messages, tools=tools, ) messages. append (response["message"]) ``` The final message should be like the following: ```python {'role': 'assistant', 'content': 'The current temperature in San Francisco is approximately 26.1 °C. For tomorrow, October 1st, 2024, the forecasted temperature will be around 25.9 °C.'} ``` (heading-target)= ### vLLM vLLM is a fast and easy-to-use library for LLM inference and serving. It uses the tokenizer from `transformers` to format the input, so we should have no trouble preparing the input. In addition, vLLm also implements helper functions so that generated tool calls can be parsed automatically if the format is supported. Tool support has been available in `vllm` since v0.6.0. Be sure to install a version that supports tool use. For more information, check the [vLLM documentation](https://docs. vllm. ai/en/stable/serving/openai_compatible_server. html #tool-calling-in-the-chat-completion-api). For this guide, we are at version v0.6.1.post2. We will use the OpenAI-Compatible API by `vllm` with the API client from the `openai` Python library. #### Preparing For Qwen2.5 , the chat template in tokenizer_config. json has already included support for the Hermes-style tool use. We simply need to start a OpenAI-compatible API with vLLM: ```bash vllm serve Qwen/Qwen2.5 -7B-Instruct --enable-auto-tool-choice --tool-call-parser hermes ``` The inputs are the same with those in [the preparation code](#prepcode): ```python tools = TOOLS messages = MESSAGES[:] ``` Let's also initialize the client: ```python from openai import OpenAI openai_api_key = "EMPTY" openai_api_base = "http://localhost:8000/v1" client = OpenAI( api_key=openai_api_key, base_url=openai_api_base, ) model_name = "Qwen/Qwen2.5 -7B-Instruct" ``` #### Tool Calls and Tool Results We can use the create chat completions endpoint to query the model: ```python response = client. chat. completions. create ( model=model_name, messages=messages, tools=tools, temperature=0.7 , top_p=0.8 , max_tokens=512, extra_body={ "repetition_penalty": 1.05 , }, ) ``` vLLM should be able to parse the tool calls for us, and the main fields in the response (`response. choices [0]`) should be like ```python Choice( finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage( content=None, role='assistant', function_call=None, tool_calls=[ ChatCompletionMessageToolCall( id='chatcmpl-tool-924d705adb044ff88e0ef3afdd155f15', function=Function(arguments='{"location": "San Francisco, CA, USA"}', name='get_current_temperature'), type='function', ), ChatCompletionMessageToolCall( id='chatcmpl-tool-7e30313081944b11b6e5ebfd02e8e501', function=Function(arguments='{"location": "San Francisco, CA, USA", "date": "2024-10-01"}', name='get_temperature_date'), type='function', ), ], ), stop_reason=None, ) ``` Note that the function arguments are JSON-formatted strings, which Qwen-Agent follows but `transformers` and Ollama differs. As before, chances are that there are corner cases where tool calls are generated but they are malformed and cannot be parsed. For production code, we should try parsing by ourselves. Then, we can obtain the tool results and add them to the messages as shown below: ```python messages. append (response. choices [0].message. model_dump ()) if tool_calls := messages[-1].get("tool_calls", None): for tool_call in tool_calls: call_id: str = tool_call["id"] if fn_call := tool_call. get ("function"): fn_name: str = fn_call["name"] fn_args: dict = json. loads (fn_call["arguments"]) fn_res: str = json. dumps (get_function_by_name(fn_name)(**fn_args)) messages. append ({ "role": "tool", "content": fn_res, "tool_call_id": call_id, }) ``` It should be noted that the OpenAI API uses `tool_call_id` to identify the relation between tool results and tool calls. The messages are now like ```python [ {'role': 'system', 'content': 'You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30'}, {'role': 'user', 'content': "What's the temperature in San Francisco now? How about tomorrow?"}, {'content': None, 'role': 'assistant', 'function_call': None, 'tool_calls': [ {'id': 'chatcmpl-tool-924d705adb044ff88e0ef3afdd155f15', 'function': {'arguments': '{"location": "San Francisco, CA, USA"}', 'name': 'get_current_temperature'}, 'type': 'function'}, {'id': 'chatcmpl-tool-7e30313081944b11b6e5ebfd02e8e501', 'function': {'arguments': '{"location": "San Francisco, CA, USA", "date": "2024-10-01"}', 'name': 'get_temperature_date'}, 'type': 'function'}, ]}, {'role': 'tool', 'content': '{"temperature": 26.1 , "location": "San Francisco, CA, USA", "unit": "celsius"}', 'tool_call_id': 'chatcmpl-tool-924d705adb044ff88e0ef3afdd155f15'}, {'role': 'tool', 'content': '{"temperature": 25.9 , "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"}', 'tool_call_id': 'chatcmpl-tool-7e30313081944b11b6e5ebfd02e8e501'}, ] ``` #### Final Response Let's call the endpoint again to seed the tool results and get response: ```python response = client. chat. completions. create ( model=model_name, messages=messages, tools=tools, temperature=0.7 , top_p=0.8 , max_tokens=512, extra_body={ "repetition_penalty": 1.05 , }, ) messages. append (response. choices [0].message. model_dump ()) ``` The final response (`response. choices [0].message. content `) should be like ```text The current temperature in San Francisco is approximately 26.1 °C. For tomorrow, the forecasted temperature is around 25.9 °C. ``` ### Discussions Now, we have introduced how to conduct inference with function calling using Qwen2 in three different frameworks! Let's make a brief comparison. | Item | OpenAI API | Hugging Face transformers | Ollama | vLLM | Qwen-Agent | | :----- | :---: | :---: | :---: | :---: | :---: | | Type | HTTP API | Python Library | HTTP API | HTTP API | Python Library | | Inference Backend | - | PyTorch | llama. cpp | PyTorch | HTTP API | | Templating Backend | - | Jinja | Go `text/template` | Jinja | Python | | Tools/Functions | Tools | Tools | Tools | Tools | Functions | | Parallel Calls | Default Yes (Configurable) | Yes | Yes | Yes | Default No (Configurable) | | Call Format | Single assistant message with `tool_calls` | Single assistant message with `tool_calls` | Single assistant message with `tool_calls` | Single assistant message with `tool_calls` | Multiple assistant messages with `function_call` | | Call Argument Format | string | object | object | string | string | | Call Result Format | Multiple tool messages with `content` | Multiple tool messages with `content` | Multiple tool messages with `content` | Multiple tool messages with `content` | Multiple function messages with `content` | There are some details not shown in the above table: - OpenAI API comes with Python, Node. js , Go, and. NET SDKs. It also follows the OpenAPI standard. - Ollama comes with Python and Node. js SDKs. It has OpenAI-compatible API at a different base url that can be accessed using OpenAI API SDK. - Qwen-Agent as an application framework can call the tools automatically for you, which is introduced in [the Qwen-Agent guide](./qwen_agent). In addition, there are more on the model side of function calling, which means you may need to consider more things in production code: - **Accuracy of function calling**: When it comes to evaluate the accuracy of function calling, there are two aspects: (a) whether the correct functions (including no functions) are selected and (b) whether the correct function arguments are generated. It is not always the case that Qwen2.5 will be accurate. Function calling can involve knowledge that is deep and domain-specific. Sometimes, it doesn't fully understand the function and select the wrong one by mistake. Sometimes, it can fall into a loop and require calling the same function again and again. Sometimes, it will fabricate required function arguments instead of asking the user for input. To improve the function calling accuracy, it is advised to first try prompt engineering: does a more detailed function description help? can we provide instructions and examples to the model in the system message? If not, finetuning on your own data could also improve performance. - **Protocol consistency**: Even with the proper function calling template, the protocol may break. The model may generate extra texts to tool calls, e. g., explanations. The generated tool call may be invalid JSON-formatted string but a representation of a Python dict The generated tool call may be valid JSON but not conforms to the provided JSON Schema. For those kinds of issues, while some of them could be addressed with prompt engineering, some are caused by the nature of LLMs and can be hard to resolve in a general manner by LLMs themselves. While we strive to improve Qwen2.5 in this regard, edge cases are unlikely to be eliminated completely. ## Function Calling Templates The template design for function calling often includes the following aspects: - How to describe the functions to the model, so that the model understands what they are and how to use them. - How to prompt the model, so that it knows that functions can be used and in what format to generate the function calls. - How to tell a function call generation from others in generated text, so that we can extract the calls from the generated texts and actually make the calls. - How to incorporate the function results to the text, so that the model can tell them from its own generation and make connection among the calls and the results. For experienced prompt engineers, it should be possible to make any LLM support function calling, using in-context learning techniques and with representative examples, though with varied accuracy and stability depending on how "zero-shot" the task at hand is. ### Starting from ReAct Prompting For example, ReAct Prompting can be used to implement function calling with an extra element of planning: - **Thought**: the overt reasoning path, analyzing the functions and the user query and saying it out "loud" - **Action**: the function to use and the arguments with which the function should be called - **Observation**: the results of the function In fact, Qwen2 is verse in the following variant of ReAct Prompting (similar to LangChain ReAct) to make the intermediate texts more structured: ``` Answer the following questions as best you can. You have access to the following tools: {function_name}: Call this tool to interact with the {function_name_human_readable} API. What is the {function_name_human_readable} API useful for? {function_desciption} Parameters: {function_parameter_descriptions} {argument_formatting_instructions} {function_name}: Call this tool to interact with the {function_name_human_readable} API. What is the {function_name_human_readable} API useful for? {function_desciption} Parameters: {function_parameter_descriptions} {argument_formatting_instructions} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of [{function_name},{function_name}] Action Input: the input to the action Observation: the result of the action... (this Thought/Action/Action Input/Observation can be repeated zero or more times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Question: {query} Thought: {some_text} Action: {function_name} Action Input: {function_arguments} Observation: {function_results} Final Answer: {response} ``` As you can see, there is no apparent user/assistant conversation structure in the template. The model will simply continue the texts. One should write the code to actively detect which step the model is at and in particular to add the observations in the process, until the Final Answer is generated. However, as most programming interfaces accept the message structure, there should be some kind of adapter between the two. [The ReAct Chat Agent](https://github. com /QwenLM/Qwen-Agent/blob/v0.0.10/qwen_agent/agents/react_chat. py ) in Qwen-Agent facilitates this kind of conversion. ### Qwen2 Function Calling Template As a step forward, the official Qwen2 function calling template is in the vein of the ReAct Prompting format but focuses more on - differentiating the keywords like `Question`, `Thought`, `Action`, etc., from generation, - simplifying the process, - supporting better multi-turn conversation, and - adding controls for specialized usage. An equivalent example would be ``` <|im_start|>system {system message} ## Tools You have access to the following tools: ### {function_name_human_readable} {function_name}: {function_description} Parameters: {function_parameter_descriptions} {argument_formatting_instructions} ### {function_name_human_readable} {function_name}: {function_description} Parameters: {function_parameter_descriptions} {argument_formatting_instructions} ## When you need to call a tool, please insert the following command in your reply, which can be called zero or multiple times according to your needs: ✿FUNCTION✿: The tool to use, should be one of [{function_name},{function_name}] ✿ARGS✿: The input of the tool ✿RESULT✿: Tool results ✿RETURN✿: Reply based on tool results. Images need to be rendered as <|im_end|> <|im_start|>user {query}<|im_end|> <|im_start|>assistant ✿FUNCTION✿: {function_name} ✿ARGS✿: {function_arguments} ✿RESULT✿: {function_result} ✿RETURN✿:{response}<|im_end|> ``` Let's first list the obvious differences: - Keywords (`✿FUNCTION✿`, `✿ARGS✿`, etc.) seem rare in ordinary text and more semantically related to function calling, but not special tokens yet. - Thought is omitted. This could affect accuracy for some use cases. - Use the system-user-assistant format for multi-turn conversations. Function calling prompting is moved to the system message. How about adding controls for specialized usage? The template actually has the following variants: - Language: the above is for non-Chinese language; there is another template in Chinese. - Parallel Calls: the above is for non-parallel calls; there is another template for parallel calls. In the canonical implementation in Qwen-Agent, those switches are implemented in Python, according to the configuration and current input. The actual text with _parallel calls_ should be like the following: ```text <|im_start|>system You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30 ## Tools You have access to the following tools: ### get_current_temperature get_current_temperature: Get current temperature at a location. Parameters: {"type": "object", "properties": {"location": {"type": "string", "description": "The location to get the temperature for, in the format \"City, State, Country\"."}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit to return the temperature in. Defaults to \"celsius\"."}}, "required": ["location"]} Format the arguments as a JSON object. ### get_temperature_date get_temperature_date: Get temperature at a location and date. Parameters: {"type": "object", "properties": {"location": {"type": "string", "description": "The location to get the temperature for, in the format \"City, State, Country\"."}, "date": {"type": "string", "description": "The date to get the temperature for, in the format \"Year-Month-Day\"."}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit to return the temperature in. Defaults to \"celsius\"."}}, "required": ["location", "date"]} Format the arguments as a JSON object. ## Insert the following command in your reply when you need to call N tools in parallel: ✿FUNCTION✿: The name of tool 1, should be one of [get_current_temperature,get_temperature_date] ✿ARGS✿: The input of tool 1 ✿FUNCTION✿: The name of tool 2 ✿ARGS✿: The input of tool 2... ✿FUNCTION✿: The name of tool N ✿ARGS✿: The input of tool N ✿RESULT✿: The result of tool 1 ✿RESULT✿: The result of tool 2... ✿RESULT✿: The result of tool N ✿RETURN✿: Reply based on tool results. Images need to be rendered as <|im_end|> <|im_start|>user What's the temperature in San Francisco now? How about tomorrow?<|im_end|> <|im_start|>assistant ✿FUNCTION✿: get_current_temperature ✿ARGS✿: {"location": "San Francisco, CA, USA"} ✿FUNCTION✿: get_temperature_date ✿ARGS✿: {"location": "San Francisco, CA, USA", "date": "2024-10-01"} ✿RESULT✿: {"temperature": 26.1 , "location": "San Francisco, CA, USA", "unit": "celsius"} ✿RESULT✿: {"temperature": 25.9 , "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"} ✿RETURN✿: The current temperature in San Francisco is approximately 26.1 °C. For tomorrow, October 1st, 2024, the forecasted temperature will be around 25.9 °C.<|im_end|> ``` This template is hard to adapt it for other frameworks that use less capable templating engines. But it is doable at least partially for Jinja, which is Python-oriented after all. We didn't use it because using the template in `transformers` leads to more changes to the inference usage, which are not very common for beginners. For the interested, you can find the Jinja template and key points on usage below: :::{dropdown} Qwen2 Function Calling Jinja Template ```jinja {%- if messages[0]["role"] == "system" %} {%- set system_message = messages[0]["content"] %} {%- set loop_messages = messages[1:] %} {%- else %} {%- set system_message = "You are a helpful assistant." %} {%- set loop_messages = messages %} {%- endif %} {%- if parallel_tool_calls is undefined %} {%- set parallel_tool_calls = false %} {%- endif %} {%- if language is undefined or language != "zh" %} {%- set language = "en" %} {%- endif %} {{- "<|im_start|>system\n" + system_message|trim }} {%- if tools is defined %} {{- " # 工具 ## 你拥有如下工具: " if language == "zh" else " ## Tools You have access to the following tools: " }} {%- set functions = tools|map(attribute="function")|list %} {%- set function_names = functions|map(attribute="name")|join(",") %} {%- for function in functions %} {{- "### " + function. name + " " + function. name + ": " + function. description + (" 输入参数:" if language == "zh" else " Parameters: ") + function. parameters |tojson + (" 此工具的输入应为JSON对象。 " if language == "zh" else " Format the arguments as a JSON object. ") }} {%- endfor %} {%- if parallel_tool_calls and language == "zh" %} {{- "## 你可以在回复中插入以下命令以并行调用N个工具: ✿FUNCTION✿: 工具1的名称,必须是[" + function_names + "]之一\n✿ARGS✿: 工具1的输入\n✿FUNCTION✿: 工具2的名称\n✿ARGS✿: 工具2的输入\n...\n✿FUNCTION✿: 工具N的名称\n✿ARGS✿: 工具N的输入\n✿RESULT✿: 工具1的结果\n✿RESULT✿: 工具2的结果\n...\n✿RESULT✿: 工具N的结果\n✿RETURN✿: 根据工具结 果进行回复,需将图片用渲染出来" }} {%- elif parallel_tool_calls %} {{- "## Insert the following command in your reply when you need to call N tools in parallel: ✿FUNCTION✿: The name of tool 1, should be one of [" + function_names + "]\n✿ARGS✿: The input of tool 1\n✿FUNCTION✿: The name of tool 2\n✿ARGS✿: The input of tool 2\n...\n✿FUNCTION✿: The name of tool N\n✿ARGS✿: The input of tool N\n✿RESULT✿: The result of tool 1\n✿RESULT✿: The result of tool 2\n...\n✿RESULT✿: The result of tool N\n✿RETURN✿: Reply based on tool results. Images need to be rendered as " }} {%- elif language == "zh" %} {{- "## 你可以在回复中插入零次、一次或多次以下命令以调用工具: ✿FUNCTION✿: 工具名称,必须是[" + function_names + "]之一。\n✿ARGS✿: 工具输入\n✿RESULT✿: 工具结果\n✿RETURN✿: 根据工具结果进行回复,需将图片用渲染出来" }} {%- else %} {{- "## When you need to call a tool, please insert the following command in your reply, which can be called zero or multiple times according to your needs: ✿FUNCTION✿: The tool to use, should be one of [" + function_names + "]\n✿ARGS✿: The input of the tool\n✿RESULT✿: Tool results\n✿RETURN✿: Reply based on tool results. Images need to be rendered as " }} {%- endif %} {%- endif %} {{- "<|im_end|>" }} {%- for message in loop_messages %} {%- if message. role == "user" %} {{- "\n<|im_start|>" + message. role + "\n" + message. content + "<|im_end|>" }} {%- if loop. last and add_generation_prompt %} {{- "\n<|im_start|>assistant\n" }} {%- endif %} {%- elif message. role == "tool" %} {{- "✿RESULT✿: " + message. content + "\n" }} {%- if loop. last and add_generation_prompt %} {{- "✿RETURN✿:" }} {%- endif %} {%- elif message. role == "assistant" and message. tool_calls is defined %} {%- if loop. previtem. role == "user" %} {{- "\n<|im_start|>assistant\n" }} {%- endif %} {%- for function in message. tool_calls |map(attribute="function") %} {{- "✿FUNCTION✿: " + function. name + "\n✿ARGS✿: " + function. arguments |tojson + "\n" }} {%- endfor %} {%- elif message. role == "assistant" %} {%- if loop. previtem. role == "user" %} {{- "\n<|im_start|>assistant\n" }} {%- elif loop. previtem. role == "tool" %} {{- "✿RETURN✿:" }} {%- endif %} {{- message. content }} {%- if loop. nextitem is undefined or loop. nextitem. role == "user" %} {{- "<|im_end|>" }} {%- endif %} {%- else %} {{- "\n<|im_start|>" + message. role + "\n" + message. content + "<|im_end|>" }} {%- endif %} {%- endfor %} ``` To use this template in `transformers`: - Switches can be enabled by passing them to the `apply_chat_template` method, e. g., `tokenizer. apply_chat_template (messages, tools=tools, add_generation_prompt=True, parallel_tool_call=True, language="zh", tokenize=False)`. By default, it is for English non-parallel function calling. - The tool arguments should be a Python `dict` instead of a JSON-formatted object `str`. - Since the generation needs to be stopped at `✿RESULT✿` or else the model will generate fabricated tool results, we should add it to `stop_strings` in `generation_config`: ```python model. generation_config. stop_strings = ["✿RESULT✿:", "✿RETURN✿:"] ``` - As a result of using `stop_strings`, you need to pass the tokenizer to `model. generate ` as `model. generate (**inputs, tokenizer=tokenizer, max_new_tokens=512)`. - `response`, i. e., the model generation based on the tool calls and tool results, may contain a leading space. You should not strip it for the model. It is resulted from the tokenization and the template design. - The `try_parse_tool_calls` function should also be modified accordingly. ::: ### Qwen2.5 Function Calling Templates For `transformers` and Ollama, we have also used templates that are easier to implement with Jinja or Go. They are variants of [the Nous Research's Hermes function calling template](https://github. com /NousResearch/Hermes-Function-Calling#prompt-format-for-function-calling). The Jinja template and the Go template should produce basically the same results. They final text should look like the following: ```text <|im_start|>system You are Qwen, created by Alibaba Cloud. You are a helpful assistant. Current Date: 2024-09-30 # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within <tools></tools> XML tags: <tools> {"type": "function", "function": {"name": "get_current_temperature", "description": "Get current temperature at a location.", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "The location to get the temperature for, in the format \"City, State, Country\"."}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit to return the temperature in. Defaults to \"celsius\"."}}, "required": ["location"]}}} {"type": "function", "function": {"name": "get_temperature_date", "description": "Get temperature at a location and date.", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "The location to get the temperature for, in the format \"City, State, Country\"."}, "date": {"type": "string", "description": "The date to get the temperature for, in the format \"Year-Month-Day\"."}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit to return the temperature in. Defaults to \"celsius\"."}}, "required": ["location", "date"]}}} </tools> For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags: <tool_call> {"name": <function-name>, "arguments": <args-json-object>} </tool_call><|im_end|> <|im_start|>user What's the temperature in San Francisco now? How about tomorrow?<|im_end|> <|im_start|>assistant <tool_call> {"name": "get_current_temperature", "arguments": {"location": "San Francisco, CA, USA"}} </tool_call> <tool_call> {"name": "get_temperature_date", "arguments": {"location": "San Francisco, CA, USA", "date": "2024-10-01"}} </tool_call><|im_end|> <|im_start|>user <tool_response> {"temperature": 26.1 , "location": "San Francisco, CA, USA", "unit": "celsius"} </tool_response> <tool_response> {"temperature": 25.9 , "location": "San Francisco, CA, USA", "date": "2024-10-01", "unit": "celsius"} </tool_response><|im_end|> <|im_start|>assistant The current temperature in San Francisco is approximately 26.1 °C. Tomorrow, on October 1, 2024, the temperature is expected to be around 25.9 °C.<|im_end|> ``` While the text may seem different from the previous one, the basic prompting structure is still the same. There are just more structural tags and more JSON-formatted strings. --- There is one thing we haven't talked about: how should functions be described to the LLMs. In short, you could describe them as you would normally describe them in an API documentation, as long as you can effectively parse, validate, and execute the tool calls generated by the models. The format with JSON Schema appears a valid and common choice. ## Finally In whichever way you choose to use function calling with Qwen2.5 , keep in mind that the limitation and the perks of prompt engineering applies: - It is not guaranteed that the model generation will always follow the protocol even with proper prompting or templates. Especially, for the templates that are more complex and relies more on the model itself to think and stay on track than the ones that are simpler and relies on the template and the use of control or special tokens. The latter one, of course, requires some kind of training. In production code, be prepared that if it breaks, countermeasures or rectifications are in place. - If in certain scenarios, the generation is not up to expectation, you can refine the template to add more instructions or constraints. While the templates mentioned here are general enough, they may not be the best or the most specific or the most concise for your use cases. The ultimate solution is fine-tuning using your own data. Have fun prompting! <article> #qwen_agent. md Qwen-Agent ========== `Qwen-Agent <https://github. com /QwenLM/Qwen-Agent>`__ is a framework for developing LLM applications based on the instruction following, tool usage, planning, and memory capabilities of Qwen. It also comes with example applications such as Browser Assistant, Code Interpreter, and Custom Assistant. Installation ------------.. code:: bash git clone https://github. com /QwenLM/Qwen-Agent. git cd Qwen-Agent pip install -e./ Developing Your Own Agent ------------------------- Qwen-Agent provides atomic components such as LLMs and prompts, as well as high-level components such as Agents. The example below uses the Assistant component as an illustration, demonstrating how to add custom tools and quickly develop an agent that uses tools... code:: py import json import os import json5 import urllib. parse from qwen_agent. agents import Assistant from qwen_agent. tools. base import BaseTool, register_tool llm_cfg = { # Use the model service provided by DashScope: 'model': 'qwen-max', 'model_server': 'dashscope', # 'api_key': 'YOUR_DASHSCOPE_API_KEY', # It will use the `DASHSCOPE_API_KEY' environment variable if 'api_key' is not set here. # Use your own model service compatible with OpenAI API: # 'model': 'Qwen/Qwen2.5 -7B-Instruct', # 'model_server': 'http://localhost:8000/v1', # api_base # 'api_key': 'EMPTY', # (Optional) LLM hyperparameters for generation: 'generate_cfg': { 'top_p': 0.8 } } system = 'According to the user\'s request, you first draw a picture and then automatically run code to download the picture ' + \ 'and select an image operation from the given document to process the image' # Add a custom tool named my_image_gen: @register_tool('my_image_gen') class MyImageGen(BaseTool): description = 'AI painting (image generation) service, input text description, and return the image URL drawn based on text information.' parameters = [{ 'name': 'prompt', 'type': 'string', 'description': 'Detailed description of the desired image content, in English', 'required': True }] def call(self, params: str, **kwargs) -> str: prompt = json5.loads (params)['prompt'] prompt = urllib. parse. quote(prompt) return json. dumps ( {'image_url': f'https://image. pollinations. ai/prompt/{prompt}'}, ensure_ascii=False) tools = ['my_image_gen', 'code_interpreter'] # code_interpreter is a built-in tool in Qwen-Agent bot = Assistant(llm=llm_cfg, system_message=system, function_list=tools, files=[os. path. abspath('doc. pdf ')]) messages = [] while True: query = input('user question: ') messages. append ({'role': 'user', 'content': query}) response = [] for response in bot. run (messages=messages): print('bot response:', response) messages. extend (response) The framework also provides more atomic components for developers to combine. For additional showcases, please refer to `examples <https://github. com /QwenLM/Qwen-Agent/tree/main/examples>`__. <article> #vllm. md # vLLM We recommend you trying [vLLM](https://github. com /vllm-project/vllm) for your deployment of Qwen. It is simple to use, and it is fast with state-of-the-art serving throughput, efficient management of attention key value memory with PagedAttention, continuous batching of input requests, optimized CUDA kernels, etc. To learn more about vLLM, please refer to the [paper](https://arxiv. org /abs/2309.06180 ) and [documentation](https://vllm. readthedocs. io/). ## Installation By default, you can install `vllm` by pip in a clean environment: ```bash pip install vllm ``` Please note that the prebuilt `vllm` has strict dependencies on `torch` and its CUDA versions. Check the note in the official document for installation ([link](https://docs. vllm. ai/en/latest/getting_started/installation. html )) for some help. We also advise you to install ray by `pip install ray` for distributed serving. ## Offline Batched Inference Models supported by Qwen2.5 codes are supported by vLLM. The simplest usage of vLLM is offline batched inference as demonstrated below. ```python from transformers import AutoTokenizer from vllm import LLM, SamplingParams # Initialize the tokenizer tokenizer = AutoTokenizer. from_pretrained ("Qwen/Qwen2.5 -7B-Instruct") # Pass the default decoding hyperparameters of Qwen2.5 -7B-Instruct # max_tokens is for the maximum length for generation. sampling_params = SamplingParams(temperature=0.7 , top_p=0.8 , repetition_penalty=1.05 , max_tokens=512) # Input the model name or path. Can be GPTQ or AWQ models. llm = LLM(model="Qwen/Qwen2.5 -7B-Instruct") # Prepare your prompts prompt = "Tell me something about large language models." messages = [ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, {"role": "user", "content": prompt} ] text = tokenizer. apply_chat_template ( messages, tokenize=False, add_generation_prompt=True ) # generate outputs outputs = llm. generate ([text], sampling_params) # Print the outputs. for output in outputs: prompt = output. prompt generated_text = output. outputs [0].text print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}") ``` ## OpenAI-Compatible API Service It is easy to build an OpenAI-compatible API service with vLLM, which can be deployed as a server that implements OpenAI API protocol. By default, it starts the server at `http://localhost:8000`. You can specify the address with `--host` and `--port` arguments. Run the command as shown below: ```bash vllm serve Qwen/Qwen2.5 -7B-Instruct ``` You don't need to worry about chat template as it by default uses the chat template provided by the tokenizer. Then, you can use the [create chat interface](https://platform. openai. com/docs/api-reference/chat/completions/create) to communicate with Qwen: ::::{tab-set} :::{tab-item} curl ```bash curl http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{ "model": "Qwen/Qwen2.5 -7B-Instruct", "messages": [ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, {"role": "user", "content": "Tell me something about large language models."} ], "temperature": 0.7 , "top_p": 0.8 , "repetition_penalty": 1.05 , "max_tokens": 512 }' ``` ::: :::{tab-item} Python You can use the API client with the `openai` Python package as shown below: ```python from openai import OpenAI # Set OpenAI's API key and API base to use vLLM's API server. openai_api_key = "EMPTY" openai_api_base = "http://localhost:8000/v1" client = OpenAI( api_key=openai_api_key, base_url=openai_api_base, ) chat_response = client. chat. completions. create ( model="Qwen/Qwen2.5 -7B-Instruct", messages=[ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, {"role": "user", "content": "Tell me something about large language models."}, ], temperature=0.7 , top_p=0.8 , max_tokens=512, extra_body={ "repetition_penalty": 1.05 , }, ) print("Chat response:", chat_response) ``` :::: :::{tip} The OpenAI-compatible server in `vllm` comes with [a default set of sampling parameters](https://github. com /vllm-project/vllm/blob/v0.5.2/vllm/entrypoints/openai/protocol. py #L130), which are not suitable for Qwen2.5 models and prone to repetition. We advise you to always pass sampling parameters to the API. ::: ### Tool Use The OpenAI-compatible API could be configured to support tool call of Qwen2.5. For information, please refer to [our guide on Function Calling](../framework/function_call. md #vllm). ### Structured/JSON Output Qwen 2.5 , when used with vLLM, supports structured/JSON output. Please refer to [vllm's documentation](https://docs. vllm. ai/en/stable/serving/openai_compatible_server. html #extra-parameters-for-chat-api) for the `guided_json` parameters. Besides, it is also recommended to instruct the model to generate the specific format in the system message or in your prompt. ## Multi-GPU Distributed Serving To scale up your serving throughput, distributed serving helps you by leveraging more GPU devices. Besides, for large models like `Qwen2.5 -72B-Instruct`, it is impossible to serve it on a single GPU. Here, we demonstrate how to run `Qwen2.5 -72B-Instruct` with tensor parallelism just by passing in the argument `tensor_parallel_size`: ::::{tab-set} :::{tab-item} Offline ```python from vllm import LLM, SamplingParams llm = LLM(model="Qwen/Qwen2.5 -72B-Instruct", tensor_parallel_size=4) ``` ::: :::{tab-item} API ```bash vllm serve Qwen/Qwen2.5 -72B-Instruct --tensor-parallel-size 4 ``` ::: :::: ## Extended Context Support By default, the context length for Qwen2.5 models are set to 32,768 tokens. To handle extensive inputs exceeding 32,768 tokens, we utilize [YaRN](https://arxiv. org /abs/2309.00071 ), a technique for enhancing model length extrapolation, ensuring optimal performance on lengthy texts. vLLM supports YARN and it can be enabled by add a `rope_scaling` field to the `config. json ` file of the model. For example, ```json { ..., "rope_scaling": { "factor": 4.0 , "original_max_position_embeddings": 32768, "type": "yarn" } } ``` However, vLLM only supports _static_ YARN at present, which means the scaling factor remains constant regardless of input length, potentially impacting performance on shorter texts. We advise adding the `rope_scaling` configuration only when processing long contexts is required. ## Serving Quantized Models vLLM supports different types of quantized models, including AWQ, GPTQ, SqueezeLLM, etc. Here we show how to deploy AWQ and GPTQ models. The usage is almost the same as above except for an additional argument for quantization. For example, to run an AWQ model. e.g., `Qwen2.5 -7B-Instruct-AWQ`: ::::{tab-set} :::{tab-item} Offline ```python from vllm import LLM, SamplingParams llm = LLM(model="Qwen/Qwen2.5 -7B-Instruct-AWQ", quantization="awq") ``` ::: :::{tab-item} API ```bash vllm serve Qwen/Qwen2.5 -7B-Instruct-AWQ --quantization awq ``` ::: :::: or GPTQ models like `Qwen2.5 -7B-Instruct-GPTQ-Int4`: ::::{tab-set} :::{tab-item} Offline ```python from vllm import LLM, SamplingParams llm = LLM(model="Qwen/Qwen2.5 -7B-Instruct-GPTQ-Int4", quantization="gptq") ``` ::: :::{tab-item} API ```bash vllm serve Qwen/Qwen2.5 -7B-Instruct-GPTQ-Int4 --quantization gptq ``` ::: :::: Additionally, vLLM supports the combination of AWQ or GPTQ models with KV cache quantization, namely FP8 E5M2 KV Cache. For example, ::::{tab-set} :::{tab-item} Offline ```python from vllm import LLM, SamplingParams llm = LLM(model="Qwen/Qwen2.5 -7B-Instruct-GPTQ-Int4", quantization="gptq", kv_cache_dtype="fp8_e5m2") ``` ::: :::{tab-item} API ```bash vllm serve Qwen/Qwen2.5 -7B-Instruct-GPTQ-Int4 --quantization gptq --kv-cache-dtype fp8_e5m2 ``` ::: :::: ## Troubleshooting You may encounter OOM issues that are pretty annoying. We recommend two arguments for you to make some fix. - The first one is `--max-model-len`. Our provided default `max_position_embedding` is `32768` and thus the maximum length for the serving is also this value, leading to higher requirements of memory. Reducing it to a proper length for yourself often helps with the OOM issue. - Another argument you can pay attention to is `--gpu-memory-utilization`. vLLM will pre-allocate this much GPU memory. By default, it is `0.9 `. This is also why you find a vLLM service always takes so much memory. If you are in eager mode (by default it is not), you can level it up to tackle the OOM problem. Otherwise, CUDA Graphs are used, which will use GPU memory not controlled by vLLM, and you should try lowering it. If it doesn't work, you should try `--enforce-eager`, which may slow down infernece, or reduce the `--max-model-len`. <article> #langchain. rst Langchain ========================== This guide helps you build a question-answering application based on a local knowledge base using ``Qwen2.5 -7B-Instruct`` with ``langchain``. The goal is to establish a knowledge base Q&A solution. Basic Usage ----------- The implementation process of this project includes loading files -> reading text -> segmenting text -> vectorizing text -> vectorizing questions -> matching the top k most similar text vectors with the question vectors -> incorporating the matched text as context along with the question into the prompt -> submitting to the Qwen2.5 -7B-Instruct to generate an answer. Below is an example:.. code:: bash pip install langchain==0.0.174 pip install faiss-gpu.. code:: python from transformers import AutoModelForCausalLM, AutoTokenizer from abc import ABC from langchain. llms. base import LLM from typing import Any, List, Mapping, Optional from langchain. callbacks. manager import CallbackManagerForLLMRun model_name = "Qwen/Qwen2.5 -7B-Instruct" model = AutoModelForCausalLM. from_pretrained ( model_name, torch_dtype="auto", device_map="auto" ) tokenizer = AutoTokenizer. from_pretrained (model_name) class Qwen(LLM, ABC): max_token: int = 10000 temperature: float = 0.01 top_p = 0.9 history_len: int = 3 def __init__(self): super().__init__() @property def _llm_type(self) -> str: return "Qwen" @property def _history_len(self) -> int: return self. history_len def set_history_len(self, history_len: int = 10) -> None: self. history_len = history_len def _call( self, prompt: str, stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, ) -> str: messages = [ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."}, {"role": "user", "content": prompt} ] text = tokenizer. apply_chat_template ( messages, tokenize=False, add_generation_prompt=True ) model_inputs = tokenizer([text], return_tensors="pt").to(model. device ) generated_ids = model. generate ( **model_inputs, max_new_tokens=512 ) generated_ids = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs. input_ids , generated_ids) ] response = tokenizer. batch_decode (generated_ids, skip_special_tokens=True)[0] return response @property def _identifying_params(self) -> Mapping[str, Any]: """Get the identifying parameters.""" return {"max_token": self. max_token , "temperature": self. temperature , "top_p": self. top_p , "history_len": self. history_len } After loading the Qwen2.5 -7B-Instruct model, you should specify the txt file for retrieval... code:: python import os import re import torch import argparse from langchain. vectorstores import FAISS from langchain. embeddings. huggingface import HuggingFaceEmbeddings from typing import List, Tuple import numpy as np from langchain. document_loaders import TextLoader from langchain. text_splitter import CharacterTextSplitter from langchain. docstore. document import Document from langchain. prompts. prompt import PromptTemplate from langchain. chains import RetrievalQA class ChineseTextSplitter(CharacterTextSplitter): def __init__(self, pdf: bool = False, **kwargs): super().__init__(**kwargs) self. pdf = pdf def split_text(self, text: str) -> List[str]: if self. pdf : text = re. sub (r"\n{3,}", "\n", text) text = re. sub ('\s', ' ', text) text = text. replace (" ", "") sent_sep_pattern = re. compile ( '([﹒﹔﹖﹗.。!?]["’”」』]{0,2}|(?=["‘“「『]{1,2}|$))') sent_list = [] for ele in sent_sep_pattern. split (text): if sent_sep_pattern. match (ele) and sent_list: sent_list[-1] += ele elif ele: sent_list. append (ele) return sent_list def load_file(filepath): loader = TextLoader(filepath, autodetect_encoding=True) textsplitter = ChineseTextSplitter(pdf=False) docs = loader. load_and_split (textsplitter) write_check_file(filepath, docs) return docs def write_check_file(filepath, docs): folder_path = os. path. join(os. path. dirname(filepath), "tmp_files") if not os. path. exists(folder_path): os. makedirs (folder_path) fp = os. path. join(folder_path, 'load_file. txt ') with open(fp, 'a+', encoding='utf-8') as fout: fout. write ("filepath=%s,len=%s" % (filepath, len(docs))) fout. write ('\n') for i in docs: fout. write (str(i)) fout. write ('\n') fout. close () def separate_list(ls: List[int]) -> List[List[int]]: lists = [] ls1 = [ls[0]] for i in range(1, len(ls)): if ls[i - 1] + 1 == ls[i]: ls1.append (ls[i]) else: lists. append (ls1) ls1 = [ls[i]] lists. append (ls1) return lists class FAISSWrapper(FAISS): chunk_size = 250 chunk_conent = True score_threshold = 0 def similarity_search_with_score_by_vector( self, embedding: List[float], k: int = 4 ) -> List[Tuple[Document, float]]: scores, indices = self. index. search(np. array ([embedding], dtype=np. float32 ), k) docs = [] id_set = set() store_len = len(self. index_to_docstore_id ) for j, i in enumerate(indices[0]): if i == -1 or 0 < self. score_threshold < scores[0][j]: # This happens when not enough docs are returned. continue _id = self. index_to_docstore_id [i] doc = self. docstore. search(_id) if not self. chunk_conent : if not isinstance(doc, Document): raise ValueError(f"Could not find document for id {_id}, got {doc}") doc. metadata ["score"] = int(scores[0][j]) docs. append (doc) continue id_set. add (i) docs_len = len(doc. page_content ) for k in range(1, max(i, store_len - i)): break_flag = False for l in [i + k, i - k]: if 0 <= l < len(self. index_to_docstore_id ): _id0 = self. index_to_docstore_id [l] doc0 = self. docstore. search(_id0) if docs_len + len(doc0.page_content ) > self. chunk_size : break_flag = True break elif doc0.metadata ["source"] == doc. metadata ["source"]: docs_len += len(doc0.page_content ) id_set. add (l) if break_flag: break if not self. chunk_conent : return docs if len(id_set) == 0 and self. score_threshold > 0: return [] id_list = sorted(list(id_set)) id_lists = separate_list(id_list) for id_seq in id_lists: for id in id_seq: if id == id_seq[0]: _id = self. index_to_docstore_id [id] doc = self. docstore. search(_id) else: _id0 = self. index_to_docstore_id [id] doc0 = self. docstore. search(_id0) doc. page_content += " " + doc0.page_content if not isinstance(doc, Document): raise ValueError(f"Could not find document for id {_id}, got {doc}") doc_score = min([scores[0][id] for id in [indices[0].tolist().index(i) for i in id_seq if i in indices[0]]]) doc. metadata ["score"] = int(doc_score) docs. append ((doc, doc_score)) return docs if __name__ == '__main__': # load docs (pdf file or txt file) filepath = 'your file path' # Embedding model name EMBEDDING_MODEL = 'text2vec' PROMPT_TEMPLATE = """Known information: {context_str} Based on the above known information, respond to the user's question concisely and professionally. If an answer cannot be derived from it, say 'The question cannot be answered with the given information' or 'Not enough relevant information has been provided,' and do not include fabricated details in the answer. Please respond in English. The question is {question}""" # Embedding running device EMBEDDING_DEVICE = "cuda" # return top-k text chunk from vector store VECTOR_SEARCH_TOP_K = 3 CHAIN_TYPE = 'stuff' embedding_model_dict = { "text2vec": "your text2vec model path", } llm = Qwen() embeddings = HuggingFaceEmbeddings(model_name=embedding_model_dict[EMBEDDING_MODEL],model_kwargs={'device': EMBEDDING_DEVICE}) docs = load_file(filepath) docsearch = FAISSWrapper. from_documents (docs, embeddings) prompt = PromptTemplate( template=PROMPT_TEMPLATE, input_variables=["context_str", "question"] ) chain_type_kwargs = {"prompt": prompt, "document_variable_name": "context_str"} qa = RetrievalQA. from_chain_type ( llm=llm, chain_type=CHAIN_TYPE, retriever=docsearch. as_retriever (search_kwargs={"k": VECTOR_SEARCH_TOP_K}), chain_type_kwargs=chain_type_kwargs) query = "Give me a short introduction to large language model." print(qa. run (query)) Next Step --------- Now you can chat with Qwen2.5 use your own document. Continue to read the documentation and try to figure out more advanced usages of model retrieval! <article> #llamaindex. rst LlamaIndex ========== To connect Qwen2.5 with external data, such as documents, web pages, etc., we offer a tutorial on `LlamaIndex <https://www. llamaindex. ai/>`__. This guide helps you quickly implement retrieval-augmented generation (RAG) using LlamaIndex with Qwen2.5. Preparation -------------------------------------- To implement RAG, we advise you to install the LlamaIndex-related packages first. The following is a simple code snippet showing how to do this:.. code:: bash pip install llama-index pip install llama-index-llms-huggingface pip install llama-index-readers-web Set Parameters -------------------------------------- Now we can set up LLM, embedding model, and the related configurations. Qwen2.5 -Instruct supports conversations in multiple languages, including English and Chinese. You can use the ``bge-base-en-v1.5 `` model to retrieve from English documents, and you can download the ``bge-base-zh-v1.5 `` model to retrieve from Chinese documents. You can also choose ``bge-large`` or ``bge-small`` as the embedding model or modify the context window size or text chunk size depending on your computing resources. Qwen2.5 model families support a maximum of 32K context window size (up to 128K for 7B, 14B, 32B, and 72B, requiring extra configuration).. code:: python import torch from llama_index. core import Settings from llama_index. core. node_parser import SentenceSplitter from llama_index. llms. huggingface import HuggingFaceLLM from llama_index. embeddings. huggingface import HuggingFaceEmbedding # Set prompt template for generation (optional) from llama_index. core import PromptTemplate def completion_to_prompt(completion): return f"<|im_start|>system\n<|im_end|>\n<|im_start|>user\n{completion}<|im_end|>\n<|im_start|>assistant\n" def messages_to_prompt(messages): prompt = "" for message in messages: if message. role == "system": prompt += f"<|im_start|>system\n{message. content }<|im_end|>\n" elif message. role == "user": prompt += f"<|im_start|>user\n{message. content }<|im_end|>\n" elif message. role == "assistant": prompt += f"<|im_start|>assistant\n{message. content }<|im_end|>\n" if not prompt. startswith ("<|im_start|>system"): prompt = "<|im_start|>system\n" + prompt prompt = prompt + "<|im_start|>assistant\n" return prompt # Set Qwen2.5 as the language model and set generation config Settings. llm = HuggingFaceLLM( model_name="Qwen/Qwen2.5 -7B-Instruct", tokenizer_name="Qwen/Qwen2.5 -7B-Instruct", context_window=30000, max_new_tokens=2000, generate_kwargs={"temperature": 0.7 , "top_k": 50, "top_p": 0.95 }, messages_to_prompt=messages_to_prompt, completion_to_prompt=completion_to_prompt, device_map="auto", ) # Set embedding model Settings. embed_model = HuggingFaceEmbedding( model_name = "BAAI/bge-base-en-v1.5 " ) # Set the size of the text chunk for retrieval Settings. transformations = [SentenceSplitter(chunk_size=1024)] Build Index -------------------------------------- Now we can build index from documents or websites. The following code snippet demonstrates how to build an index for files (regardless of whether they are in PDF or TXT format) in a local folder named 'document'... code:: python from llama_index. core import VectorStoreIndex, SimpleDirectoryReader documents = SimpleDirectoryReader("./document").load_data() index = VectorStoreIndex. from_documents ( documents, embed_model=Settings. embed_model , transformations=Settings. transformations ) The following code snippet demonstrates how to build an index for the content in a list of websites... code:: python from llama_index. readers. web import SimpleWebPageReader from llama_index. core import VectorStoreIndex, SimpleDirectoryReader documents = SimpleWebPageReader(html_to_text=True).load_data( ["web_address_1","web_address_2",...] ) index = VectorStoreIndex. from_documents ( documents, embed_model=Settings. embed_model , transformations=Settings. transformations ) To save and load the index, you can use the following code snippet... code:: python from llama_index. core import StorageContext, load_index_from_storage # save index storage_context = StorageContext. from_defaults (persist_dir="save") # load index index = load_index_from_storage(storage_context) RAG ------------------- Now you can perform queries, and Qwen2.5 will answer based on the content of the indexed documents... code:: python query_engine = index. as_query_engine () your_query = "<your query here>" print(query_engine. query (your_query).response) <article> #qwen2.py # coding=utf-8 # Copyright 2024 The Qwen team, Alibaba Group and the HuggingFace Inc. team. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www. apache. org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Qwen2 model configuration""" from...configuration_utils import PretrainedConfig from...modeling_rope_utils import rope_config_validation from...utils import logging logger = logging. get_logger (__name__) class Qwen2Config(PretrainedConfig): r""" This is the configuration class to store the configuration of a [`Qwen2Model`]. It is used to instantiate a Qwen2 model according to the specified arguments, defining the model architecture. Instantiating a configuration with the defaults will yield a similar configuration to that of Qwen2-7B-beta [Qwen/Qwen2-7B-beta](https://huggingface. co /Qwen/Qwen2-7B-beta). Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the documentation from [`PretrainedConfig`] for more information. Args: vocab_size (`int`, *optional*, defaults to 151936): Vocabulary size of the Qwen2 model. Defines the number of different tokens that can be represented by the `inputs_ids` passed when calling [`Qwen2Model`] hidden_size (`int`, *optional*, defaults to 4096): Dimension of the hidden representations. intermediate_size (`int`, *optional*, defaults to 22016): Dimension of the MLP representations. num_hidden_layers (`int`, *optional*, defaults to 32): Number of hidden layers in the Transformer encoder. num_attention_heads (`int`, *optional*, defaults to 32): Number of attention heads for each attention layer in the Transformer encoder. num_key_value_heads (`int`, *optional*, defaults to 32): This is the number of key_value heads that should be used to implement Grouped Query Attention. If `num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if `num_key_value_heads=1` the model will use Multi Query Attention (MQA) otherwise GQA is used. When converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed by meanpooling all the original heads within that group. For more details checkout [this paper](https://arxiv. org /pdf/2305.13245.pdf). If it is not specified, will default to `32`. hidden_act (`str` or `function`, *optional*, defaults to `"silu"`): The non-linear activation function (function or string) in the decoder. max_position_embeddings (`int`, *optional*, defaults to 32768): The maximum sequence length that this model might ever be used with. initializer_range (`float`, *optional*, defaults to 0.02 ): The standard deviation of the truncated_normal_initializer for initializing all weight matrices. rms_norm_eps (`float`, *optional*, defaults to 1e-06): The epsilon used by the rms normalization layers. use_cache (`bool`, *optional*, defaults to `True`): Whether or not the model should return the last key/values attentions (not used by all models). Only relevant if `config. is_decoder =True`. tie_word_embeddings (`bool`, *optional*, defaults to `False`): Whether the model's input and output word embeddings should be tied. rope_theta (`float`, *optional*, defaults to 10000.0 ): The base period of the RoPE embeddings. rope_scaling (`Dict`, *optional*): Dictionary containing the scaling configuration for the RoPE embeddings. NOTE: if you apply new rope type and you expect the model to work on longer `max_position_embeddings`, we recommend you to update this value accordingly. Expected contents: `rope_type` (`str`): The sub-variant of RoPE to use. Can be one of ['default', 'linear', 'dynamic', 'yarn', 'longrope', 'llama3'], with 'default' being the original RoPE implementation. `factor` (`float`, *optional*): Used with all rope types except 'default'. The scaling factor to apply to the RoPE embeddings. In most scaling types, a `factor` of x will enable the model to handle sequences of length x * original maximum pre-trained length. `original_max_position_embeddings` (`int`, *optional*): Used with 'dynamic', 'longrope' and 'llama3'. The original max position embeddings used during pretraining. `attention_factor` (`float`, *optional*): Used with 'yarn' and 'longrope'. The scaling factor to be applied on the attention computation. If unspecified, it defaults to value recommended by the implementation, using the `factor` field to infer the suggested value. `beta_fast` (`float`, *optional*): Only used with 'yarn'. Parameter to set the boundary for extrapolation (only) in the linear ramp function. If unspecified, it defaults to 32. `beta_slow` (`float`, *optional*): Only used with 'yarn'. Parameter to set the boundary for interpolation (only) in the linear ramp function. If unspecified, it defaults to 1. `short_factor` (`List[float]`, *optional*): Only used with 'longrope'. The scaling factor to be applied to short contexts (< `original_max_position_embeddings`). Must be a list of numbers with the same length as the hidden size divided by the number of attention heads divided by 2 `long_factor` (`List[float]`, *optional*): Only used with 'longrope'. The scaling factor to be applied to long contexts (< `original_max_position_embeddings`). Must be a list of numbers with the same length as the hidden size divided by the number of attention heads divided by 2 `low_freq_factor` (`float`, *optional*): Only used with 'llama3'. Scaling factor applied to low frequency components of the RoPE `high_freq_factor` (`float`, *optional*): Only used with 'llama3'. Scaling factor applied to high frequency components of the RoPE use_sliding_window (`bool`, *optional*, defaults to `False`): Whether to use sliding window attention. sliding_window (`int`, *optional*, defaults to 4096): Sliding window attention (SWA) window size. If not specified, will default to `4096`. max_window_layers (`int`, *optional*, defaults to 28): The number of layers that use SWA (Sliding Window Attention). The bottom layers use SWA while the top use full attention. attention_dropout (`float`, *optional*, defaults to 0.0 ): The dropout ratio for the attention probabilities. ```python >>> from transformers import Qwen2Model, Qwen2Config >>> # Initializing a Qwen2 style configuration >>> configuration = Qwen2Config() >>> # Initializing a model from the Qwen2-7B style configuration >>> model = Qwen2Model(configuration) >>> # Accessing the model configuration >>> configuration = model. config ```""" model_type = "qwen2" keys_to_ignore_at_inference = ["past_key_values"] # Default tensor parallel plan for base model `Qwen2` base_model_tp_plan = { "layers.*.self_attn. q_proj ": "colwise", "layers.*.self_attn. k_proj ": "colwise", "layers.*.self_attn. v_proj ": "colwise", "layers.*.self_attn. o_proj ": "rowwise", "layers.*.mlp. gate_proj ": "colwise", "layers.*.mlp. up_proj ": "colwise", "layers.*.mlp. down_proj ": "rowwise", } def __init__( self, vocab_size=151936, hidden_size=4096, intermediate_size=22016, num_hidden_layers=32, num_attention_heads=32, num_key_value_heads=32, hidden_act="silu", max_position_embeddings=32768, initializer_range=0.02 , rms_norm_eps=1e-6, use_cache=True, tie_word_embeddings=False, rope_theta=10000.0 , rope_scaling=None, use_sliding_window=False, sliding_window=4096, max_window_layers=28, attention_dropout=0.0 , **kwargs, ): self. vocab_size = vocab_size self. max_position_embeddings = max_position_embeddings self. hidden_size = hidden_size self. intermediate_size = intermediate_size self. num_hidden_layers = num_hidden_layers self. num_attention_heads = num_attention_heads self. use_sliding_window = use_sliding_window self. sliding_window = sliding_window if use_sliding_window else None self. max_window_layers = max_window_layers # for backward compatibility if num_key_value_heads is None: num_key_value_heads = num_attention_heads self. num_key_value_heads = num_key_value_heads self. hidden_act = hidden_act self. initializer_range = initializer_range self. rms_norm_eps = rms_norm_eps self. use_cache = use_cache self. rope_theta = rope_theta self. rope_scaling = rope_scaling self. attention_dropout = attention_dropout # Validate the correctness of rotary position embeddings parameters # BC: if there is a 'type' field, move it to 'rope_type'. if self. rope_scaling is not None and "type" in self. rope_scaling : self. rope_scaling ["rope_type"] = self. rope_scaling ["type"] rope_config_validation(self) super().__init__( tie_word_embeddings=tie_word_embeddings, **kwargs, )