大语言模型在解决通用问题的领域上表现都相当不错,但是在特定领域上往往会产生幻觉,比如在没有联网的状态下,询问大模型天气情况,有时候大模型会一本正经的胡说八道,

为了解决这种幻觉,业界也摸索出来很多方法:

1、网络搜索和本地知识库

主要是通过检索外部信息(网络或本地知识库)验证模型输出,提升准确性。主要路径如下:

网络搜索:模型结合实时网络数据进行事实核对(如通过搜索引擎获取最新天气信息)。

本地知识库:企业或机构构建私有知识库(如医疗、法律数据库),模型在生成答案前检索相关条目。

通过这两条路径可以有效的减少依赖模型训练数据的局限性,增强时效性和专业性。

2、调用第三方API

主要是通过直接调用外部服务接口(如天气查询API)获取实时数据。

比如用户询问天气时,模型调用get_weather API,返回真实天气数据。

通过调用第三方API可以确保输出与现实数据一致,避免虚构信息。

(3) 标准化协议(如MCP)

MCP(Model Communication Protocol):统一AI模型与业务服务的交互方式,解决工具调用风格不统一的问题。

1.用户提问 → 2. 模型解析需求并生成结构化函数调用 → 3. 开发者执行调用 → 4. 返回结果整合到回答中。

通过标准化JSON交互,兼容Function Calling机制。可以解耦AI逻辑与业务逻辑,提升开发效率和可扩展性。

那么上述的方法有一个核心的问题:如何让模型选择并调用正确的服务?

(1) 如何确定调用哪个MCP/Function?

依赖模型的意图理解能力:模型需通过训练学习何时需要调用工具(如天气查询、数据库访问)。工具定义(如函数名、参数要求)需提前提供给模型,帮助其匹配需求。

系统提示词(System Prompt):

通过预设规则或提示词(如“若用户询问天气,请调用天气API”)引导模型选择合适工具。

(2) 如何赋予模型Function Call能力?

Function Calling机制:

训练阶段:模型通过微调学习生成结构化函数调用(如{"name": "get_weather", "parameters": {"location": "北京"}})。

执行阶段:开发者解析模型输出的函数调用,执行实际API操作,并将结果反馈给模型。

多模型协同验证:使用多个模型对主模型的输出进行“多数表决”,提升调用准确性(如Mira Network的分布式验证网络)。

那么如何让大模型有function call的能力呢?下面我们通过用Unsloth来微调Qwen2.5-Coder-1.5B-Instruct,让大模型拥有自定义的function call的能力。主要流程如下:

从定义上讲,工具调用是指模型与外部工具交互的能力。正如你可能已经知道的,LLMs 擅长生成问答对、类似聊天的对话消息等文本。当提供有用的上下文时,LLMs 擅长响应提示,这些提示可以用作另一个系统处理的可执行命令参数。这可能是一个搜索引擎、计算器、公司API、电子表格或 SQL 查询。与这些系统交互通常通过单行命令完成,或者对于更复杂的任务,使用可运行的脚本语言,如 Bash 或 Python。令人头疼的部分是如何对这些命令进行排序,并分配正确的参数名称和值。如果我们让 LLM 为我们执行这些操作会怎么样?例如,“为 Alice 安排与 Bob 周一上午 10:00(美国东部时间)的预约”。

我们的目标是为将工具调用集成到 unsloth 的微调工作流程中提供一个简单的框架,首先我们用unsloth加载模型

