一、前提

  • 虽然大模型数量众多,但就编程接口这件事来说,基本上都是大差不差的。以编程的术语来说,虽然实现各有差异,但接口基本上是统一的。只要学习了其中一个,相信其它 API 你也能够很快地上手。
  • 既然要学习,我们还是要有一个具体的学习目标,我在这里选择的是 OpenAI API。
  • 之所以选择它,自然是因为 GPT 模型给行业带来的影响,后来者多多少少都会参考它的 API 设计。此外,还有一个很重要的原因,它几乎成了行业的事实标准,现在很多项目选择提供兼容 OpenAI API。有一些中间件性质的项目,不管后台接入的是什么模型,给自己的用户提供的都是 OpenAI 兼容的 API。基于这样的现状,如果只学习一个 API,OpenAI API 是理所当然的选择。

二、OpenAI API

2.1、示例

  • 为了让你有一个直观的感受,我们来看一个具体的例子:

    curl https://api.openai.com/v1/chat/completions-H "Content-Type: application/json"-H "Authorization: Bearer $OPENAI_API_KEY"-d '{        "model": "gpt-4o",        "messages": [            {"role": "user", "content": "写一首关于AI的诗"}        ]    }'
    
  • 在了解具体内容之前,我们先从整体上看一下这个 API。虽然我们的主要工作是面向大模型进行编程,但 Open AI API 其实提供了许多功能,比如:

    Text Generation:生成和处理文本 Embeddings:文本转向量 Speech to Text:语音转文本 Image Generation:生成图像 Vision:处理图像输入 ……

  • 这些不同的接口是通过不同的路径进行区分。这里看到的 /v1/chat/completions 就是我们常用的大模型的编程接口。

  • 除了各种强大的功能,OpenAI API 还有两个关键的参数是各个接口统一的,一个是访问地址,一个是 API Key

  • 之所以访问地址要拿出来单独说,因为在实际使用这个 API 时,我们可能并不是直接访问 OpenAI API,也许是别人提供的兼容的 API,也许是通过代理方式提供的访问,总之,我们使用的地址是不同的地址,需要专门配置。

  • 而 API Key 则比较好理解,不同的 Key 用来标识不同的用户。从上面的例子不难看出,这个 Key 是写在 HTTP 访问的头里。如果我们使用的是 OpenAI 提供的程序库,这两个参数可以通过程序设置,也可以通过命令行设置,下面是使用命令行设置的例子:

    export OPENAI_API_BASE="your_api_base_here"export OPENAI_API_KEY="your_api_key_here"
    
  • 有了这些通用的知识,接下来,我们来具体关注一下最核心的大模型编程接口。

2.1、Chat Completions(聊天补全)

  • 大模型编程接口的路径是 /v1/chat/completions 。其中,v1 是版本号,这个接口起的名字叫聊天补全。补全,这个名字刚好也呼应了我们前面讲的 GPT 的工作原理。其实,OpenAI 最初有个接口就是单纯地补全,只是 ChatGPT 大火之后,聊天的形式成了主流,聊天补全接口成了我们眼中的主力。
  • 新旧两种补全 API 之间最大的差异是,旧 API 传入的是一个提示词,而新 API 传入的是一个消息列表。
2.1.1、请求
  • 我们来看看这个接口的具体内容。作为一个 HTTP 请求,它分为请求和应答两个部分。我们先来看请求。请求的参数非常多,我们会做一个通览。有一点需要提示一下,你学习到这篇内容时,可能与最新的 OpenAI API 文档对不上,因为这个 API 本身也在不断的演化过程中,它会不断地增删参数。

  • 为了方便你理解,我把请求参数划分成四类:

    核心参数 工程参数 工具参数 模型参数

