本文是agent开发系列的第一篇,适合刚开始接触大模型开发的同学阅读,既然是agent开发,那就离不开大模型调用,学习大模型调用我认为第一课理解API。

其他相关文章:AI 大模型 Agent 开发入门(二):从让AI从“会回答”到“会做事”-CSDN博客

很多人第一次学习大模型API,通常是从复制一段代码开始的:

from openai import OpenAI

client = OpenAI(
    api_key="your-api-key"
)

response = client.chat.completions.create(
    model="your-model",
    messages=[
        {
            "role": "user",
            "content": "请介绍一下你自己"
        }
    ]
)

print(response.choices[0].message.content)

代码运行后,终端成功打印出模型回答,于是我们似乎已经“学会了调用大模型”。
开发的过程中,我们可能会就会产生一些疑问:

  • API Key 到底是什么,为什么不能直接写进代码?
  • base_url 有什么作用?
  • 为什么请求参数不是一个 prompt,而是一个 messages 数组?
  • 为什么连续对话时,要把之前的聊天记录重新发送?
  • Token 为什么会影响费用和请求长度?
  • 为什么 ChatGPT 可以逐字显示答案,而自己的程序要等待很久才返回?

这些问题表面上互不相关,实际上都发生在同一次 API 请求中。
要真正理解大模型 API,不需要先背大量参数。更好的方式,是沿着一条请求从程序出发,观察它如何到达模型服务器,又如何把答案返回给用户。
一次最基础的大模型调用,可以概括为:理解这条链路后,后续学习参数控制、结构化输出、工具调用和 Agent,都会容易很多。

一、程序是怎样找到并访问模型的

调用模型之前,程序必须先解决两个问题:

我是谁?我要访问哪个服务器?

这会用到两个概念,API Key Base URL

client = OpenAI(
    api_key=os.getenv("MODEL_API_KEY"),
    base_url="https://example.com/v1"
)

API Key 用来证明调用者身份,Base URL 用来确定请求发送到哪里。

1、API Key:程序访问模型服务的身份凭证

大模型 API 通常不是匿名服务。
模型平台收到请求后,需要判断:

  • 请求属于哪个账号;
  • 账号能否使用当前模型;
  • 是否还有可用余额;
  • 是否超过调用频率限制;
  • 本次请求应该记录到谁的账单中。

API Key 就是程序调用模型服务时使用的身份凭证。

from openai import OpenAI

client = OpenAI(
    api_key="your-api-key"
)

从功能上看,它有些类似账号密码。但与普通密码不同,API Key 主要供程序使用这意味着,一旦 API Key 泄露,其他人就可能冒用你的身份调用模型,消耗额度甚至产生费用。因此,不建议把真实 API Key 直接写进代码:

# 不推荐
client = OpenAI(
    api_key="sk-xxxxxxxxxxxxxxxx"
)

#更常见的做法,是通过环境变量读取:
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("MODEL_API_KEY")
)
#Windows PowerShell 可以这样设置
$env:MODEL_API_KEY="your-api-key"

#Linux 或 macOS 可以这样设置:
export MODEL_API_KEY="your-api-key"

这样做有三个明显好处:

  1. 避免密钥随着代码上传到 GitHub;
  2. 开发、测试和生产环境可以使用不同的 Key;
  3. 更换密钥时不需要修改业务代码。

.env 文件虽然比直接写进代码更方便,但它仍然包含真实密钥,在项目上传git时也要加入 .gitignore

.env

2、Base URL:告诉程序模型服务器在哪里

API Key 解决了“我是谁”,Base URL 则解决了“请求发到哪里”。

client = OpenAI(
    api_key=os.getenv("MODEL_API_KEY"),
    base_url="https://example.com/v1"
)

Base URL 可以理解为一组 API 接口共同使用的根地址。

SDK 会在它后面拼接具体接口路径。例如聊天接口最终可能请求:

https://example.com/v1/chat/completions

