logo

LLM:模型调用

王哲峰 / 2024-08-14


目录

基本概念

Prompt

Prompt 最初是 NLP(自然语言处理)研究者为下游任务设计出来的一种任务专属的输入模板, 类似于一种任务(例如:分类,聚类等)会对应一种 Prompt。 在 ChatGPT 推出并获得大量应用之后,Prompt 开始被推广为给大模型的所有输入。 即,每一次访问大模型的输入为一个 Prompt,而大模型给我们的返回结果则被称为 Completion。

Temperature

LLM 生成是具有随机性的,在模型的顶层通过选取不同预测概率的预测结果来生成最后的结果。 一般可以通过控制 temperature 参数来控制 LLM 生成结果的随机性与创造性。

temperature 一般取值在 0~1 之间,当取值较低接近 0 时,预测的随机性会较低, 产生更保守、可预测的文本,不太可能生成意想不到或不寻常的词。 当取值较高接近 1 时,预测的随机性会较高,所有词被选择的可能性更大, 会产生更有创意、多样化的文本,更有可能生成不寻常或意想不到的词。

对于不同的问题与应用场景,可能需要设置不同的 temperature。例如,

System Prompt

System Prompt 是随着 ChatGPT API 开放并逐步得到大量使用的一个新兴概念, 事实上,它并不在大模型本身训练中得到体现,而是大模型服务方为提升用户体验所设置的一种策略。

具体来说,在使用 ChatGPT API 时,你可以设置两种 Prompt:

一般设置 System Prompt 来对模型进行一些初始化设定,例如, 可以在 System Prompt 中给模型设定希望它具备的人设,如一个个人知识库助手等。 System Prompt 一般在一个会话中仅有一个。在通过 System Prompt 设定好模型的人设或是初始设置后, 可以通过 User Prompt 给出模型需要遵循的指令。例如,当我们需要一个幽默风趣的个人知识库助手, 并向这个助手提问我今天有什么事时,可以构造如下的 Prompt:

{
    "system prompt": "你是一个幽默风趣的个人知识库助手,可以根据给定的知识库内容回答用户的提问,注意,你的回答风格应是幽默风趣的",
    "user prompt": "我今天有什么事务?"
}

通过如上 Prompt 的构造,我们可以让模型以幽默风趣的风格回答用户提出的问题。

LLM API

主要介绍四种大语言模型:ChatGPT、文心一言、讯飞星火、智谱 GLM 的 API 申请指引和 Python 版本的原生 API 调用方法, 可以按照实际情况选择一种自己可以申请的 API 进行使用即可。

如果你需要在 LangChain 中使用 LLM,可以参照 LangChain 中的调用方式。

OpenAI ChatGPT

API key 申请

API key 配置

加载环境变量

读取 .env 文件,将密钥加载到环境变量

import os
from dotenv import load_dotenv, find_dotenv

# 读取本地/项目的环境变量
# find_dotenv(): 寻找并定位 `.env` 文件的路基那个
# load_dotenv(): 读取 `.env` 文件,并将其中的环境变量加载到当前的运行环境中,如果设置的是环境变量,代码没有任何作用
_ = load_dotenv(find_dotenv())

# 如果需要通过代理端口访问,还需要做如下配置
os.environ["HTTPS_PROXY"] = "http://127.0.0.1:7890"
os.environ["HTTP_PROXY"] = "http://127.0.0.1:7890"

调用 OpenAI API

调用 ChatGPT 需要使用 ChatCompletion API, 该 API 提供了 ChatGPT 系列模型的调用,包括 ChatGPT-3.5、GPT-4 等。

ChatCompletion API 的调用方法如下:

from openai import OpenAI

client = OpenAI(api_key = os.environ.get("OPENAI_API_KEY"))

completion = client.chat.completions.create(
    # 调用模型
    model = "gpt-3.5-turbo",
    # 对话列表
    messages = [
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "Hello!"
        },
    ]
)

调用该 API 会返回一个 ChatCompletion 对象,其中包括了回答文本、创建时间、id 等属性。 我们一般需要的是回答文本,也就是回答对象中的 content 信息。