2.1.1.1、核心参数
  • 我们先来看核心参数,这也是你学习大模型编程几乎一定会碰到的核心概念。最核心的两个已经呈现在上面的例子中:

    model,与哪个模型进行沟通,在这个例子里就是 gpt-4o。

    messages,发给模型的消息,这里的消息是一个消息列表,你可以简单地把它理解成一个历史的消息列表,为的就是给模型提供更多的上下文信息。每条消息通常包括两个部分:角色(role)和内容(content)。内容最好理解,就是消息本身的内容。对于常规对话而言,角色主要是系统(system)和用户(user),你可以理解开发者设置的就是系统,用户的问题就是用户。有时,我们还会把大模型生成的消息添加进去,比如,聊天历史,它的角色是助理(assistant)。

  • 除了这两个参数,常用参数还有后面这三个。

    temperature,温度的概念我们前面讲过,主要是用来设定大模型回复的确定性,值越小,表示确定性越强,值越大,表示随机性越强。

    max_completion_tokens,它表示生成应答的最大 token 数。大模型生成内容通常是按照 token 数计费,所以,限制 max_completion_tokens 大小是控制成本的一种好做法。当然,也不能设置得非常大,你可以理解为每个模型内部都有自己最大限制,当你的限制值大于其内部的限制值时,它就会忽略你的设置。这个字段曾经叫 max_tokens,所以,在很多地方,包括参考 OpenAI 设计的一些框架里,你依然可以看到 max_tokens 的影子。

    stream,是否需要流式应答。流式应答主要是为了提升聊天的响应速度,这个部分我们会在下一讲详细地讨论。

  • 请注意,对于不同的模型,这些参数的取值范围可能是不同的,比如,不同模型会有自己的 max_tokens,另外,由于很多服务供应商提供了兼容的 OpenAI API,同样的参数也会有取值范围的差异,比如,在 OpenAI API 里,temperature 的取值范围是从 0 到 2,默认值是 1,而对某些兼容模型而言,其取值范围可能会有所不同,需要根据文档确定。

  • 这里我们梳理了最核心的参数,对于常规聊天,大部分情况下,我们调节的就是这些参数。接下来,我们再来看看其它参数。

2.1.1.2、工程参数
  • 在请求参数中,有一部分是出于工程目的提供的:

    user,终端用户标识,它是我们作为开发者提供给 OpenAI 的,主要就是用作监控和检测 API 的滥用,监控粒度就到了个体上。

    n,为每条输入消息生成多少个回复。虽然看上去可以生成更多内容,但生成内容要计费,所以,如果没有特别需求,就不要额外设置这个参数。

    response_format,应答格式。缺省情况下,这个接口只生成文本内容。但对开发来说,我们经常会用到 JSON 格式。我们当然可以用提示词要求大模型返回,也可以通过设置 response_format 让 API 直接返回 JSON 格式,具体做法可以参考 OpenAI 的结构化输出。

  • 这些参数只是可能会用到的,但真正用到的机会也不多,比如,如果某个用户滥用 API,我们并不会把它放给 OpenAI,而是自己就会处理掉。再比如,虽然 OpenAI 支持 JSON 格式输出,但为了模型的兼容性,我们可能还是会选择使用提示词,让大模型直接返回。

2.1.1.3、工具参数
  • 除了常规的聊天,目前大模型还有一种常见用法,就是构建 Agent。Agent 一个很重要的用法就是扩展大模型的边界,让它能做更多的事情,比如,查询天气、搜索网页等等,这些可以做的事情大模型怎样知道呢?

  • 除了常规的聊天,目前大模型还有一种常见用法,就是构建 Agent。Agent 一个很重要的用法就是扩展大模型的边界,让它能做更多的事情,比如,查询天气、搜索网页等等,这些可以做的事情大模型怎样知道呢?

  • 工具参数里最常用的是这两个:

    tools,模型可以调用的工具列表。其中的每个工具都会包含 type(类型)和 function(函数)两个部分。目前 type 表示工具的类型,目前只有 function 一个类型。function 主要用来告诉模型函数可以怎样调用,包含 description(函数的描述),name(函数名)以及 parameters(函数参数)。

    tool_choice,选择怎样调用工具。参数值为 none 表示不调用工具,auto 表示模型自行选择是生成消息,还是调用工具,required 表示必须调用工具。这个参数值也可以是一个对象,比如,下面这行代码就告诉模型,要调用我指定的这个工具。

    {"type": "function", "function": {"name": "my_function"}}
    