from unsloth import FastQwen2Modelimport torchmax_seq_length = 2048  # Choose any! We auto support RoPE Scaling internally!dtype = None  # None for auto detection. Float16 for Tesla T4, V100, Bfloat16 for Ampere+load_in_4bit = True  # Use 4bit quantization to reduce memory usage. Can be False.# 4bit pre quantized models we support for 4x faster downloading + no OOMs.fourbit_models = [    "unsloth/Meta-Llama-3.1-8B-bnb-4bit",  # Llama-3.1 2x faster    "unsloth/Meta-Llama-3.1-70B-bnb-4bit",    "unsloth/Mistral-Small-Instruct-2409",  # Mistral 22b 2x faster!    "unsloth/mistral-7b-instruct-v0.3-bnb-4bit",    "unsloth/Phi-3.5-mini-instruct",  # Phi-3.5 2x faster!    "unsloth/Phi-3-medium-4k-instruct",    "unsloth/gemma-2-27b-bnb-4bit",  # Gemma 2x faster!    "unsloth/Llama-3.2-1B-bnb-4bit",  # NEW! Llama 3.2 models    "unsloth/Llama-3.2-1B-Instruct-bnb-4bit",    "unsloth/Llama-3.2-3B-Instruct-bnb-4bit",]  # More models at https://huggingface.co/unslothqwen_models = [    "unsloth/Qwen2.5-Coder-32B-Instruct",  # Qwen 2.5 Coder 2x faster    "unsloth/Qwen2.5-Coder-7B",    "unsloth/Qwen2.5-14B-Instruct",  # 14B fits in a 16GB card    "unsloth/Qwen2.5-7B",    "unsloth/Qwen2.5-72B-Instruct",  # 72B fits in a 48GB card]  # More models at https://huggingface.co/unslothmodel, tokenizer = FastQwen2Model.from_pretrained(    model_name="unsloth/Qwen2.5-Coder-1.5B-Instruct",    max_seq_length=None,    dtype=None,    load_in_4bit=False,    fix_tokenizer=False    # token = "hf_...", # use one if using gated models like meta-llama/Llama-2-7b-hf)

输出如下:

第二步我们复制一个copy

# save a copy because apply_chat_template() has in-place modificationsimport copytokenizer_orig = copy.deepcopy(tokenizer)

第三步我们自定义工具:这是我们将提供给模型的全部函数列表。标准格式来自 OpenAI 的函数调用定义。很有可能为工具调用而训练的模型采用了 OpenAI 标准格式。

以下是两个函数定义的示例。

get_vector_sum和get_dot_product的函数定义随后将被添加到提示词中作为我们的提示词的上下文:

求 a = [1, -1, 2] 和 b = [3, 0, -4] 的和。我们可以直接提供正确的那个:get_vector_sum但为了实验模型是否能够识别正确的函数来调用,我们将提供两者
def get_tool_definition_list():    return [        {            "type": "function",            "function": {                "name": "get_vector_sum",                "description": "Get the sum of two vectors",                "parameters": {                    "type": "object",                    "properties": {                        "a": {"type": "list", "description": "First vector"},                        "b": {"type": "list", "description": "Second vector"}                    },                    "required": ["a", "b"]                }            }        },        {            "type": "function",            "function": {                "name": "get_dot_product",                "description": "Get the dot product of two vectors",                "parameters": {                    "type": "object",                    "properties": {                        "a": {"type": "list", "description": "First vector"},                        "b": {"type": "list", "description": "Second vector"}                    },                    "required": ["a", "b"]                }            }        },    ]

下面是prompt定义

user_query = {    "role": "user",    "content": "Find the sum of a = [1, -1, 2] and b = [3, 0, -4]."}

以下是该函数的实际代码,您可能会注意到它包含 Python 文档字符串。这是因为apply_chat_template()可以接受并翻译函数为符合 PEP 257标准的 OpenAI 函数定义。

def get_vector_sum(a: list[float], b: list[float]) -> list[float]:    """    Performs element-wise addition of two numerical vectors.    Both vectors must be of the same length and contain numerical values.    Args:        a: First vector containing numerical values        b: Second vector containing numerical values    Returns:        Resulting vector where each element is the sum of corresponding elements in a and b    Raises:        ValueError: If vectors have different lengths    Example:        >>> get_vector_sum([1, 2], [3, 4])        [4, 6]    """    if len(a) != len(b):        raise ValueError("Vectors must be of the same length")    return [x + y for x, y in zip(a, b)]