completion
ChatCompletion(id='chatcmpl-9FAKG4M6HXML257axa12PUuCXbJJz', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='Hello! How can I assist you today?', role='assistant', function_call=None, tool_calls=None))], created=1713401640, model='gpt-3.5-turbo-0125', object='chat.completion', system_fingerprint='fp_c2295e73ad', usage=CompletionUsage(completion_tokens=9, prompt_tokens=19, total_tokens=28))
print(completion.choices[0].message.content)
Hello! How can I assist you today?

ChatCompletion 常用的几个参数:

另外,OpenAI 提供了充分的自定义空间,支持通过自定义 prompt 来提升模型回答效果, 下面是一个简答的封装 OpenAI 接口的函数,支持直接传入 prompt 并获得模型的输出。

from openai import OpenAI

client = OpenAI(api_key = os.environ.get("OPENAI_API_KEY"))

def gen_gpt_messages(prompt):
    """
    构造 GPT 模型请求参数 messages

    Params:
        prompt: 对应的用户提示词
    """
    messages = [
        {
            "role": "user",
            "content": prompt,
        }
    ]

    return messages

def get_completion(prompt, model = "gpt-3.5-turbo", temperature = 0):
    """
    获取 GPT 模型调用结果

    Params:
        prompt: 对应的提示词
        model: 调用的模型,默认为 gpt-3.5-turbo,也可以按需选择 gpt-4 等其他模型
        temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~2。温度系数越低,输出内容越一致。
    """
    response = client.chat.completion.create(
        model = model,
        messages = gen_gpt_messages(prompt),
        temperature = temperature,
    )
    if len(response.choices) > 0:
        return response.choices[0].message.content
    
    return "generate answer error"

get_completion("你好")
'你好!有什么可以帮助你的吗?'

百度文心一言

百度同样提供了 文心一言的 API 接口,其在推出大模型的同时, 也推出了 文心千帆企业级大语言模型服务平台,包括了百度整套大语言模型开发工作链。 对于不具备大模型实际落地能力的中小企业或传统企业,考虑文心千帆是一个可行的选择。

API key 申请

需要注意的是,千帆目前只有 Prompt 模板Yi-34B-ChatFuyu-8B公有云在线调用体验服务 这三个服务是免费调用的,如果想体验其他的模型服务, 需要在计费管理处开通相应模型的付费服务才能体验。

API key 配置

- 将上面获取到的 `API Key`、`Secret Key` 填写至 `.env` 文件的 `QIANFAN_AK` 和 `QIANFAN_SK` 参数。
- 如果使用的是安全认证的参数校验,需要在 **百度智能云控制台-用户账户-安全认证** 页,
  查看 `Access Key`、`Secret Key`,并将获取到的参数相应的填写到 `.env` 文件的 `QIANFAN_ACCESS_KEY`、`QIANFAN_SECRET_KEY`。

加载环境变量

读取 .env 文件,将密钥加载到环境变量

import os
from dotenv import load_dotenv, find_dotenv

# 读取本地/项目的环境变量
# find_dotenv(): 寻找并定位 `.env` 文件的路基那个
# load_dotenv(): 读取 `.env` 文件,并将其中的环境变量加载到当前的运行环境中,如果设置的是环境变量,代码没有任何作用
_ = load_dotenv(find_dotenv())

# 如果需要通过代理端口访问,还需要做如下配置
os.environ["HTTPS_PROXY"] = "http://127.0.0.1:7890"
os.environ["HTTP_PROXY"] = "http://127.0.0.1:7890"

调用文心千帆 API

百度文心同样支持在传入参数的 messages 字段中配置 userassistant 两个成员角色的 prompt, 但与 OpenAI 的 prompt 格式不同的是,模型人设是通过另一个参数 system 字段传入的,而不是在 messages 字段中。

import qianfan

def gen_wenxin_messages(prompt):
    """
    构造文心模型请求参数 message

    Params:
        prompt: 对应的用户提示词
    """
    messages = [{
        "role": "user",
        "content": prompt,
    }]

    return messages


def get_completion(prompt, model = "ERNIE-Bot", temperature = 0.01):
    """
    获取文心模型调用结果

    Params:
        prompt: 对应的提示词
        model: 调用的模型,默认为 ERNIE-Bot,也可以按需选择 Yi-34B-Chat 等其他模型
        temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~1.0,
                        且不能设置为 0。温度系数越低,输出内容越一致。
    """
    chat_comp = qianfan.ChatCompletion()
    message = gen_wenxin_messages(prompt)
    resp = chat_comp.do(
        messages = message,
        model = model,
        temperature = temperature,
        system = "你是一名个人助理"
    )

    return resp["result"]