2.1.1.4、模型参数
  • 如果说上面这些参数还是比较好理解的,看参数名就知道是干什么的,还有一大堆参数看上去就比较令人费解了,比如,frequency_penalty。其实,这些参数通常不是给开发应用的工程师用的,你可以理解成它们都是给模型开发者预备的,所以,我把它归入到模型参数的行列里。你可以简单地了解一下,等你哪一天真的要用它,再去深入也不迟。

    seed,种子值。种子值的存在是为了解决可重复输出的问题,也就是说,如果采用相同的种子值以及相同的参数,生成的输出结果应该是一样的。采用开发的视角来看,我们可以把这种行为理解为缓存。

    stop,停止序列。它用来告诉大模型,在生成文本的过程中,如果遇到停止序列,就停止生成。

    frequency_penalty(频率惩罚)和 presence_penalty(存在惩罚)。这两个参数主要是为了减少内容重复的几率,所以,名字里都带有“惩罚”。二者的差别就是,frequency_penalty 表示根据一个 token 在已生成文本中出现的频率计算,presence_penalty 则表示根据一个 token 是否已经出现进行来计算。

    logit_bias,logit 偏差。logit 是统计学中的一个函数。这个参数就是在 logit 函数计算中调整计算结果,主要的目的就是修改某些 token 出现的可能性,比如,我不希望某些词出现在最终的结果里。

    logprobs:是否返回对数概率。前面我们说过,大模型生成每个 token 都是有概率的,如果设置了这个参数,就可以把概率返回,对大模型开发人员来说,方便进行调试。这里的概率采用对数的方式进行表示。

    top_logprobs:返回每个位置最可能返回的 token 数量。如果要调试大模型,除了希望知道概率,有时候,我们还想知道排名靠前的 token 都有哪些。通过设置这个参数,我们就可以让大模型返回这些排名靠前的 token。

    top_p:另一种采样方式,与 temperature 相对。我们前面讲了温度决定了大模型如何选取下一个 token,top_p 是另外一种采用方式,也就是在概率前多少的 token 中进行选择。在实际的使用中,选择 top_p 和 temperature 其中之一就好了。

  • 至此,我们把补全接口的请求参数讲了一遍,你已经对它有了一个初步的认识。
    读者福利:如果大家对大模型感兴趣,这套大模型学习资料一定对你有用

对于0基础小白入门:

如果你是零基础小白,想快速入门大模型是可以考虑的。

一方面是学习时间相对较短,学习内容更全面更集中。
二方面是可以根据这些资料规划好学习计划和方向。

作为一名老互联网人,看着AI越来越火,也总想为大家做点啥。干脆把我这几年整理的AI大模型干货全拿出来了。
包括入门指南、学习路径图、精选书籍、视频课,还有我录的一些实战讲解。全部免费,不搞虚的。
学习从来都是自己的事,我能做的就是帮你把路铺平一点。资料都放在下面了,有需要的直接拿,能用到多少就看你自己了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以点击文章最下方的VX名片免费领取【保真100%】
在这里插入图片描述

👉AI大模型学习路线汇总👈

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!(全套教程文末领取哈)
在这里插入图片描述

👉大模型实战案例👈

光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述👉大模型视频和PDF合集👈

观看零基础学习书籍和视频,看书籍和视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
在这里插入图片描述

640套AI大模型报告合集👈

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

在这里插入图片描述

👉学会后的收获:👈

• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。

👉获取方式:

😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓
在这里插入图片描述
OpenAI API:LLM编程的事实标准(下)
一、前提

  • 在实际工作中,我们还有更重要的部分要去处理,这就是大模型的回答。这一讲,看看 LLM 怎样回答我们的问题。