很多模型平台都提供所谓的“OpenAI 兼容接口”

它的意思通常不是这些平台使用了 OpenAI 的模型,而是它们采用了相似的请求格式。开发者可以继续使用 OpenAI SDK,只修改以下内容:

  • API Key;
  • Base URL;
  • 模型名称。
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("MODEL_API_KEY"),
    base_url="模型平台提供的地址"
)

response = client.chat.completions.create(
    model="模型名称",
    messages=[
        {
            "role": "user",
            "content": "你好"
        }
    ]
)

这也是为什么同一套代码稍作修改,就可能接入 Kimi、DeepSeek、Qwen、OpenRouter 或其他兼容平台。

不过,“OpenAI 兼容”并不等于所有能力完全一致。

不同平台可能在以下方面存在差异:

  • 支持的模型参数不同;
  • 流式返回格式不同;
  • 是否支持图片输入;
  • 是否支持工具调用;
  • 是否支持结构化输出;
  • 错误码和限流规则不同。

因此,兼容接口可以降低接入成本,但正式开发时仍要查看对应平台的官方文档。

3、Messages:模型本次能够看到的对话

找到服务器并完成身份认证后,程序需要把问题发送给模型。

现代聊天模型通常不是只接收一个字符串,而是接收一组消息:

messages = [
    {
        "role": "system",
        "content": "你是一名耐心的 Python 教师。"
    },
    {
        "role": "user",
        "content": "请解释什么是装饰器。"
    }
]

这里的 role 表示消息由谁发出。

最常见的角色包括:

  • system:定义模型的身份、总体规则和回答方式;
  • user:用户当前提出的问题;
  • assistant:模型之前返回的回答。

例如,一个连续对话可能是:

messages = [
    {
        "role": "system",
        "content": "你是一名 Python 教师,回答时尽量使用简单示例。"
    },
    {
        "role": "user",
        "content": "什么是列表推导式?"
    },
    {
        "role": "assistant",
        "content": "列表推导式是一种简洁创建列表的语法。"
    },
    {
        "role": "user",
        "content": "再给我一个带条件判断的例子。"
    }
]

这里有一个非常重要的事实:

大多数聊天 API 本身并不会自动记住你上一轮说了什么。

第二次调用时,之所以模型能理解“再给我一个例子”指的是什么,是因为程序把前面的消息重新发送给了模型。

也就是说,模型的“聊天记忆”通常不是模型平台自动保存的,而是由调用方维护 messages 列表实现的。

你的程序需要负责:

  • 保存历史消息;
  • 将必要历史重新发送;
  • 删除无关历史;
  • 在消息过长时进行摘要或截断。

这一点会直接引出下一个核心概念:Token。

二、模型看到的不是文字,而是 Token

1、什么是token

当请求到达模型服务器后,模型不会直接按照汉字数或单词数处理文本。

在真正进入模型之前,文本会先经过 Tokenizer,也就是分词器,被切分成一系列 Token。

Token 是模型处理文本时使用的基本单位。

例如:

请帮我写一个 Python 函数

这句话并不一定按照每个汉字、每个英文单词分别计算。

因此,下面两种说法都不准确:

1 个汉字一定等于 1 个 Token
1 个英文单词一定等于 1 个 Token

Token 之所以重要,是因为它同时决定三件事:

  • 请求费用;
  • 请求能够容纳的内容长度;
  • 模型处理请求所需的时间。

2、输入 Token 不只是当前问题

一次请求中的输入 Token,通常包括:

System 提示词
+ 历史对话
+ 当前用户问题
+ 工具调用结果
+ 检索到的文档
+ 其他附加上下文

假设用户当前只问了一句话:

请继续。

这句话本身可能很短。

但如果程序同时发送了前面几十轮聊天,那么平台计算的不是“请继续”这三个字,而是整份 messages 中的全部内容。

例如:

系统提示词:1,000 Token
历史对话:18,000 Token
当前问题:20 Token