get_completion(prompt = "你好,介绍以下你自己", model = "Yi-34B-Chat")
get_completion(prompt = "你好,介绍以下你自己")

API 介绍:

ERNIE SDK

API 申请

API Key 配置

点击 访问令牌 获取账户的 access token, 复制 access token 并且以此形式 EB_ACCESS_TOKEN="..." 保存到 .env 文件中

读取 .env 文件,将密钥加载到环境变量

import os
from dotenv import load_dotenv, find_dotenv

# 读取本地/项目的环境变量
# find_dotenv(): 寻找并定位 `.env` 文件的路基那个
# load_dotenv(): 读取 `.env` 文件,并将其中的环境变量加载到当前的运行环境中,如果设置的是环境变量,代码没有任何作用
_ = load_dotenv(find_dotenv())

# 如果需要通过代理端口访问,还需要做如下配置
os.environ["HTTPS_PROXY"] = "http://127.0.0.1:7890"
os.environ["HTTP_PROXY"] = "http://127.0.0.1:7890"

调用 Ernie Bot API

import os
import erniebot

erniebot.api_type = "aistudio"
erniebot.access_token = os.environ.get("EB_ACCESS_TOKEN")

def gen_wenxin_messages(prompt):
    """
    构造文心模型请求参数 messages

    Params:
        prompt: 对应的用户提示词
    """
    messages = [{
        "role": "user",
        "content": prompt
    }]

    return messages


def get_completion(prompt, model = "ernie-3.5", temperature = 0.01):
    """
    获取文心模型调用结果

    Params:
        prompt: 对应的提示词
        model: 调用的模型
        temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0~1.0,
        且不能设置为 0。温度系数越低,输出内容越一致。
    """
    chat_comp = erniebot.ChatCompletion()
    message = gen_wenxin_messages(prompt)
    resp = chat_comp.create(
        messages = message,
        model = model,
        temperature = temperature,
        system = "你是一名个人助理",
    )

    return resp["result"]

讯飞星火

讯飞星火认知大模型,由科大讯飞于 2023 年 5 月推出的中文大模型,也是国内大模型的代表产品之一。

API key 申请

API key 配置

将上面获取的 APPIDAPISecretAPIKey 写入 .env

加载环境变量

读取 .env 文件,将密钥加载到环境变量

import os
from dotenv import load_dotenv, find_dotenv

# 读取本地/项目的环境变量
# find_dotenv(): 寻找并定位 `.env` 文件的路基那个
# load_dotenv(): 读取 `.env` 文件,并将其中的环境变量加载到当前的运行环境中,如果设置的是环境变量,代码没有任何作用
_ = load_dotenv(find_dotenv())

# 如果需要通过代理端口访问,还需要做如下配置
os.environ["HTTPS_PROXY"] = "http://127.0.0.1:7890"
os.environ["HTTP_PROXY"] = "http://127.0.0.1:7890"

模型调用

使用 SDK,封装一个 get_completion() 函数

from sparkai.llm.llm import ChatSparkLLM, ChunkPrintHandler
from sparkai.core.messages import ChatMessage


def gen_spark_params(model):
    """
    构造星火模型请求参数
    """
    spark_url_tpl = "wss://spark-api.xf-yun.com/{}/chat"
    model_params_dict = {
        # v1.5 版本
        "v1.5": {
            "domain": "general", # 用于配置大模型版本
            "spark_url": spark_url_tpl.format("v1.1") # 云端环境的服务地址
        },
        # v2.0 版本
        "v2.0": {
            "domain": "generalv2", # 用于配置大模型版本
            "spark_url": spark_url_tpl.format("v2.1") # 云端环境的服务地址
        },
        # v3.0 版本
        "v3.0": {
            "domain": "generalv3", # 用于配置大模型版本
            "spark_url": spark_url_tpl.format("v3.1") # 云端环境的服务地址
        },
        # v3.5 版本
        "v3.5": {
            "domain": "generalv3.5", # 用于配置大模型版本
            "spark_url": spark_url_tpl.format("v3.5") # 云端环境的服务地址
        }
    }

    return model_params_dict[model]


def gen_spark_messages(prompt):
    """
    构造星火模型请求参数 messages

    Params:
        prompt: 对应的用户提示词
    """
    messages = [
        ChatMessage(role = "user", content = prompt)
    ]

    return messages