二、聊天应答

  • 在正常情况下,聊天补全的应答内容本身是比较简单的,就是一个标准的 HTTP 的应答。之所以我们还要把它单独拿出来说一下,主要是它还有一种流式应答的模式。

  • 我们先来看正常的 HTTP 应答,也就是一个请求过去,大模型直接回复一个完整的应答。下面是一个应答的例子:

    {  "id": "chatcmpl-123","object": "chat.completion","created": 1677652288,"model": "gpt-4o-mini","system_fingerprint": "fp_44709d6fcb","choices": [{    "index": 0,    "message": {      "role": "assistant",      "content": "\n\nHello there, how may I assist you today?",    },    "logprobs": null,    "finish_reason": "stop"  }],"usage": {    "prompt_tokens": 9,    "completion_tokens": 12,    "total_tokens": 21  }}
    
  • 我们先来了解几个简单的参数:

    id,应答的唯一标识。这个比较好理解,方便在出问题时定位问题。

    object,对象类型。这是 OpenAI API 应答的一个通用字段,不同类型的应答都会有自己固定的对象类型,在聊天补全接口中,它的值就是 chat.completion。

    created,Unix 时间戳。它表明了这个应答生成的时间。

    model,生成应答的模型。大部分情况下,它就是请求时所带的模型。不过,同一个模型可能存在不同版本的情况,它有时会返回具体的版本,比如:gpt-4o-mini-2024-07-18。

    system_fingerprint,系统指纹。它代表了模型运行时使用的后端配置。在讲到请求中的技术参数时,我们提到过一个 seed 参数,可以当做后端缓存来看。seed 参数就是要与这个 system_fingerprint 配合使用的。

  • 前面几个参数都非常好理解,接下来的 choices 才是我们应答的重点,这是大模型给我们回复的真正内容。choices 本身是一个对象列表,其中的每个对象就是大模型生成文本的一部分。我们来具体看一下:

    index,索引。这就是一个顺序编号,如果文本被切分了,通过索引就可以将内容重新排列,生成正确的顺序。不过,如果对于标准的 HTTP 应答,切片的必要性不大,往往只有一块。

    finish_reason,停止生成 token 的原因。文本不会无限生成,总会停下来。到了停止点或遇到停止序列,原因就是 stop,到了一定的长度,原因就是 length,生成了工具调用就是 tool_calls。

    message,回复的消息。在这个例子中,包含了两个字段:角色(role)和内容(content)。这个部分与请求中的消息是一样的,最核心的字段就是内容,角色部分已经解释过了(可以回看上一讲的核心参数部分)。

  • 除了常规的回复内容之外,如果回复内容是一个工具调用,也是通过 message 里返回的,我们再来看一个例子:

    {  "id": "chatcmpl-abc123","object": "chat.completion","created": 1699896916,"model": "gpt-4o-mini","choices": [    {      "index": 0,      "message": {        "role": "assistant",        "content": null,        "tool_calls": [          {            "id": "call_abc123",            "type": "function",            "function": {              "name": "get_current_weather",              "arguments": "{\n\"location\": \"Boston, MA\"\n}"            }          }        ]      },      "logprobs": null,      "finish_reason": "tool_calls"    }  ],"usage": {    "prompt_tokens": 82,    "completion_tokens": 17,    "total_tokens": 99  }}
    
  • 在这个例子里,我们看到 content 字段是空的,但多了一个 tool_calls,正如其名字所示,它也是一个对象列表,也就是一次可以返回多个工具调用。其中的每个对象都包含了一些字段:

    id,函数调用的 ID。

    type,目前只支持 function,这在前面讲请求的时候也说过了。

    function,函数调用部分,其中包含了名字(name)和参数(arguments),在这个例子里,就表示调用 get_current_weather 这个函数,其参数 location 的值是 Boston, MA。

  • 除了基本的内容,我们前面还提到过,大模型生成文本是根据概率进行计算的,我们上一讲说过,设置 logprobs 就可以让大模型把概率返回给我们,设置 top_logprobs 可以返回概率比较高的几个选项。

  • 下面这个例子里,我们开启了 logprobs,还把 top_logprobs 设置成了 2。

    {  "id": "chatcmpl-123","object": "chat.completion","created": 1702685778,"model": "gpt-4o-mini","choices": [    {      "index": 0,      "message": {        "role": "assistant",        "content": "Hello! How can I assist you today?"      },      "logprobs": {        "content": [          {            "token": "Hello",            "logprob": -0.31725305,            "bytes": [72, 101, 108, 108, 111],            "top_logprobs": [              {                "token": "Hello",                "logprob": -0.31725305,                "bytes": [72, 101, 108, 108, 111]              },              {                "token": "Hi",                "logprob": -1.3190403,                "bytes": [72, 105]              }            ]          },          {            "token": "!",            "logprob": -0.02380986,            "bytes": [              33            ],            "top_logprobs": [              {                "token": "!",                "logprob": -0.02380986,                "bytes": [33]              },              {                "token": " there",                "logprob": -3.787621,                "bytes": [32, 116, 104, 101, 114, 101]              }            ]          },          {            "token": " How",            "logprob": -0.000054669687,            "bytes": [32, 72, 111, 119],            "top_logprobs": [              {                "token": " How",                "logprob": -0.000054669687,                "bytes": [32, 72, 111, 119]              },              {                "token": "<|end|>",                "logprob": -10.953937,                "bytes": null              }            ]          },          {            "token": " can",            "logprob": -0.015801601,            "bytes": [32, 99, 97, 110],            "top_logprobs": [              {                "token": " can",                "logprob": -0.015801601,                "bytes": [32, 99, 97, 110]              },              {                "token": " may",                "logprob": -4.161023,                "bytes": [32, 109, 97, 121]              }            ]          },          {            "token": " I",            "logprob": -3.7697225e-6,            "bytes": [              32,              73            ],            "top_logprobs": [              {                "token": " I",                "logprob": -3.7697225e-6,                "bytes": [32, 73]              },              {                "token": " assist",                "logprob": -13.596657,                "bytes": [32, 97, 115, 115, 105, 115, 116]              }            ]          },          {            "token": " assist",            "logprob": -0.04571125,            "bytes": [32, 97, 115, 115, 105, 115, 116],            "top_logprobs": [              {                "token": " assist",                "logprob": -0.04571125,                "bytes": [32, 97, 115, 115, 105, 115, 116]              },              {                "token": " help",                "logprob": -3.1089056,                "bytes": [32, 104, 101, 108, 112]              }            ]          },          {            "token": " you",            "logprob": -5.4385737e-6,            "bytes": [32, 121, 111, 117],            "top_logprobs": [              {                "token": " you",                "logprob": -5.4385737e-6,                "bytes": [32, 121, 111, 117]              },              {                "token": " today",                "logprob": -12.807695,                "bytes": [32, 116, 111, 100, 97, 121]              }            ]          },          {            "token": " today",            "logprob": -0.0040071653,            "bytes": [32, 116, 111, 100, 97, 121],            "top_logprobs": [              {                "token": " today",                "logprob": -0.0040071653,                "bytes": [32, 116, 111, 100, 97, 121]              },              {                "token": "?",                "logprob": -5.5247097,                "bytes": [63]              }            ]          },          {            "token": "?",            "logprob": -0.0008108172,            "bytes": [63],            "top_logprobs": [              {                "token": "?",                "logprob": -0.0008108172,                "bytes": [63]              },              {                "token": "?\n",                "logprob": -7.184561,                "bytes": [63, 10]              }            ]          }        ]      },      "finish_reason": "stop"    }  ],"usage": {    "prompt_tokens": 9,    "completion_tokens": 9,    "total_tokens": 18  },"system_fingerprint": null}
    
  • 在这个例子里,在 logprobs 的 content 字段里,我们看到了一个个 token 与其对应的概率(logprob)。bytes 表示这个 token 对应的 UTF-8 的字节表现形式,而 top_logprobs 则包含了每个 token 对应的备选 token 及其概率。

