Ollama+Qwen2,轻松搭建支持函数调用的聊天系统
本文介绍如何通过Ollama结合Qwen2,搭建OpenAI格式的聊天API,并与外部函数结合来拓展模型的更多功能。
tools是OpenAI的Chat Completion API中的一个可选参数,可用于提供函数调用规范(function specifications)。这样做的目的是使模型能够生成符合所提供的规范的函数参数格式。同时,API 实际上不会执行任何函数调用。开发人员需要使用模型输出来执行函数调用。
Ollama支持OpenAI格式API的tool参数,在tool参数中,如果functions提供了参数,Qwen将会决定何时调用什么样的函数,不过Ollama目前还不支持强制使用特定函数的参数tool_choice。
注:本文测试用例参考OpenAI cookbook:https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models
本文主要包含以下三个部分:
-
模型部署:使用Ollama和千问,通过设置template,部署支持Function call的聊天API接口。
-
生成函数参数:指定一组函数并使用 API 生成函数参数。
-
调用具有模型生成的参数的函数:通过实际执行具有模型生成的参数的函数来闭合循环。
01.模型部署
单模型文件下载
使用ModelScope命令行工具下载单个模型,本文使用Qwen2-7B的GGUF格式:
modelscope download --model=qwen/Qwen2-7B-Instruct-GGUF --local_dir . qwen2-7b-instruct-q5_k_m.gguf
Linux环境使用
Liunx用户可使用魔搭镜像环境安装【推荐】
modelscope download --model=modelscope/ollama-linux --local_dir ./ollama-linux``cd ollama-linux``sudo chmod 777 ./ollama-modelscope-install.sh``./ollama-modelscope-install.sh
启动Ollama服务
ollama serve
创建ModelFile
复制模型路径,创建名为“ModelFile”的meta文件,其中设置template,使之支持function call,内容如下:
FROM /mnt/workspace/qwen2-7b-instruct-q5_k_m.gguf`` ``# set the temperature to 0.7 [higher is more creative, lower is more coherent]``PARAMETER temperature 0.7``PARAMETER top_p 0.8``PARAMETER repeat_penalty 1.05``TEMPLATE """{{ if .Messages }}``{{- if or .System .Tools }}<|im_start|>system``{{ .System }}``{{- if .Tools }}`` ``# Tools`` ``You are provided with function signatures within <tools></tools> XML tags. You may call one or more functions to assist with the user query. Don't make assumptions about what values to plug into functions. Here are the available tools:``<tools>{{- range .Tools }}{{ .Function }}{{- end }}</tools>`` ``For each function call, return a JSON object with function name and arguments within <tool_call></tool_call> XML tags as follows:``<tool_call>``{"name": <function-name>, "arguments": <args-json-object>}``</tool_call>{{- end }}<|im_end|>{{- end }}``{{- range .Messages }}``{{- if eq .Role "user" }}``<|im_start|>{{ .Role }}``{{ .Content }}<|im_end|>``{{- else if eq .Role "assistant" }}``<|im_start|>{{ .Role }}``{{- if .Content }}``{{ .Content }}``{{- end }}``{{- if .ToolCalls }}``<tool_call>``{{ range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}``{{ end }}</tool_call>``{{- end }}<|im_end|>``{{- else if eq .Role "tool" }}``<|im_start|>user``<tool_response>``{{ .Content }}``</tool_response><|im_end|>``{{- end }}``{{- end }}``<|im_start|>assistant``{{ else }}{{ if .System }}<|im_start|>system``{{ .System }}<|im_end|>``{{ end }}{{ if .Prompt }}<|im_start|>user``{{ .Prompt }}<|im_end|>``{{ end }}<|im_start|>assistant``{{ end }}``"""
创建自定义模型
使用ollama create命令创建自定义模型
ollama create myqwen2 --file ./ModelFile
运行模型:
ollama run myqwen2
02.生成函数参数
安装依赖
!pip install scipy --quiet``!pip install tenacity --quiet``!pip install tiktoken --quiet``!pip install termcolor --quiet``!pip install openai --quiet
使用OpenAI的API格式调用本地部署的qwen2模型
import json``import openai``from tenacity import retry, wait_random_exponential, stop_after_attempt``from termcolor import colored` ` ``MODEL = "myqwen2"``client = openai.OpenAI(` `base_url="http://127.0.0.1:11434/v1",` `api_key = "None"``)
实用工具
首先,让我们定义一些实用工具,用于调用聊天完成 API 以及维护和跟踪对话状态。
@retry(wait=wait_random_exponential(multiplier=1, max=40), stop=stop_after_attempt(3))``def chat_completion_request(messages, tools=None, tool_choice=None, model=MODEL):` `try:` `response = client.chat.completions.create(` `model=model,` `messages=messages,` `tools=tools,` `tool_choice=tool_choice,` `)` `return response` `except Exception as e:` `print("Unable to generate ChatCompletion response")` `print(f"Exception: {e}")` `return e
def pretty_print_conversation(messages):` `role_to_color = {` `"system": "red",` `"user": "green",` `"assistant": "blue",` `"function": "magenta",` `}`` ` `for message in messages:` `if message["role"] == "system":` `print(colored(f"system: {message['content']}\n", role_to_color[message["role"]]))` `elif message["role"] == "user":` `print(colored(f"user: {message['content']}\n", role_to_color[message["role"]]))` `elif message["role"] == "assistant" and message.get("function_call"):` `print(colored(f"assistant: {message['function_call']}\n", role_to_color[message["role"]]))` `elif message["role"] == "assistant" and not message.get("function_call"):` `print(colored(f"assistant: {message['content']}\n", role_to_color[message["role"]]))` `elif message["role"] == "function":` `print(colored(f"function ({message['name']}): {message['content']}\n", role_to_color[message["role"]]))``
基本概念(https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models#basic-concepts)
这里假设了一个天气 API,并设置了一些函数规范和它进行交互。将这些函数规范传递给 Chat API,以便模型可以生成符合规范的函数参数。
tools = [` `{` `"type": "function",` `"function": {` `"name": "get_current_weather",` `"description": "Get the current weather",` `"parameters": {` `"type": "object",` `"properties": {` `"location": {` `"type": "string",` `"description": "The city and state, e.g. San Francisco, CA",` `},` `"format": {` `"type": "string",` `"enum": ["celsius", "fahrenheit"],` `"description": "The temperature unit to use. Infer this from the users location.",` `},` `},` `"required": ["location", "format"],` `},` `}` `},` `{` `"type": "function",` `"function": {` `"name": "get_n_day_weather_forecast",` `"description": "Get an N-day weather forecast",` `"parameters": {` `"type": "object",` `"properties": {` `"location": {` `"type": "string",` `"description": "The city and state, e.g. San Francisco, CA",` `},` `"format": {` `"type": "string",` `"enum": ["celsius", "fahrenheit"],` `"description": "The temperature unit to use. Infer this from the users location.",` `},` `"num_days": {` `"type": "integer",` `"description": "The number of days to forecast",` `}` `},` `"required": ["location", "format", "num_days"]` `},` `}` `},``]
如果我们向模型询问当前的天气情况,它将会反问,希望获取到进一步的更多的参数信息。
messages = []``messages.append({"role": "system", "content": "Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous."})``messages.append({"role": "user", "content": "hi ,can you tell me what's the weather like today"})``chat_response = chat_completion_request(` `messages, tools=tools``)``assistant_message = chat_response.choices[0].message``messages.append(assistant_message)``assistant_message
ChatCompletionMessage(content=‘Of course, I can help with that. To provide accurate information, could you please specify the city and state you are interested in?’, role=‘assistant’, function_call=None, tool_calls=None)
一旦我们通过对话提供缺失的参数信息,模型就会为我们生成适当的函数参数。
messages.append({"role": "user", "content": "I'm in Glasgow, Scotland."})``chat_response = chat_completion_request(` `messages, tools=tools``)``assistant_message = chat_response.choices[0].message``messages.append(assistant_message)``assistant_message
ChatCompletionMessage(content=‘’, role=‘assistant’, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id=‘call_qq8e5z9w’, function=Function(arguments=‘{“location”:“Glasgow, Scotland”}’, name=‘get_current_weather’), type=‘function’)])
通过不同的提示词,我们可以让它反问不同的问题以获取函数参数信息。
messages = []``messages.append({"role": "system", "content": "Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous."})``messages.append({"role": "user", "content": "can you tell me, what is the weather going to be like in Glasgow, Scotland in next x days"})``chat_response = chat_completion_request(` `messages, tools=tools``)``assistant_message = chat_response.choices[0].message``messages.append(assistant_message)``assistant_message
ChatCompletionMessage(content=‘Sure, I can help with that. Could you please specify how many days ahead you want to know the weather forecast for Glasgow, Scotland?’, role=‘assistant’, function_call=None, tool_calls=None)
messages.append({"role": "user", "content": "5 days"})``chat_response = chat_completion_request(` `messages, tools=tools``)``chat_response.choices[0]
Choice(finish_reason=‘stop’, index=0, logprobs=None, message=ChatCompletionMessage(content=‘’, role=‘assistant’, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id=‘call_b7f3j7im’, function=Function(arguments=‘{“location”:“Glasgow, Scotland”,“num_days”:5}’, name=‘get_n_day_weather_forecast’), type=‘function’)]))
并行函数调用(https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models#parallel-function-calling)
支持一次提问中,并行调用多次函数
messages = []``messages.append({"role": "system", "content": "Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous."})``messages.append({"role": "user", "content": "what is the weather going to be like in San Francisco and Glasgow over the next 4 days"})``chat_response = chat_completion_request(` `messages, tools=tools, model=MODEL``)`` ``assistant_message = chat_response.choices[0].message.tool_calls``assistant_message
[ChatCompletionMessageToolCall(id=‘call_vei89rz3’, function=Function(arguments=‘{“location”:“San Francisco, CA”,“num_days”:4}’, name=‘get_n_day_weather_forecast’), type=‘function’),
ChatCompletionMessageToolCall(id=‘call_4lgoubee’, function=Function(arguments=‘{“location”:“Glasgow, UK”,“num_days”:4}’, name=‘get_n_day_weather_forecast’), type=‘function’)]
使用模型生成函数(https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models#how-to-call-functions-with-model-generated-arguments)
在这个示例中,演示如何执行输入由模型生成的函数,并使用它来实现可以为我们解答有关数据库的问题的代理。
本文使用Chinook 示例数据库(https://www.sqlitetutorial.net/sqlite-sample-database/)。
指定执行 SQL 查询的函数(https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models#specifying-a-function-to-execute-sql-queries)
首先,让我们定义一些有用的函数来从 SQLite 数据库中提取数据。
import sqlite3`` ``conn = sqlite3.connect("data/Chinook.db")``print("Opened database successfully")
def get_table_names(conn):` `"""Return a list of table names."""` `table_names = []` `tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table';")` `for table in tables.fetchall():` `table_names.append(table[0])` `return table_names`` `` ``def get_column_names(conn, table_name):` `"""Return a list of column names."""` `column_names = []` `columns = conn.execute(f"PRAGMA table_info('{table_name}');").fetchall()` `for col in columns:` `column_names.append(col[1])` `return column_names`` `` ``def get_database_info(conn):` `"""Return a list of dicts containing the table name and columns for each table in the database."""` `table_dicts = []` `for table_name in get_table_names(conn):` `columns_names = get_column_names(conn, table_name)` `table_dicts.append({"table_name": table_name, "column_names": columns_names})` `return table_dicts
现在可以使用这些实用函数来提取数据库模式的表示。
database_schema_dict = get_database_info(conn)``database_schema_string = "\n".join(` `[` `f"Table: {table['table_name']}\nColumns: {', '.join(table['column_names'])}"` `for table in database_schema_dict` `]``)
与之前一样,我们将为希望 API 为其生成参数的函数定义一个函数规范。请注意,我们正在将数据库模式插入到函数规范中。这对于模型了解这一点很重要。
tools = [` `{` `"type": "function",` `"function": {` `"name": "ask_database",` `"description": "Use this function to answer user questions about music. Input should be a fully formed SQL query.",` `"parameters": {` `"type": "object",` `"properties": {` `"query": {` `"type": "string",` `"description": f"""` `SQL query extracting info to answer the user's question.` `SQL should be written using this database schema:` `{database_schema_string}` `The query should be returned in plain text, not in JSON.` `""",` `}` `},` `"required": ["query"],` `},` `}` `}``]
执行 SQL 查询(https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models#executing-sql-queries)
现在让我们实现实际执行数据库查询的函数。
def ask_database(conn, query):` `"""Function to query SQLite database with a provided SQL query."""` `try:` `results = str(conn.execute(query).fetchall())` `except Exception as e:` `results = f"query failed with error: {e}"` `return results
使用 Chat Completions API 调用函数的步骤:
(https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models#steps-to-invoke-a-function-call-using-chat-completions-api)
步骤 1:向模型提示可能导致模型选择要使用的工具的内容。工具的描述(例如函数名称和签名)在“工具”列表中定义,并在 API 调用中传递给模型。如果选择,函数名称和参数将包含在响应中。
步骤 2:通过编程检查模型是否想要调用函数。如果是,则继续执行步骤 3。
步骤 3:从响应中提取函数名称和参数,使用参数调用该函数。将结果附加到消息中。
步骤 4:使用消息列表调用聊天完成 API 以获取响应。
messages = [{` `"role":"user",`` "content": "What is the name of the album with the most tracks?"``}]`` ``response = client.chat.completions.create(` `model='myqwen2',`` messages=messages, `` tools= tools, `` tool_choice="auto"``)`` ``# Append the message to messages list``response_message = response.choices[0].message` `messages.append(response_message)`` ``print(response_message)
ChatCompletionMessage(content=‘’, role=‘assistant’, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id=‘call_23nnhlv6’, function=Function(arguments=‘{“query”:“SELECT Album.Title FROM Album JOIN Track ON Album.AlbumId = Track.AlbumId GROUP BY Album.Title ORDER BY COUNT(*) DESC LIMIT 1”}’, name=‘ask_database’), type=‘function’)])
`# Step 2: determine if the response from the model includes a tool call.` `tool_calls = response_message.tool_calls``if tool_calls:` `# If true the model will return the name of the tool / function to call and the argument(s)`` tool_call_id = tool_calls[0].id` `tool_function_name = tool_calls[0].function.name` `tool_query_string = json.loads(tool_calls[0].function.arguments)['query']`` ` `# Step 3: Call the function and retrieve results. Append the results to the messages list.`` if tool_function_name == 'ask_database':` `results = ask_database(conn, tool_query_string)`` ` `messages.append({` `"role":"tool",`` "tool_call_id":tool_call_id, `` "name": tool_function_name, `` "content":results` `})`` ` `# Step 4: Invoke the chat completions API with the function response appended to the messages list` `# Note that messages with role 'tool' must be a response to a preceding message with 'tool_calls'` `model_response_with_function_call = client.chat.completions.create(` `model="myqwen2",` `messages=messages,` `) # get a new response from the model where it can see the function response` `print(model_response_with_function_call.choices[0].message.content)` `else:`` print(f"Error: function {tool_function_name} does not exist")``else: `` # Model did not identify a function to call, result can be returned to the user ``print(response_message.content)`
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】
更多推荐
所有评论(0)