def get_completion(prompt, model = "v3.5", temperature = 0.1):
    """
    获取星火模型调用结果

    Params:
        prompt: 对应的提示词
        model: 调用的模型,默认为 v3.5,也可以按需选择 v3.0 等其他模型
        temperature: 模型输出的温度系数,控制输出的随机程度,
            取值范围是 0~1.0,且不能设置为 0。温度系数越低,输出内容越一致。
    """
    spark_llm = ChatSparkLLM(
        spark_api_url = gen_spark_params(model)["spark_url"],
        spark_app_id = os.environ["SPARK_APPID"],
        spark_api_key = os.environ["SPARK_API_KEY"],
        spark_api_secret = os.environ["SPARK_API_SECRET"],
        spark_llm_domain = gen_spark_params(model)["domain"],
        temperature = temperature,
        streaming = False,
    )
    messages = gen_spark_messages(prompt)
    handler = ChunkPrintHandler()
    # 当 streaming设置为 False的时候, callbacks 并不起作用
    resp = spark_llm.generate([messages], callbacks=[handler])

    return resp

get_completion("你好").generations[0][0].text

智谱 GLM

API key 申请

API key 配置

import os
from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())

调用智谱 GLM API

智谱的调用传参和其他类似,也需要传入一个 message 列表,列表中包括 roleprompt。 我们封装如下的 get_completion 函数,供后续使用

from zhipuai import ZhipuAI

client = ZhipuAI(api_key = os.environ["ZHIPUAI_API_KEY"])

def get_glm_params(prompt):
    """
    构造 GLM 模型请求参数 message

    Params:
        prompt: 对应的用户提示词
    """
    message = [
        {
            "role": "user",
            "content": prompt,
        }
    ]

    return message

def get_completion(prompt, model = "glm-4", temperature = 0.95):
    """
    
    """

LLM 接入 LangChain

LangChain 为基于 LLM 开发自定义应用提供了高效的开发框架,便于开发者迅速地激发 LLM 的强大能力, 搭建 LLM 应用。LangChain 也同样支持多种大模型,内置了 OpenAI、LLAMA 等大模型的调用接口。 但是,LangChain 并没有内置所有大模型,它通过允许用户自定义 LLM 类型,来提供强大的可扩展性。

基于 LangChain 调用 ChatGPT

LangChain 提供了对于多数大模型的封装, 基于 LangChain 的接口可以便捷地调用 ChatGPT 并将其集合在以 LangChain 为基础框架搭建的个人应用中。

注:基于 LangChain 接口调用 ChatGPT 同样需要配置个人密钥。

Model

langchain.chat_models 导入 OpenAI 的对话模型 ChatOpenAI。除了 OpenAI 以外, langchain.chat_models 还集成了其他对话模型。

import os
import openai
from dotenv import load_dotenv, find_dotenv
from langchain.openai import ChatOpenAI

# 读取本地的环境变量
_ = load_dotenv(find_dotenv())

# 获取环境变量 OPENAI_API_KEY
openai_api_key = os.environ("OPENAI_API_KEY")

# OpenAI API 密钥在环境变量中设置
llm = ChatOpenAI(temperature = 0.0)
# 手动指定 API 密钥
llm = ChatOpenAI(temperature = 0.0, openai_api_key = "YOUR_API_KEY")

output = llm.invoke("请你自我介绍以下自己!")
output

可以看到,默认调用的是 ChatGPT-3.5 模型。另外,几种常用的超参数设置包括:

Prompt

在开发大模型应用时,大多数情况下不会直接将用户的输入直接传递给 LLM。 通常,他们会将用户输入添加到一个较大的文本中,称为提示模板(Prompt Template), 该文本提供有关当前特定任务的附加上下文。

PromptTemplates 正是帮助解决这个问题,它们捆绑了从用户输入到完全格式化的提示的所有逻辑。 这可以非常简单地开始。例如,生成上述字符串的提示就是。

聊天模型的接口是基于消息(message),而不是原始的文本。 PromptTemplates 也可以用于产生消息列表,在这种样例中, prompt 不仅包含了输入内容信息,也包含了每条 message 的信息(角色、在列表中的位置等)。 通常情况下,一个 ChatPromptTemplate 是一个 ChatMessageTemplate 的列表。 每个 ChatMessageTemplate 包含格式化该聊天消息的说明(其角色以及内容)。

