|
|
#
|
|
|
# # 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()
|