# # # Fine-tuning Script: # Please start by reading the Fine-tuning section of README.md. # # # Fine-tuning Data Preparation: # Then, if you would like to see some examples of how to prepare training samples for function calling, # which is actually ReAct prompting under the hood, please read this file. # # Inference Script: # If you are interested in implementing function calling via ReAct prompting for inference, # please refer to openai_api.py in our repository. # # If you have any questions, please raise an issue. # import json def format_train_sample(messages): # # You do not need the `function` role, as Qwen's function calling is actually implemented via ReAct, # not by adding a `function` role or `function_call` message. See openai_api.py for details. # # If you need the `system` role, you might need to modify `finetune.py` accordingly. # assert set(m["role"] for m in messages) == {"user", "assistant"} sample = { "conversations": [ { "from": m["role"], "value": m["content"], } for m in messages ] } return sample TOOL_DESC = """{name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters}""" REACT_INSTRUCTION = """Answer the following questions as best you can. You have access to the following APIs: {tools_text} 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 [{tools_name_text}] 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!""" def build_react_instruction(functions): tools_text = [] tools_name_text = [] for func_info in functions: name = func_info.get("name", "") name_m = func_info.get("name_for_model", name) name_h = func_info.get("name_for_human", name) desc = func_info.get("description", "") desc_m = func_info.get("description_for_model", desc) tool = TOOL_DESC.format( name_for_model=name_m, name_for_human=name_h, description_for_model=desc_m, parameters=json.dumps(func_info["parameters"], ensure_ascii=False), ) tools_text.append(tool) tools_name_text.append(name_m) tools_text = "\n\n".join(tools_text) tools_name_text = ", ".join(tools_name_text) instruction = REACT_INSTRUCTION.format( tools_text=tools_text, tools_name_text=tools_name_text, ) return instruction def main(): example_train_samples = [] example_functions = [ { "name_for_human": "在线搜索", "name_for_model": "search", "description_for_model": "使用此工具可以搜索群内成员的信息,或者在互联网上搜索信息。Format the arguments as a JSON object.", # If you expect `Action Input` to be a JSON. "parameters": [ { "name": "keywords", "description": "需要搜索的关键词。", "required": True, "schema": {"type": "string"}, }, ], }, { "name_for_human": "天气信息", "name_for_model": "get_weather", "description_for_model": "查询某个地点的天气信息。Format the arguments as a JSON object.", "parameters": [ { "name": "position", "description": "需要查询天气的地点。", "required": True, "schema": {"type": "string"}, }, ], }, { "name_for_human": "封禁用户", "name_for_model": "ban_user", "description_for_model": "在用户多次触犯道德规范时,使用此工具可以封禁用户。", "parameters": [], } ] example_instruction = build_react_instruction(example_functions) # This example has multiple actions in one single response. example_train_samples.append( [ { "role": "user", "content": f"{example_instruction}\n\nQuestion: Who is jay chou? And who is his wife?", }, { "role": "assistant", "content": """ Thought: I need to google Jay Chou. Action: google_search Action Input: {"search_query": "Jay Chou"} Observation: Jay Chou is ... Thought: I need to google Jay's wife. Action: google_search Action Input: {"search_query": "Jay Chou's wife"} Observation: Jay Chou's wife is ... Thought: I now know the final answer Final Answer: Jay Chou is ... His wife is ... """.strip(), }, ] ) # This example involves multiple rounds of conversation. example_train_samples.append( [ # Round #1 { "role": "user", "content": f"{example_instruction}\n\nQuestion: 123+456=?", }, { "role": "assistant", "content": """ Thought: I need to compute the result using Code Interpreter. Action: code_interpreter Action Input: ```py 123 + 456 ``` Observation: 579 Thought: I now know the final answer Final Answer: 579 """.strip(), }, # Round #2 { "role": "user", "content": "Multiply the result by 2.", }, { "role": "assistant", "content": """ Thought: Code Interpreter is helpful for answering this question. Action: code_interpreter Action Input: ```py 579 * 2 ``` Observation: 1158 Thought: I now know the final answer Final Answer: 1158 """.strip(), }, # Round #3 { "role": "user", "content": "You are so smart, Qwen.", # No action is needed for this question. }, { "role": "assistant", "content": """ Thought: I now know the final answer Final Answer: Thank you. """.strip(), }, # Round #4 { "role": "user", "content": "Please re-execute the code for computing my first question again.", }, { "role": "assistant", "content": """ Thought: I need to re-compute the result. Action: code_interpreter Action Input: ```py 123 + 456 ``` Observation: 579 Thought: I now know the final answer Final Answer: 579 """.strip(), }, ] ) example_train_samples = [format_train_sample(x) for x in example_train_samples] with open( "example_func_call_train_samples.json", "w" ) as fout: # data for fine-tuning fout.write(json.dumps(example_train_samples, indent=2, ensure_ascii=False)) if __name__ == "__main__": main()