现在让我们提示模型以 JSON 格式提供参数。您可能会注意到,我们传递给tool参数的是实际函数get_vector_sum(),而不是get_tool_definition_list()。可以尝试将其从tools=[get_vector_sum],更改为tools=[get_tool_definition_list(),看看是否适用于函数定义。

messages = []messages.append(user_query)tokenizer = copy.deepcopy(tokenizer_orig)input_ids = tokenizer.apply_chat_template(    messages,    tokenize=True,    add_generation_prompt=True,    add_special_tokens=False,    padding=True,    tools=[get_vector_sum],    return_tensors="pt",).to("cuda")print(tokenizer.decode(input_ids[0]))

输出结果如下:

如果我们将tool替换成get_tool_definition_list,会提示没有文本描述

以下是调用 unsloth 函数generate_with_grammar()的位置。该函数使用语法约束解码,这意味着它只会以 JSON 格式响应。它使用了 Saibo-creator 的 transformers-CFG 库的一个分支,输出结果与 llama-cpp-python 的输出非常相似。我们决定使用这个库,以便我们的代码以后在生产过程中可以移植到llama-cpp-python。如果成功,模型应该输出一个包含以下结果的单个有效 JSON 响应:

[    {        "name": "get_vector_sum",        "arguments": {            "a": [1, -1, 2],            "b": [3, 0, -4]        }    }]

输出限制的配置

#@title Function for Generation Constraint { display-mode: "form" }from functools import partialfrom transformers_cfg.grammar_utils import IncrementalGrammarConstraintfrom transformers_cfg.generation.logits_process import GrammarConstrainedLogitsProcessorJSON_ARR_GBNF = r"""# This is the same as json.gbnf but we restrict whitespaces at the end of the root array# Useful for generating JSON arraysroot   ::= arrvalue  ::= object | array | string | number | ("true" | "false" | "null") wsarr  ::=  "[\n" ws (            value    (",\n" ws value)*  )? "]"object ::=  "{" ws (            string ":" ws value    ("," ws string ":" ws value)*  )? "}" wsarray  ::=  "[" ws (            value    ("," ws value)*  )? "]" wsstring ::=  "\"" (    [^"\\\x7F\x00-\x1F] |    "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) # escapes  )* "\"" wsnumber ::= ("-"? ([0-9] | [1-9] [0-9]*)) ("." [0-9]+)? ([eE] [-+]? [0-9]+)? ws# Optional space: by convention, applied in this grammar after literal chars when allowedws ::= ([ \t\n] ws)?"""def generate_with_grammar(model, input_ids, **kwargs):    tokenizer = AutoTokenizer.from_pretrained(model.config.name_or_path)    grammar = IncrementalGrammarConstraint(JSON_ARR_GBNF, start_rule_name="root", tokenizer=tokenizer)    grammar_processor = GrammarConstrainedLogitsProcessor(grammar)    partial_generate = partial(        model.generate,        do_sample=False,        repetition_penalty=1.1,        num_return_sequences=1,        logits_processor=[grammar_processor],  # Ensure grammar_processor is accessible        temperature=None,        top_p=None,        top_k=None,        sliding_window=None,    )    # Execute generation with merged parameters    return partial_generate(        input_ids=input_ids,        **kwargs    )
output = generate_with_grammar(    model=model,    input_ids=input_ids)generated_tokens = output[:, input_ids.shape[1]:]decoded_output = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)for i, message in enumerate(decoded_output):    print(f"{message}")

从这里我们可以解析模型提供给我们的参数

import jsoncontent = json.loads(decoded_output[0])arguments = content[0]['arguments']vector_a = arguments['a']vector_b = arguments['b']print(f"args a: {vector_a}, b: {vector_b}")
args a: [1, -1, 2], b: [3, 0, -4]

在这里,我们实际调用一下get_vector_sum()并输出结果

result = get_vector_sum(vector_a, vector_b)print(f"result: {result}")
result: [4, -1, -2]

以下是发送给模型的最终提示,采用聊天消息的形式。为了确保模型回复实际答案,提示模型以以下内容回复:

