LLM API 调用

个人学习记录。上一篇把路线和环境整理完,这一篇先不碰 LangChain、LangGraph,也不急着上 Spring AI。先把一次请求怎么发、结果怎么取、Key 怎么放这些东西记录清楚。

什么是 LLM API

LLM = Large Language Model 大语言模型

API = Application Programming Interface 应用程序编程接口
LLM API 就是让你的程序通过 HTTP 请求去调用大语言模型的接口。
可以先当成一次普通的 HTTP 请求来看。

里面主要有几个东西:

HTTP
JSON
API Key

modelmessagestemperature 这些参数发给模型服务,模型服务再返回一段文本、JSON,或者后面要用到的工具调用结果。

这里先不急着学框架,先把请求体和响应体看清楚。后面用 Spring AI、LangChain、LangGraph 的时候,也能知道框架底下大概做了什么。

这一步在整个路线里的位置

后面大概都会变成这种结构:

用户输入
  -> Java / Python 服务
  -> 组装 messages
  -> 调用 LLM API
  -> 解析响应
  -> 返回给前端或进入下一步流程

普通聊天是这样。

RAG 也是这样,只是中间多了一步检索:

用户问题
  -> 检索知识库
  -> 拼接上下文
  -> 调用 LLM API
  -> 生成答案

Tool Calling 也差不多,只是模型中途可能会判断要不要调用工具:

用户问题
  -> 调用 LLM API
  -> 模型决定调用哪个工具
  -> 后端执行工具
  -> 再把工具结果交给模型
  -> 生成最终回答

这里先把 API 调用当成入口来学。

这一步不是为了写一个多复杂的聊天机器人,而是先知道大模型请求大概长什么样、返回结果怎么取、错误时应该先查哪里。

Chat Completions 是什么

现在很多模型都支持 OpenAI 兼容格式。

常见路径是:

POST /chat/completions

请求体大概长这样:

{
  "model": "模型名称",
  "messages": [
    {
      "role": "system",
      "content": "你是一个严谨的 Java 后端工程师,回答要简洁。"
    },
    {
      "role": "user",
      "content": "什么是 LLM API?用一句话解释。"
    }
  ],
  "temperature": 0.3,
  "max_tokens": 512
}

这里先看 messages

它不是一个普通字符串,而是一组对话消息。

messages 怎么理解

messages 里常见三个角色:

system:给模型定规则
user:用户实际问的问题
assistant:模型之前回复过的内容

system 可以理解成模型的“工作说明”。

比如:

{
  "role": "system",
  "content": "你是一个严谨的 Java 后端工程师,回答要简洁,尽量给代码示例。"
}

user 就是用户输入。

{
  "role": "user",
  "content": "用 Java 写一个调用大模型 API 的例子。"
}

assistant 一般用来放历史回复。

单轮测试可以先不传历史。

可以先按这个方式区分:

规则放 system
问题放 user
历史对话按顺序补 assistant / user

规则尽量放在 system 里,不要全部塞到 user。

API Key 先放到环境变量里

API Key 相当于:

账号身份
调用权限
计费凭证
限流凭证

代码里不直接写 Key:

String apiKey = "sk-xxxx";

学习阶段先用 .env 或环境变量就够。

.env

DASHSCOPE_API_KEY=你的阿里云百炼APIKey

.gitignore

.env
.venv/
target/
__pycache__/
*.log

先用 curl 调一下

学习 API 可以先用 curl

因为 curl 能通,说明 Key、地址、模型名、网络至少大方向没问题。

如果 curl 都不通,先查 Key、地址、模型名和网络,再去看 Java、Python 代码。

通义千问

阿里云百炼的通义千问支持 OpenAI 兼容接口。

这里用 Qwen3.5-Flash,接口参数写:

qwen3.5-flash

国内常见是北京区域:

https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions

国际站文档里经常出现新加坡区域:

https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions

这两个不要混着用。Key 和区域对不上时,可能会出现权限或访问错误。

curl https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" \
  -d '{
    "model": "qwen3.5-flash",
    "messages": [
      {
        "role": "system",
        "content": "你是一个严谨的 Java 后端工程师,回答要简洁。"
      },
      {
        "role": "user",
        "content": "什么是 LLM API?用一句话解释。"
      }
    ]
  }'

结果如下

截屏20260605 01.28.02.png

Python 调用

Python 这里先用 OpenAI SDK,通过阿里云百炼的 OpenAI 兼容接口调用通义千问。

安装依赖:

python -m pip install openai python-dotenv

如果下载慢:

python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
python -m pip install openai python-dotenv

创建 qwen_chat.py

import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

response = client.chat.completions.create(
    model="qwen3.5-flash",
    messages=[
        {
            "role": "system",
            "content": "你是一个严谨的 Java 后端工程师,回答要简洁。"
        },
        {
            "role": "user",
            "content": "什么是 LLM API?用一句话解释。"
        }
    ],
    temperature=0.3,
    max_tokens=512
)

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

运行:

python qwen_chat.py

这里重点看两行:

response.choices[0].message.content
response.usage

image.png
第一行是模型回答。

第二行是 token 使用情况,后面做成本统计会用到。

Java 调用

Java 这边先不用 Spring AI。