这次请求的输入量大约是 19,020 Token,而不是 20 Token。

这也是为什么聊天时间越长,后续调用往往越贵、越慢。

3、上下文窗口是一张有限大小的工作台

每个模型都有自己的上下文窗口,例如:

32K
128K
256K
1M

这里的 K 通常表示约一千个 Token。

很多初学者会把上下文窗口理解为:

用户一次最多可以输入多少内容。

但更准确的理解是:

模型在一次请求中能够同时处理的全部 Token 数量。

其中不仅包括用户输入,也通常需要包括模型即将生成的内容。

可以把上下文窗口理解为一张固定大小的工作台。

工作台上可能放着:

  • 系统提示词;
  • 历史对话;
  • 当前问题;
  • 检索资料;
  • 工具执行结果;
  • 模型需要填写的答案。

前面的资料放得越多,留给模型生成答案的空间就越少。

假设某个模型的上下文窗口是 128K Token,当前请求中已经包含:

系统提示词:5K
历史对话:80K
当前输入:20K
工具结果:10K

输入部分已经占用了 115K Token。

理论上剩余空间大约为 13K Token。如果程序仍要求模型生成 20K Token,就可能超过上下文限制。

因此,上下文窗口并不等于单纯的“最大输入长度”。

它更接近:

输入 Token + 输出 Token ≤ 上下文窗口

具体计算规则会因模型和接口而异,但这种理解适用于绝大多数开发场景。

4、为什么上下文不是越长越好

上下文窗口越大,意味着模型能够一次读取更多资料,但不代表应该把所有信息都塞进去。

过多上下文可能带来几个问题:

  • 请求费用增加;
  • 响应速度变慢;
  • 无关信息干扰模型判断;
  • 重要指令被淹没;
  • 更容易触发长度限制。

实际开发中,通常需要主动管理上下文。

常见做法包括:

  • 只保留最近几轮对话;
  • 将较早内容压缩成摘要;
  • 删除和当前问题无关的消息;
  • 通过检索只取回相关文档;
  • 避免重复发送完全相同的大段提示词;
  • 对超长文本进行分块处理。

例如,一个聊天程序不一定要永久保留全部对话:

MAX_HISTORY = 10

recent_messages = messages[-MAX_HISTORY:]

更复杂的做法,是在对话变长后,将旧消息总结为一段较短的摘要:

此前对话摘要:
用户正在开发一个 Unity 项目,已经完成对象池基础实现,
当前希望增加异步资源加载和异常处理。

相比发送几十轮完整消息,摘要可以显著降低 Token 消耗。

不过摘要也会丢失细节,因此不能机械地压缩所有内容。哪些信息必须保留,取决于具体业务。


三、模型生成的答案如何回到程序

1、流式输出

模型开始生成内容后,服务器还需要决定:如何把结果返回给调用方。

最简单的方式,是等模型生成完整答案后,再一次性返回。

response = client.chat.completions.create(
    model="your-model",
    messages=[
        {
            "role": "user",
            "content": "请介绍 Python 的主要特点"
        }
    ]
)

print(response.choices[0].message.content)

这种模式的流程是:

发送请求
→ 等待模型完成全部生成
→ 返回完整答案
→ 程序开始显示

如果答案很短,等待感并不明显。
但当模型需要生成长文章或大量代码时,用户可能等待十几秒,却看不到任何内容,容易误以为程序卡住了。
ChatGPT、Claude、Kimi 等产品能够逐步显示答案,是因为它们通常采用了 Streaming,也就是流式输出。

开启流式输出后,服务器不会等待完整答案生成完毕,而是将已经生成的内容片段持续发送给程序:

模型生成一部分
→ 立即返回一部分
→ 模型继续生成
→ 继续返回

Python 示例通常类似这样:

stream = client.chat.completions.create(
    model="your-model",
    messages=[
        {
            "role": "user",
            "content": "请介绍 Python 的主要特点"
        }
    ],
    stream=True
)