你是一个超级有帮助的 AI 助手。你被要求根据以下上下文信息回答问题。

问题:

答案:

然后我们为分词器设置

continue_final_message=True

import randomimport stringdef generate_alphanumeric():    characters = string.ascii_letters + string.digits    result = ''.join(random.choice(characters) for _ in range(9))    return resultmessages = []original_prompt = user_query['content']prompt_with_context = f"""You are a super helpful AI assistant.You are asked to answer a question based on the following context information.Question:{original_prompt}"""messages.append({    "role": "user",    "content": prompt_with_context})tool_call_id = generate_alphanumeric()tool_calls = [{    "id": tool_call_id,    "type": "function",    "function": {        "name": "get_vector_sum",        "arguments": arguments    }}]messages.append({    "role": "assistant",    "tool_calls": tool_calls})messages.append({    "role": "tool",    "name": "get_vector_sum",    "content": result})messages.append({    "role": "assistant",    "content": "Answer:\n"})tokenizer = copy.deepcopy(tokenizer_orig)tool_prompt = tokenizer.apply_chat_template(    messages,    continue_final_message=True,    add_special_tokens=True,    return_tensors="pt",    return_dict=True,    tools=None,)tool_prompt = tool_prompt.to(model.device)print(tokenizer.decode(tool_prompt['input_ids'][0]))
<|im_start|>systemYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|><|im_start|>userYou are a super helpful AI assistant.You are asked to answer a question based on the following context information.Question:Find the sum of a = [1, -1, 2] and b = [3, 0, -4].<|im_end|><|im_start|>assistant<tool_call>{"name": "get_vector_sum", "arguments": {"a": [1, -1, 2], "b": [3, 0, -4]}}</tool_call><|im_end|><|im_start|>user<tool_response>[4, -1, -2]</tool_response><|im_end|><|im_start|>assistantAnswer:
out = model.generate(**tool_prompt, max_new_tokens=128)generated_text = out[0, tool_prompt['input_ids'].shape[1]:]print(tokenizer.decode(generated_text, skip_special_tokens=True))
The sum of a = [1, -1, 2] and b = [3, 0, -4] is [4, -1, -2]

为了进行比较,如果我们不使用工具调用来提示模型:

tokenizer = copy.deepcopy(tokenizer_orig)input_ids = tokenizer.apply_chat_template(    [user_query],    tokenize=True,    add_generation_prompt=True,    add_special_tokens=False,    padding=True,    tools=None,    return_tensors="pt",).to("cuda")print(tokenizer.decode(input_ids[0]))
<|im_start|>systemYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|><|im_start|>userFind the sum of a = [1, -1, 2] and b = [3, 0, -4].<|im_end|><|im_start|>assistant
output = model.generate(    input_ids=input_ids,    max_new_tokens=1024)generated_tokens = output[:, input_ids.shape[1]:]decoded_output = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)for i, message in enumerate(decoded_output):    print(f"{message}")
To find the sum of two vectors \( \mathbf{a} = [1, -1, 2] \) and \( \mathbf{b} = [3, 0, -4] \), we need to add corresponding components of the vectors.The sum of two vectors \( \mathbf{a} = [a_1, a_2, a_3] \) and \( \mathbf{b} = [b_1, b_2, b_3] \) is given by:\[ \mathbf{a} + \mathbf{b} = [a_1 + b_1, a_2 + b_2, a_3 + b_3] \]Let's compute this step-by-step using Python code.```python# Define the vectorsa = [1, -1, 2]b = [3, 0, -4]# Compute the sum of the vectorsresult = [a_i + b_i for a_i, b_i in zip(a, b)]print(result)``````output[4, -1, -2]```The sum of the vectors \( \mathbf{a} = [1, -1, 2] \) and \( \mathbf{b} = [3, 0, -4] \) is \( \boxed{[4, -1, -2]} \).

到此,我们发现,当使用function提示的时候,大模型就可以调用相应的function了.

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