from langchain.prompts.chat import ChatPromptTemplate

template = "你是一个翻译助手,可以帮助我将 {input_language} 翻译成 {output_language}"
human_template = "{text}"
text = "我带着比身体重的行李,\
游入尼罗河底,\
经过几道闪电 看到一堆光圈,\
不确定是不是这里。\"

chat_prompt = ChatPromptTemplate([
     ("system", template),
     ("human", human_template),
])

message = chat_prompt.format_messages(
     input_language = "中文", 
     output_language = "英文", 
     text = text
)
print(message)

output = llm.invoke(message)
print(output)

Output parser

OutputParsers 将语言模型的原始输出转换为可以在下游使用的格式。 OutputParser 有几种主要类型,包括:

最后,我们将模型输出传递给 output_parser,它是一个 BaseOutputParser, 这意味着它接受字符串或 BaseMessage 作为输入。 StrOutputParser 特别简单地将任何输入转换为字符串。

from langchain_core.output_parsers import StrOutputParser

output_parser = StrOutputParser()
output_parser.invoke(output)

从上面结果可以看到,我们通过输出解析器成功将 ChatMessage 类型的输出解析为了字符串。

完整的流程

现在可以将所有这些组合成一条链,该链将获取输入变量,将这些变量传递给提示模板以创建提示, 将提示传递给语言模型,然后通过(可选)输出解析器传递输出。下面使用 LCEL 这种语法去快速实现一条链(chain)。

chain = chat_prompt | llm | output_parser
chain.invoke({
     "input_language": "中文",
     "output_language": "英文",
     "text": text,
})

text = "I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place."
chain.invoke({
     "input_language": "英文", 
     "output_language": "中文",
     "text": text
})

使用 LangChain 调用文心一言

通过 LangChain 框架来调用百度文心大模型,以将文心模型接入到应用框架中。

自定义 LLM 接入 langchain

在 langchain 直接调用文心一言

使用 LangChain 调用讯飞星火

使用 LangChain 调用智谱 GLM

自定义 chatglm

由于 LangChain 中提供的 ChatGLM 已不可用,因此需要自定义一个 LLM。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

import os
from typing import Any, List, Mapping, Optional, Dict
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
from langchain_core.language_models.llms import LLM
from zhipuai import ZhipuAI

# 继承自 langchain.llms.base.LLM
class ZhipuAILLM(LLM):
    # 默认选用 glm-4
    model: str = "glm-4"
    # 温度系数
    temperature: float = 0.1
    # API_Key
    api_key: str = None
    
    def _call(self, prompt : str, stop: Optional[List[str]] = None,
                run_manager: Optional[CallbackManagerForLLMRun] = None,
                **kwargs: Any):
        client = ZhipuAI(
            api_key = self.api_key
        )

        def gen_glm_params(prompt):
            '''
            构造 GLM 模型请求参数 messages

            请求参数:
                prompt: 对应的用户提示词
            '''
            messages = [{"role": "user", "content": prompt}]
            return messages
        
        messages = gen_glm_params(prompt)
        response = client.chat.completions.create(
            model = self.model,
            messages = messages,
            temperature = self.temperature
        )

        if len(response.choices) > 0:
            return response.choices[0].message.content
        return "generate answer error"


    # 首先定义一个返回默认参数的方法
    @property
    def _default_params(self) -> Dict[str, Any]:
        """获取调用API的默认参数。"""
        normal_params = {
            "temperature": self.temperature,
            }
        # print(type(self.model_kwargs))
        return {**normal_params}

    @property
    def _llm_type(self) -> str:
        return "Zhipu"

    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        """Get the identifying parameters."""
        return {**{"model": self.model}, **self._default_params}

自定义 chatglm 接入 LangChain

import os
from zhipuai_llm import ZhipuAILLM
from dotenv import find_dotenv, load_dotenv

# 读取本地/项目的环境变量
_ = load_dotenv(find_dotenv())

# 获取环境变量 API_KEY
api_key = os.environ["ZHIPUAI_API_KEY"]

zhipuai_model = ZhipuAILLM(model = "glm-4", temperature = 0.1, api_key = api_key)  # model="glm-4-0520"
zhipuai_model("你好,请自我介绍以下!")

参考资料