for chunk in stream:
    content = chunk.choices[0].delta.content

    if content:
        print(content, end="", flush=True)

其中:stream=True表示启用流式返回。程序收到的每个 chunk 都只是完整答案的一部分,因此需要不断读取并拼接。
输出过程可能类似:

Python
Python 是
Python 是一种
Python 是一种高级编程语言……

需要注意,Streaming 的核心价值通常不是大幅缩短模型生成完整答案的总时间,而是缩短用户第一次看到内容的时间。
假设模型完整生成答案需要 15 秒:
非流式模式用户可能等 15 秒后才看到全部内容;流式模式用户可能在第 1 秒就看到第一段文字。

因此,流式输出特别适合:

  • AI 聊天页面;
  • AI 写作工具;
  • 代码生成工具;
  • 长文本生成;
  • 需要支持“停止生成”的场景。

2、Streaming 也会增加开发复杂度

流式输出看起来只是增加了一个 stream=True,但在真实项目中,还需要处理很多问题:

  • 多个文本片段如何拼接;
  • 网络中断后如何处理;
  • 用户点击停止时如何终止请求;
  • 已生成内容是否需要实时保存;
  • 工具调用参数如何从多个片段中组合;
  • 前端如何避免频繁刷新造成卡顿;
  • 流式过程中出现错误如何提示用户。

尤其是结构化输出场景。

假设模型需要返回 JSON:

{
  "title": "大模型 API 入门",
  "score": 90
}

在流式过程中,你可能先收到:

{
  "title": "大模型

此时它还不是合法 JSON,程序不能直接解析。

因此,下面这些业务通常更适合等待完整结果:

  • 严格 JSON 输出;
  • 数据抽取;
  • 后台批处理;
  • 自动化任务;
  • 需要完整校验后才能继续执行的流程。

Streaming 不是必须开启的高级功能,而是一种结果交付方式。

判断是否使用它,可以问自己一个问题:

用户是否需要在模型生成完成之前,就看到中间内容?

需要,就使用流式输出;不需要,则一次性返回往往更加简单可靠。


四、把一次 API 请求重新串起来

现在回头看最开始的代码:

from openai import OpenAI

client = OpenAI(
    api_key="your-api-key"
)

response = client.chat.completions.create(
    model="your-model",
    messages=[
        {
            "role": "user",
            "content": "请介绍一下你自己"
        }
    ]
)

print(response.choices[0].message.content)

整个API可以概括为:

后续学习的很多能力,都建立在这条请求链路上:

  • temperature 控制生成结果的随机程度;
  • 输出长度参数限制模型生成规模;
  • Structured Output 约束模型返回固定格式;
  • Function Calling 允许模型请求外部工具;
  • Rate Limit 限制单位时间内的请求和 Token 数;
  • Retry 处理限流、超时和服务异常;
  • RAG 将检索资料加入模型上下文;
  • Agent 在模型、工具和状态之间组织多轮执行。

这些知识并不是相互独立的功能清单,而是不断扩展同一条调用链路。

结语

学习大模型 API 最容易陷入的误区,是一开始就背模型名称、接口参数和 SDK 写法。

这些内容变化很快。

模型会更新,接口会升级,不同厂商的参数也不完全一致。但一次请求背后的核心逻辑相对稳定:

  • 程序需要凭证才能访问服务;
  • 请求需要发送到正确的接口地址;
  • 模型根据本次提供的消息理解上下文;
  • 文本会被转换为 Token;
  • Token 影响费用、速度和上下文容量;
  • 生成结果可以一次性返回,也可以流式返回。

只要理解了这条链路,即使以后更换模型平台、SDK 或编程语言,也只是代码形式发生变化,底层思路并没有改变。

下面这篇文章可以让模型不仅仅是回答问题,还可以让他调用工具

AI 大模型 Agent 开发入门(二):从让AI从“会回答”到“会做事”-CSDN博客


Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