☰
Java后端ai agent学习记录_01
2026/11/4 7:17:47 网站建设 项目流程

1、什么是大模型

大模型是一个部署在云端的、无状态的接口:你POST一个请求过去,它返回一条响应。从java角度理解,就是去调一个服务器上的接口,且其接口文档如下:

请求类型:POST

请求地址(glm为例):https://open.bigmodel.cn/api/paas/v4/chat/completions

请求头:

Authorization: Bearer 你的API KEY

Content-Type: application/json

请求体:

{
"model": "glm-5.3-flash",
"messages": [
{
"role": "system",
"content": "你是一个专业的 Java 技术顾问,回答简洁。"
},
{
"role": "user",
"content": "Spring 的 @Transactional 失效有哪些常见原因?"
}
],
"temperature": 0.3
}

响应体:

{
"choices": [
{
"message": {
"role": "assistant",
"content": "常见原因有:1. 方法不是 public……"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 42,
"completion_tokens": 180,
"total_tokens": 222
}
}

2、基本概念

·prompt:提示词。就是发过去的输入文本,"等于"构造请求参数"。

·role:角色。

system:给模型的"人设和规则",用户看不见。类比:注入的全局配置。

user:用户说的话。

assistant:模型之前说过的话。

tool:工具执行结果。

·无状态:服务端不保存任何对话记录。你以为的"多轮对话",实际上是客户端每次都把完整历史重新发一遍。模型上一轮说了什么,你得自己把它作为assistant消息塞回数组里,它才"记得"。每次对话都要全量传参,这也是为什么有上下文限制的原因,入参不可以过大。

·token:词元。模型不按"字"处理文本,按 token。粗略数量级:英文 1 token ≈ 0.75 个单词;中文 1 个汉字 ≈ 1 个 token 上下(各家分词器不同)。计费按 token,长度限制也按 token。响应里的usage字段就是本次消耗。

·context window:上下文窗口。单次请求能容纳的最大 token 数,输入 + 输出合计。类比:请求体大小上限。现在主流模型128K到1M不等。超出就报错或被截断。

·temperature:温度。控制输出随机性,范围一般 0 到 1(部分到 2)。可以理解为ai工具可选的思考强度。

·finish_reason:结束原因。响应里这个字段告诉你模型为什么停了,stop是正常说完;length是撞到长度上限被截断;tool_calls是它想调工具。

3、Function Calling

大模型只能输出文本,那他怎么实现“帮用户查订单这种功能”?

原理:你在请求里告诉模型"我这里有哪些函数可以用",模型在回复里返回"我想调用某个函数,参数是这些"的结构化JSON,由你的代码真正执行,然后把结果塞回历史数组,再调一次模型。 以查订单为例:
1.用户请求:

{
"model": "glm-5.3-flash",
"messages": [
{
"role": "system",
"content": "你是电商客服助手。"
},
{
"role": "user",
"content": "帮我查一下订单 A1001 到哪了"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "query_order",
"description": "根据订单号查询订单的物流状态。当用户询问订单进度、物流、快递时调用。",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单号,例如 A1001"
}
},
"required": [
"order_id"
]
}
}
}
]
}

2.模型回复“我要调工具”:

{
"choices": [
{
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "query_order",
"arguments": "{\"order_id\": \"A1001\"}"
}
}
]
}
}
]
}

此时finish_reason变成了tool_calls;content是空的,表示模型这轮没说话;arguments是一个JSON 字符串,不是JSON对象,需要自己反序列化,作为工具方法的入参。

3.工具方法执行:

String result = orderService.queryLogistics("A1001");
// 返回 {"status":"运输中","location":"杭州转运中心"}

以java为例,此时调用的方法自己编写,模型不参与编写工具方法,调用mapper层查询数据库获得结果,接下来再喂给模型。

4.把“模型要调用工具方法”和“执行工具方法的结果”两个消息放进请求模型的入参里,再调一次模型:

[
{ "role": "system", "content": "你是电商客服助手。" },
{ "role": "user", "content": "帮我查一下订单 A1001 到哪了" },
{ "role": "assistant", "content": null, "tool_calls": [ …第 2 步那段原样… ] },
{ "role": "tool", "tool_call_id": "call_abc123", "content": "{\"status\":\"运输中\",\"location\":\"杭州转运中心\"}" }
]

注意:模型那条带tool_calls的 assistant 消息必须原样放回去,然后紧跟一条role: tool的消息,tool_call_id要对得上。漏掉 assistant 那条,模型的接口直

接报错。

5.模型给出最终回复:

{ "finish_reason": "stop",
"message": { "role": "assistant", "content": "您的订单 A1001 正在运输中,目前在杭州转运中心,预计明天送达。" } }

以上即是agent 的逻辑中最核心的循环,以java代码来写的话,大概长这样:

List<Message> history = new ArrayList<>(List.of(systemMsg, userMsg)); while (true) { Response resp = llm.chat(history, tools); history.add(resp.message()); // 模型说的话,原样入史 if (!"tool_calls".equals(resp.finishReason())) { return resp.message().content(); // 不调工具了 = 任务完成 } for (ToolCall call : resp.message().toolCalls()) { String result = toolRegistry.execute(call.name(), call.arguments()); // 你的普通 Java 方法 history.add(Message.tool(call.id(), result)); } }

注意:

description即描述,是写给模型看的,它决定了模型会不会、何时、怎么调你的工具。写得含糊,模型就会该调不调、不该调乱调、参数填错。写工具描述的能力 = 写接口文档的能力。

模型的输出是不可信输入。arguments可能是坏 JSON、可能缺字段、可能是模型"编"的订单号。要校验,要当成前端传来的参数一样对待——绝对不能直接拼 SQL,模型可能给出的是如“{"order_id": "A1001' OR '1'='1"}”这样恒成立的sql条件

模型一次可以请求多个工具调用(tool_calls是数组),互不依赖的可以并行执行,然后每个都要回一条tool消息。

循环必须有上限。 模型可能陷入死循环——反复调同一个工具、或者永远不给最终答案。生产代码里一定要有最大轮数、超时、总 token 预算。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询