Skip to content

Ollama-python:工具赋能大模型,以数学运算智能问答应用为例

发表: at 10:30

Ollama 是一个优秀的本地部署与管理大模型的框架。通过 Ollama,我们可以在本地部署、定制自己的大模型服务。Ollama-python:定义工具集,增强大模型的能力介绍了如何通过调用工具,让大模型准确地回答数字运算相关的问题(大模型本身一般不擅长回答较长的数字的数学运算问题)。本文在前文的基础上,初步搭建数学运算智能问答应用。该智能应用,具有以下功能:

使用以下提问示例,验证数学运算智能问答应用的功能:

用户、助手、工具

助手、用户、工具.png

在本文的应用示例中,助手为 qwen2.5:7b 模型,工具为数学运算函数(加减乘除)。通过 Ollama,在本地部署运行助手( qwen2.5:7b 模型)。基于 Ollama 的 python sdk,完成上述交互流程步骤。

代码实现

工具定义

from ollama import ChatResponse
from ollama import Client

def add_two_numbers(a: float, b: float) -> float:
    """
    Add two numbers (including decimals)

    Args:
      a (float): The first number
      b (float): The second number

    Returns:
      float: The sum of the two numbers
    """
    return a + b

def multiply_two_numbers(a: float, b: float) -> float:
    """
    Multiply two numbers (including decimals)

    Args:
      a (float): The first number
      b (float): The second number

    Returns:
      float: The product of the two numbers
    """
    return a * b


def divide_two_numbers(a: float, b: float) -> float:
    """
    Divide one number by another (including decimals)

    Args:
      a (float): The numerator
      b (float): The denominator

    Returns:
      float: The quotient of a divided by b
    """
    if b == 0:
        raise ZeroDivisionError("Division by zero is not allowed.")
    return a / b


def subtract_two_numbers(a: float, b: float) -> float:
    """
    Subtract two numbers
    """
    return a - b

# Tools can still be manually defined and passed into chat
subtract_two_numbers_tool = {
    'type': 'function',
    'function': {
      'name': 'subtract_two_numbers',
      'description': 'Subtract two numbers',
      'parameters': {
        'type': 'object',
        'required': ['a', 'b'],
        'properties': {
          'a': {'type': 'float', 'description': 'The first number'},
          'b': {'type': 'float', 'description': 'The second number'},
        },
      },
    },
}

# 定义可用的函数工具
available_functions = {
    'add_two_numbers': add_two_numbers,
    'subtract_two_numbers': subtract_two_numbers,
    'multiply_two_numbers': multiply_two_numbers,
    'divide_two_numbers': divide_two_numbers
}

# 工具列表,作为对话的上下文传递给助手(大模型)
tool_list = [add_two_numbers, subtract_two_numbers_tool, multiply_two_numbers, divide_two_numbers]

工具调用

  1. 工具调用检测与处理

    • 检查助手响应中是否包含工具调用(response.message.tool_calls)。
    • 如果存在工具调用,从 available_functions 中查找对应的工具函数并执行。
    • 将工具调用的结果整合到会话历史中,以便助手参考。
  2. 工具调用验证:如果工具不存在,打印日志通知用户,提示找不到工具。

  3. 会话逻辑管理

    • 将助手响应和工具调用结果添加到会话历史(messages)中。
    • 在工具调用结束后,返回最终的工具输出结果。
def call_tools(response, messages):
    final_tool_output = None
    output = None
    is_tool_call = False
    if response.message.tool_calls:
        is_tool_call = True
        print('--------------开始工具调用-----------------')
    while response.message.tool_calls:
        for tool in response.message.tool_calls:
            if function_to_call := available_functions.get(tool.function.name):
                print('调用工具:', tool.function.name)
                print('参数:', tool.function.arguments)
                output = function_to_call(**tool.function.arguments)
                final_tool_output = output
                print('工具输出:', output)
            else:
                print('找不到工具: ', tool.function.name)
        messages.append(response.message)
        messages.append({'role': 'tool', 'content': str(output), 'name': tool.function.name})
        response = client.chat(model=model_name, messages=messages, tools=tool_list)
    if is_tool_call:
        print('--------------工具调用结束-----------------')
    return final_tool_output

多轮对话交互

  1. 连续对话循环
    • 使用 while True 实现用户和助手的连续对话。
    • 当用户输入特定的退出指令(如 ‘退出’、‘quit’ 等)时,程序退出。
  2. 用户提问
    • 用户的输入通过 input() 获取,并添加到 messages 中,标记为 role: ‘user’。
  3. 助手响应和工具调用
    • 调用 client.chat(),传入模型名称、会话历史和工具列表。
    • 助手的响应(response)可能包含工具调用,通过 call_tools() 处理这些工具调用。
  4. 工具调用结果处理
    • 如果工具调用返回结果(final_solve_output),将其添加到会话历史,标记为 role: ‘tool’。这里强调多次工具执行的最终结果,降低助手响应的幻觉可能性。
  5. 助手的流式响应处理
    • 使用 stream=True 参数,逐块获取助手的流式响应。