三、流式应答

  • 现在你已经对大模型回复的内容有了一个完整的了解。接下来,我们再来看大模型回复的一种重要行为:流式应答。

  • 流式应答的出现主要是为了解决大模型生成文本比较慢的问题。如果等大模型把所有内容生成一次性返回,等待的时间会非常长。对于聊天的场景,这会让本已很长的等待时间会显得更加漫长。

  • 如果想要提高响应速度,可以怎么做呢?之前我们讲过,大模型的核心工作就是一次添加一个 Token。正是因为有了这种工作原理,我们不用等所有内容生成完毕,已经确定生成出来的内容可以先推送给用户。

  • 对于用户而言,看到第一个字出来,单纯的等待就结束了。这样给人的感觉就是,响应速度得到了大幅度提升。所以,在大部分的大模型聊天界面上,我们都会看到一个字一个字往外蹦的效果,要实现这种效果,通常就是采用流式应答的方式。

    在这里插入图片描述

  • 那什么时候该用流式应答,什么时候该用常规的应答呢?流式应答主要是为了提高聊天的响应速度,这就是流式应答的主要工作场景。如果不是这种情况,比如,我们把大模型当做推理引擎,让它觉得下一步该做什么,那就用常规的应答。

  • 接下来的问题就是,流式应答可以采用什么技术实现呢?如果你做过服务端的开发,像这种涉及到服务端主动向客户端推送内容,我们可能会选择 Websocket,但 OpenAI 在这个问题上却选择了 SSE 这种技术。

