某些模型能够进行工具方法调用:生成用户自定义的方法的签名并调用它。一般来说,此交互过程主要包含下面几个步骤:

  • LLM(大语言模型) 根据用户的问题推理生成调用工具方法的名称和参数
  • 调用工具,返回简单的结果
  • 再次调用 LLM ,生成自然流畅的回答

下面我们来说明这一过程。

应该是因为 langchiandeepseek 的支持还完善,使用 deepseek-r1 无法生成工具方法签名,使用 MFDoom/deepseek-r1-tool-calling:7b 可以生成工具方法签名,但是在调用工具方法时不成功。
所以本文仅使用 llama3.1

准备

在正式开始撸代码之前,需要准备一下编程环境。

  1. 计算机
    本文涉及的所有代码可以在没有显存的环境中执行。 我使用的机器配置为:

    • CPU: Intel i5-8400 2.80GHz
    • 内存: 16GB
  2. Visual Studio Code 和 venv 这是很受欢迎的开发工具,相关文章的代码可以在 Visual Studio Code 中开发和调试。 我们用 pythonvenv 创建虚拟环境, 详见:
    在Visual Studio Code中配置venv

  3. Ollama 在 Ollama 平台上部署本地大模型非常方便,基于此平台,我们可以让 langchain 使用 llama3.1qwen2.5 等各种本地大模型。详见:
    在langchian中使用本地部署的llama3.1大模型

定义工具方法

下面我们定义两个简单的工具方法,它可以做加法和乘法运算。

@tool
def add(a: int, b: int) -> int:
    """计算a和b的和。"""
    print (f"add is called...{a}+{b}")
    return a + b

@tool
def multiply(a: int, b: int) -> int:
    """计算a和b的乘积。"""
    print (f"multiply is called...{a}*{b}")
    return a * b

tools = [add, multiply]

绑定工具

下面,我们将 LLM 和 工具 关联起来。

llm = ChatOllama(model=model_name,temperature=0.1,verbose=True)
llm_with_tools = llm.bind_tools(tools)

生成工具方法签名

我们出一个题目,让 llm_with_tools 生成工具方法签名。

query = "3 * 12等于多少? 11 + 49等于多少?"
messages = [HumanMessage(query)]

ai_msg = llm_with_tools.invoke(messages)
print(f' tool_calls is:{ai_msg.tool_calls}')
messages.append(ai_msg)

经过上面的一次调用,llm_with_tools 将返回一个 AIMessage ,它的 tool_calls 的内容为:

[
    {'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': '851a6249-e8da-4f3a-81ba-8377569d2dbe', 'type': 'tool_call'}, 
    {'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': '3929f2b1-e160-4f45-aaeb-eeea876a1b7d', 'type': 'tool_call'}
]

真棒,我们把用户的提问生成了两个 tool_call 。然后把消息合并,为最后大模型可以返回流畅的答案做准备。

调用工具方法

下面我们使用模型生成的参数来调用工具行数,生成答案:

for tool_call in ai_msg.tool_calls:
    selected_tool = {"add": add, "multiply": multiply}[tool_call["name"].lower()]
    tool_msg = selected_tool.invoke(tool_call)
    messages.append(tool_msg)
print(f'now,messages are:{messages}')

上面的循环主要作用是根据 tool_calls 调用工具方法,我们看看现在的消息内容:

[
    HumanMessage(content='3 * 12等于多少? 11 + 49等于多少?', additional_kwargs={}, response_metadata={}), 
    AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'llama3.1', 'created_at': '2025-02-07T06:59:30.4497694Z', 'done': True, 'done_reason': 'stop', 'total_duration': 44403254600, 'load_duration': 13950016100, 'prompt_eval_count': 239, 'prompt_eval_duration': 7522000000, 'eval_count': 43, 'eval_duration': 22923000000, 'message': Message(role='assistant', content='', images=None, tool_calls=None)}, id='run-306ea35c-1b9b-4871-b7f7-4d726892e9bf-0', tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': '851a6249-e8da-4f3a-81ba-8377569d2dbe', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': '3929f2b1-e160-4f45-aaeb-eeea876a1b7d', 'type': 'tool_call'}], usage_metadata={'input_tokens': 239, 'output_tokens': 43, 'total_tokens': 282}), 
    ToolMessage(content='36', name='multiply', tool_call_id='851a6249-e8da-4f3a-81ba-8377569d2dbe'), 
    ToolMessage(content='60', name='add', tool_call_id='3929f2b1-e160-4f45-aaeb-eeea876a1b7d')
]

此过程又生成了两个新的 ToolMessage

再次调用 LLM,生成回答

我们将上述生成的消息列表塞给 LLM ,让它给出自然流畅的回答:

result = llm_with_tools.invoke(messages)
print(f'result is:{result.content}')

最终返回的结果为:

So, the answers to your questions are:

* 3 * 12 = 36
* 11 + 49 = 60

总结

在这篇文章里,我们逐步演示了如何使用大语言模型调用外部工具方法并返回结果。当然,目前的方式还比较笨拙,比如:如果用户的提问不涉及到调用工具方法时,上述代码显然会报错。
在后面的部分中我们将让大语言模型与外部工具的交互更加智能,变成 Agent(智能体)

代码

本文涉及的所有代码以及相关资源都已经共享,参见:

参考:

🪐祝好运🪐