大模型基础知识--多轮对话与函数调用

一、大模型多轮对话实现

大模型本身是没有记忆,每次API调用都是独立的,因此体现出来的记忆功能,实际是请求时消息的上下文;即实现多轮对话的关键在于客户端维护上下文,并在每次请求时将完整的历史对话传回模型。

核心原理

  1. 维护对话历史:用数组(messages)保存用户提问和模型回答。

  2. 累积传递:每次调用 API 时,将完整的 messages 发送给模型。

  3. 动态更新:收到新回答后追加到 messages,用于下一轮对话。

from agently import Agently

Agently.set_settings(
    "OpenAICompatible",
    {
        "model":"qwen2.5:7b",
        "base_url":"http://127.0.0.1:11434/v1/",
        "api_key":"nothing",
    },
)

llm = Agently.create_agent()

print(
    llm.set_chat_history(
        [
            {"role": "user", "content": "帮我记一个笔记,知乎知学堂每周一、周三有课"},
            {"role": "assistant", "content": "好的"},
        ]
    )
    .input("知乎知学堂每周周几有课?")
    .start()
)

  结果:

image

 Python 示例(OpenAI 兼容接口):

from openai import OpenAI
import os
client = OpenAI(api_key=os.getenv("API_KEY"))
messages = []
while True:
   user_input = input("你: ")
   if user_input.lower() == "exit":
       break
   messages.append({"role": "user", "content": user_input})
   resp = client.chat.completions.create(model="gpt-4o", messages=messages)
   ai_msg = resp.choices[0].message.content
   print(f"AI: {ai_msg}")
   messages.append({"role": "assistant", "content": ai_msg})

 

上下文长度控制

多轮对话或长输入、长输出场景下,需要同时考虑模型的上下文窗口限制和输入、输出长度限制,达到长度限制后模型会截断或报错。

实现超长多轮对话现有的解决方案:

解放方案 优势 劣势  
保存历史记录 简单直接,完整存储了历史对话记录,对历史对话的理解肯定是对全面的 token消耗大,内容冗余,超过限制会损失信息  
摘要生成:对历史对话记录总结记录 相比直接记录历史对话减少了冗余内容,只抓关键点,大大增强了多轮对话能力 摘要效果取决于模型,模型不好可能会大量丢失关键信息,且需要额外的token去总结摘要  
外部存储 增加记忆数据库,可以存储更多多轮对话的内容,在时间和容量上跨度很大 需要构建外部记忆系统,并需要具备对应的检索能力  
基于主题的上下文聚焦 这种方法帮助模型在复杂多主题对话中保持清晰的上下文结构,并避免无关信息占用上下文窗口。

多轮对话技术优化主要看重时间上的跨度和有用信息的集中程度

效果取决于检索能力,关键点把握不住,会存在语义偏差

 

 

一般是通过历史对话管理 + 上下文传递,即可让无记忆的大模型实现连贯的多轮对话,并可结合摘要、外部存储等技术突破超长对话限制。

 

二. 工具调用(Function Calling)

1.定义:

为大模型提供了与外部工具或代码相连的功能,通过该功能增强大模型在实时性、数据计算等应用场景的推理效果或进行其他外部操作,包括信息检索、数据库操作、图谱搜索与处理等工具调用场景。

  • Function Calling成立的模型能力基础:
    • 问题理解和行动规划
    • 结构化数据输出
    • In-Context Learning

Function Calling让模型输出不再局限于自身推理输出,而是可以与外部系统交互,完成更复杂的任务

  • 常见Function Calling应用场景包括:
    • 查询检索,补充额外信息(如RAG、搜索)
    • 理解用户输入,向外部系统写入信息(如表单填写)
    • 调用外部系统能力,完成实际行为动作(如下订单)

 

2 工作原理

OpenAI官方说明文档:https://platform.openai.com/docs/guides/function-calling

 

image

 2.1.解释说明:

Calling是结果,理解和选择才是第一步
  • 除了代表用户诉求的Prompt之外,Function Calling还需要将可用的工具信息(Function Definitions)也提供给模型
  • 在第一次请求时,模型的核心工作如下:
    1. 理解Prompt所代表的“诉求”和Definitions所代表的“行动可能性”
    2. “选择”完成“诉求”所需要进行的“行动”(从“行动可能性”中获得)
    3. 根据所选择的“行动”,给出执行“行动”所需的“行动参数”(Parameters)
  • 那么想一想:
    1. 什么影响“选择”的效果?诉求的清晰表达,准确的描述工具情况,模型的选择
    2. 什么影响“行动”的可执行性和效果?