client = Client()
model_name = 'qwen2.5:7b'
messages = []
def chat_with_ollama():
    print("计算助手已启动!输入 '退出' 结束对话。")

    whileTrue:
        # 获取用户输入
        user_input = input("用户: ")
        if user_input.lower() in ["退出", "quit", "stop", "bye", "拜拜"]:
            print("退出与计算助手的对话。")
            break

        # 将用户输入添加到对话历史
        messages.append({"role": "user", "content": user_input})

        response: ChatResponse = client.chat(
            model=model_name,
            messages=messages,
            tools=[add_two_numbers, subtract_two_numbers_tool, multiply_two_numbers, divide_two_numbers])
        final_solve_output = call_tools(response, messages)
        if final_solve_output isnotNone:
            messages.append({'role': 'tool', 'content': str(final_solve_output), 'name': '最终计算结果'})

        response_content = ""
        print("计算助手: ", end='', flush=True)
        for part in client.chat(model=model_name, messages=messages, stream=True):
            response_content += part['message']['content']
            print(f"{part['message']['content']}", end='', flush=True)
        print()
        # 将模型的响应添加到对话历史
        messages.append({"role": "assistant", "content": response_content})


if __name__ == '__main__':
    chat_with_ollama()

测试验证

用户依次提出以下问题:

  1. 现在要进一批货,单价是35.324,数量是765345677,商家优惠 1500 元,一共要付多少钱?

  2. 后来商家又说,可以在上面的基础上,优惠两个点,最终要付多少钱?

  3. 商家后来又说,再打 9.4523 折,太好了。最终要付多少钱?

    验证结果如下(模型助手可正确理解”优惠两个点“、”9.4523 折“等中文表达,将用户的表达转化为正确的工具调用):

1740068932(1).png


计算助手已启动!输入 '退出' 结束对话。
用户: 现在要进一批货,单价是35.324,数量是765345677,商家优惠 1500 元,一共要付多少钱?
--------------开始工具调用-----------------
调用工具: multiply_two_numbers
参数: {'a': 35.324, 'b': 765345677}
工具输出: 27035070694.348
调用工具: subtract_two_numbers
参数: {'a': 27035070694.348, 'b': 1500}
工具输出: 27035069194.348
--------------工具调用结束-----------------
计算助手: 根据计算结果,您需要支付的金额是27,035,069,194.348元。由于实际交易中通常不会出现如此巨大的金额,这里可能是一个模拟或测试场景。在实际情况中,如果您有任何疑问或需要进一步的帮助,请随时告诉我!
用户: 后来商家又说,可以在上面的基础上,优惠两个点,最终要付多少钱?
--------------开始工具调用-----------------
调用工具: multiply_two_numbers
参数: {'a': 27035069194.348, 'b': 0.98}
工具输出: 26494367810.46104
--------------工具调用结束-----------------
计算助手: 根据计算结果,在享受了2%的额外优惠后,您最终需要支付的金额是26,494,367,810.46元。

如果您有任何其他问题或需要进一步的帮助,请随时告诉我!
用户: 回答正确,给你一个赞,今天晚饭可以加只鸡腿!
计算助手: 太好了,很高兴能帮到您!既然得到了您的认可,今晚的晚餐确实可以让您的饭菜更加丰富哦。如果以后还有任何数学计算或其他问题需要帮助,请随时告诉我。祝您用餐愉快!
用户: 商家后来又说,再打 9.4523 折,太好了。最终要付多少钱?
--------------开始工具调用-----------------
调用工具: multiply_two_numbers
参数: {'a': 26494367810.46104, 'b': 0.94523}
工具输出: 25043271285.48209
--------------工具调用结束-----------------
计算助手: 根据计算结果,在享受了额外的9.4523折优惠后,您最终需要支付的金额是25,043,271,285.48元。

这个数字看起来依然非常大,不过已经反映了最后一次折扣后的总价。如果您有任何其他问题或需要进一步的帮助,请随时告诉我!希望这次计算结果对您有帮助。

文章来源:微信公众号-数智脉动,原始发表时间:2025年02月21日。


上篇文章
台大李宏毅2025 AI Agent新课来了!
下篇文章
好书推荐 |《从零构建大模型》:从理论到实践,手把手教你打造自己的大语言模型