这里直接用 JDK 自带的 HttpClient,先看清楚底层 HTTP 请求。

这样做的好处是:

不引入太多框架
请求头、请求体都能看清楚
出错时知道先查哪一层
后面再换 Spring AI 心里有底

通义千问示例

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class QwenChatDemo {

    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("DASHSCOPE_API_KEY");

        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalStateException("请先配置环境变量 DASHSCOPE_API_KEY");
        }

        String body = """
                {
                  "model": "qwen3.5-flash",
                  "messages": [
                    {
                      "role": "system",
                      "content": "你是一个严谨的 Java 后端工程师,回答要简洁。"
                    },
                    {
                      "role": "user",
                      "content": "什么是 LLM API?用一句话解释。"
                    }
                  ],
                  "temperature": 0.3,
                  "max_tokens": 512
                }
                """;

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions"))
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(
                request,
                HttpResponse.BodyHandlers.ofString()
        );

        System.out.println(response.statusCode());
        System.out.println(response.body());
    }
}

先打印完整 JSON,不急着解析。

原因很简单:

先知道原始响应长什么样,再封装对象。否则对象字段写错了,还以为是模型没返回。

常见参数

model:用哪个模型。

学习阶段先不选最贵、最复杂的模型。先能跑通,再慢慢比较效果。

messages:对话上下文。

后面做多轮对话、RAG、Agent,很多时候都是在维护和加工这组消息。

temperature:随机性。

可以先按这个范围理解:

写代码:0.1 - 0.3
总结文档:0.3 - 0.5
创意写作:0.7 - 1.0

max_tokens:限制输出长度。

先给输出加个限制,避免 token 用得太随意。

stream:是否流式输出。

聊天框逐字显示会用到,但这篇先不展开。先把非流式跑通。

返回结果怎么看

CompletionUsage(completion_tokens=1131, prompt_tokens=36, total_tokens=1167, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=1091, rejected_prediction_tokens=None, text_tokens=1131), prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=None, text_tokens=36))

典型响应里重点看这些:

choices[0].message.content:模型回复的内容
usage.prompt_tokens:输入用了多少 token
usage.completion_tokens:输出用了多少 token
usage.total_tokens:总 token

usage 后面会用到。

现在看起来只是一个数字,后面做平台时可能会变成:

成本统计
用户额度
接口限流
模型效果对比
调用链日志

这也是 Java 后端能发挥价值的地方。

模型调用不是只要“能问能答”就结束了,企业里还要知道谁调了、调了多少、花了多少钱、失败了几次。

简单封装

这一步先不在每个文件里复制 HTTP 代码。

可以先有一个最小封装。

Python:

class LlmClient:
    def __init__(self, client, model: str):
        self.client = client
        self.model = model

    def chat(self, user_input: str) -> str:
        response = self.client.chat.completions.create(
            model=self.model,
            messages=[
                {
                    "role": "system",
                    "content": "你是一个严谨的 Java 后端工程师,回答要简洁。"
                },
                {
                    "role": "user",
                    "content": user_input
                }
            ],
            temperature=0.3
        )
        return response.choices[0].message.content

Java 后面可以这样想:

LlmClient
  ├── chat(String prompt)
  ├── chat(List<Message> messages)
  ├── parseResponse(String responseBody)
  └── recordUsage(...)

接 Spring Boot 以后,再变成:

@Service
public class LlmService {

    public String chat(String message) {
        // 这里封装模型调用
        return "";
    }
}

这时候就能自然接上:

Controller
Service
日志
限流
数据库
用户权限

常见问题

401

先查 API Key。

Key 是否复制完整
Authorization 是否写成 Bearer xxx
环境变量是否真的生效
账户是否欠费或没有权限

404

先查接口地址。

base_url 是否正确
/chat/completions 路径是否正确
通义千问的区域是否选对

model not found

先查模型名。

模型名会变,以控制台和官方文档为准。

请求超时

大模型响应慢是正常的。

工程里不能无限等,后面要加:

连接超时
读取超时
失败重试
熔断降级

这部分先记着,后面做 Spring Boot 服务时再补。

练习清单

这一篇我只要求自己完成几件事:

阿里云百炼 API Key 已准备
.env 已创建
.gitignore 已排除 .env
curl 能调通
Python 能调通
Java HttpClient 能调通
能看懂 messages
能从 choices[0].message.content 取出回复
能看懂 usage token
知道 temperature / max_tokens / stream 的作用

建议目录:

ai-agent-study
├── java
│   └── llm-api-demo
│       └── QwenChatDemo.java
├── python
│   └── llm-api-demo
│       ├── qwen_chat.py
│       ├── llm_client.py
│       └── .env
└── docs
    └── llm-api.md

小结

这一篇先记住这句话:

LLM API 可以先按 HTTP + JSON + API Key 来理解。后面更需要慢慢补的,应该是怎么把它稳定、安全、可观测地接进业务系统。

对 Java 后端来说,这一步很适合从工程视角去理解。

先不急着堆框架,先做到:

请求能发出去
响应能看懂
错误能定位
Key 不泄露
token 能统计

这些先打牢,后面学 Spring AI、RAG、Tool Calling、Agent 工作流,心里会更有底一点。

参考资料


这个家伙很懒,啥也没有留下😋