作为可选项的结果回调和最终回复输出
  • 在对话流中,将Function Calling的结果(Function Result)与初始的Prompt诉求再次组合,提供给模型以获得最终的回复输出,是常见的流程(RAG就是一个典型的例子)
  • 但如果我们将Function Calling用于非对话流场景,最终回复输出就不一定是必选项了,例如:
    1. 【只需要完成Calling动作】我们只是希望通过Function Calling完成行动选择和发起,接下来就进入业务处理流程,例如:理解用户表达并代替用户下单
    2. 【只需要完成行动参数Parameters生成】我们只是希望将Function Calling做好工具使用决策,并完成部分请求参数的生成,接下来需要走业务流程补全其他参数(比如鉴权信息),例如:敏感数据查询

在实际生产中,不给出最终回复输出,而只是使用Function Calling返回的调用方法数据,是很常见的用法。

2.2 实践

第一步:调用决策

import requests
from typing import Dict, Optional

def get_weather_by_location(lat: str, lon: str) -> Dict:
    """
    根据经纬度获取天气信息(使用免费API)

    Args:
        lat: 纬度
        lon: 经度

    Returns:
        包含天气信息的字典,如果获取失败则返回None
    """
    try:
        # 使用免费的天气API(示例,实际使用时可能需要更换)
        base_url = f"https://api.open-meteo.com/v1/forecast"

        # 构建请求参数
        params = {
            "latitude": float(lat),
            "longitude": float(lon),
            "current_weather": "true",
            "timezone": "auto"
        }

        # 发送请求
        response = requests.get(base_url, params=params, timeout=10)
        response.raise_for_status()

        # 解析响应数据
        data = response.json()

        if "current_weather" in data:
            current = data["current_weather"]
            weather_info = {
                "temperature": current["temperature"],
                "windspeed": current["windspeed"],
                "winddirection": current["winddirection"],
                "weathercode": current["weathercode"],
                "time": current["time"],
                "latitude": data["latitude"],
                "longitude": data["longitude"],
                "timezone": data["timezone"]
            }
            return weather_info
        else:
            print("API响应中未找到天气数据")
            return None

    except requests.exceptions.RequestException as e:
        print(f"请求天气API时出错: {e}")
        return None
    except ValueError as e:
        print(f"经纬度格式错误: {e}")
        return None
    except Exception as e:
        print(f"获取天气信息时发生未知错误: {e}")
        return None


function_mappings = {
    "get_weather": get_weather_by_location,
}
#给出工具定义
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",  # 工具名(类似ID)
            "description": "获取当前指定经纬度位置的天气",  # 工具描述
            "parameters": {  # 描述参数细节
                "type": "object",
                "properties": {
                    "lat": {"type": "string"},  # lat参数描述
                    "lon": {"type": "string"},  # lon参数描述
                },
                "required": ["lat", "lon"],
                "additionalProperties": False,
            },
        },
    },
]

import os
import dotenv

dotenv.load_dotenv(dotenv.find_dotenv())

from openai import OpenAI

client = OpenAI(
    # DeepSeek
    api_key=os.environ.get("DEEPSEEK_API_KEY"),
    base_url=os.environ.get("DEEPSEEK_BASE_URL"),
)


# 给出诉求表达
messages = [{"role": "user", "content": "What's the weather like in Beijing today?"}]


# 发起请求
completion = client.chat.completions.create(
    model="deepseek-chat",
    messages=messages,  # type: ignore
    # 把工具定义提交给模型,就已经默认启用了Function Calling
    tools=tools,  # type: ignore
)
print(completion.choices[0].message.model_dump_json(indent=2))
if completion.choices[0].message.tool_calls:
    print(completion.choices[0].message.tool_calls[0])
else:
    print("No function is called.")

 运行结果:注:不同的模型输出结果不同

下面是用deepseek模型输出结果

image

 这个是使用本地模型ollama的qwen模型的输出结果

image

 

第二步:实际的工具调用

function_calling_message = completion.choices[0].message
function_calling = completion.choices[0].message.tool_calls[0]

print("Call Function Name:", function_calling.function.name)
print("Call Function Arguments:", function_calling.function.arguments)
#调用函数
function_result = function_mappings[function_calling.function.name](**json.loads(function_calling.function.arguments))
print(fucnction_result)

 运行结果:

image

 

第三步:将工具调用结果告知大模型

# 必须:让模型知道之前给了一个什么指令(包含tool_call_id)
messages.append(function_calling_message)
# 包含了tool_call_id的结果加入消息列
messages.append(
    {
        "role": "tool", # 角色固定写"tool"
        "tool_call_id": function_calling.id, # 要写模型上次返回的ID,帮助模型去定位这是之前发出的哪个指定的结果
        "content": str(function_result), # 工具调用结果
    }
)
print(messages)

final_result = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
tools=tools,
)
print(final_result.choices[0].message.content)

  运行结果:

image

 

posted @ 2026-04-13 17:36  sunshine_coast  阅读(49)  评论(0)    收藏  举报