四、SSE

  • SSE 是服务器发送事件(Server-Sent Event),它是一种服务器推送技术,客户端通过 HTTP 连接接收来自服务器的自动更新,它描述了服务器如何在建立初始客户端连接后向客户端发起数据传输。

  • 简单理解,SSE 就是在 HTTP 连接建立起来之后,由服务器端向客户端推送消息。就像下图所示,常规的应答会在建立起的 HTTP 通道上,一次性地把所有内容都发送给客户端,而 SSE 的方式是在连接建立之后,一块一块地把消息发给用户。对应到大模型上,就是每生成一部分内容就发送一次。

  • 对比很多人熟悉的 Websocket 来看,我们会更清楚一些:

    二者都是建立在 HTTP 协议基础上, Websocket 建立了一套自己的通信协议,而 SSE 则是可以理解为建立在 HTTP 通信协议基础上的一层应用协议。

    Websocket 主要是双向通信(客户端可以发消息给服务端,服务端也可以发消息给客户端),SSE 是单向通信(从服务端到客户端)。

    Websocket 通常用于长连接,SSE 更适合用在单次请求的场景。

  • OpenAI 之所以选择 SSE,而非 WebSocket,是因为 SSE 的技术特点刚好可以契合流式应答的需求:客户端与大模型的交互是一次性的,每产生一个 token,服务端就可以给客户端推送一次,当生成内容结束时,断掉连接,无需考虑客户端的存活情况。

  • 如果采用 WebSocket 的话,服务端就需要维护连接,像 OpenAI 这样的服务体量,维护连接就会造成很大的服务器压力,而且,在生成内容场景下,也没有向服务端进一步发送内容,WebSocket 的双向通信在这里也是多余的。

  • 单就 SSE 这项技术而言,它存在已经很长时间了,2004 年就有人提出,但直到大模型的兴起,这项技术才彻底流行起来。

  • 这里有一点细节的问题,SSE 通常是用在 GET 请求上的,而 OpenAI 的聊天接口是一个 POST 请求,从规范的角度看,它的用法并不完全恰当,只是 OpenAI API 的流行让大家接受了它。如果严格遵守 SSE 程序库处理 OpenAI API ,就可能会遇到无法 POST 请求 SSE 的情况。

  • SSE 貌似是一项高深的技术,但只要我们看一下报文就不难理解它是如何实现的。根据报文的形式,SSE 通常分成纯数据消息和事件消息。纯数据消息,顾名思义就是只有数据的消息,下面是一个例子:

    data: This is the first message.data: This is the second message, itdata: has two lines.data: This is the third message.
    
  • 正如你在这里看到的,每一条消息开头都有一个 data,表示后面的内容就是一条数据。这种形式的数据消息就是流式应答里最常用的消息,每次生成了一个 token,就推送一条以 data 开头的数据块,所以,我们会看到一个又一个的消息块。

  • 事件消息,我们看一个例子也就很容易理解

    event: adddata: 73857293event: removedata: 2153event: adddata: 113411
    
  • 这里的消息会先有一个事件(event),后面跟着具体的数据(data),对于程序员来说,这种做法类似于函数和它的参数。服务端每次推送,都会推送一个事件加上一个数据。

  • 清楚了 SSE 的实现,我们再来看聊天补全接口中流式应答的实现,你就很容易理解了,下面是一个例子:

    {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]}{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{"content":"Hello"},"logprobs":null,"finish_reason":null}]}....{"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-4o-mini", "system_fingerprint": "fp_44709d6fcb", "choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]}
    
  • 这里面我们看到了很多消息块,每块就对应着 SSE 中的一个 data 块。有了前面的基础,消息内容也很好理解了,我这里只把需要注意的一些细节说一下,其它部分含义同之前的解释是一致的。

    id,所有消息块的 ID 是一样的,保证它们是一个消息。

    object,消息类型是 chat.completion.chunk,这说明它是一个消息块。

    消息内容是放在 choices 列表中对象的 delta 字段中,有了前面的基础,你已经知道了,这里面存放的是每次生成的 token。

👉获取方式:

😝有需要的小伙伴,可以点击文章最下方的微信名片添加免费领取【保证100%免费】🆓
在这里插入图片描述

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