一、LLM 调用的本质
用户 → HTTP请求 → 远端GPU算力 → 生成结果 → 返回
核心认知:调用 LLM 接口本质上是一次 HTTP 请求,把你的输入送到远端服务器,消耗算力,生成结果再返回给你。
高并发、高可用的后端架构要求服务是 无状态(Stateless) 的------这是理解 LLM 工程化的第一块基石。
二、什么是无状态?
2.1 HTTP 天然就是无状态协议
sql
HTTP 协议本身:每个 GET/POST 请求独立,协议层不维护任何上下文
身份识别靠:Header 中的 Cookie / Authorization 传递身份令牌
无状态的核心含义:
| 维度 | 说明 |
|---|---|
| 请求独立性 | 每次请求都是独立的,不依赖之前的请求 |
| 服务端不存状态 | 服务器不需要存储任何客户端会话状态 |
| 水平扩展 | 因为每个请求独立,请求可以被分发到任意一台服务器 |
2.2 类比理解
arduino
有状态 = "你是谁?" → 服务器要查你的档案,压力大
无状态 = 所有人都公平 → 每次来都自报家门,服务器不需要记你是谁
为什么 LLM 服务器必须无状态?因为如果 LLM 服务器要为每个用户维护对话状态,内存和连接压力会巨大,无法做到高并发。
三、LLM 的无状态运行规则
3.1 底层规则
js
规则1:标准 LLM API 在请求层面不保存对话历史
规则2:想让 LLM "懂"你 → 每次请求都把全部对话手动带上
规则3:服务端可以水平扩展 → 你的请求落在任意一台服务器上都一样
3.2 Demo 代码解析
demo/index.mjs 完整展示了这个核心模式:
js
// 第1步:客户端自己维护对话历史
const chatHistory = [
{ role: 'system', content: '你是一个严谨的助手' }
];
// 第2步:每次对话,手动追加到 history
chatHistory.push({ role: 'user', content: '请记住,我的名字是001' });
// 第3步:把完整的 chatHistory 发送给 LLM
const response = await client.chat.completions.create({
model: 'deepseek-v4-flash',
messages: chatHistory // ← 关键:每次带上完整对话历史
});
// 第4步:LLM 的回复也加入 history,供下轮使用
chatHistory.push({
role: 'assistant',
content: response.choices[0].message.content
});
对这个流程的理解:
perl
第1次请求:system + "我叫001" → LLM 回复
第2次请求:system + "我叫001" + 上轮回复 + "我叫什么" → LLM 回复
服务端视角:
- 第1次:收到一堆文字,生成回复,完事儿,忘了
- 第2次:又收到一堆文字(含之前对话),生成回复,完事儿,忘了
- 两次请求之间服务端没记住任何东西
关键认知:LLM 的"记忆"是客户端伪造出来的假象。 是客户端每次都把历史消息重新发给服务端,服务端每次都是"第一次见你"。
四、从 Prompt Engineering 到 Loop Engineering 的演进
markdown
Prompt Engineering → 聊天对话层面,优化 prompt 设计
↓
Context Engineering → 上下文工程,给 LLM 更优质的知识
↓
Loop Engineering → AI 工程化,让 AI 在循环中自主工作
4.1 Prompt Engineering(提示词工程)
- 是什么:设计 prompt 模板、优化指令、少样本学习(few-shot)
- 局限:像"抽卡"------好的 prompt 能提高抽到金卡的概率,但不可控
- 手段 :历史对话、知识库、
CLAUDE.md、AGENTS.md
4.2 Context Engineering(上下文工程)
-
是什么:系统性管理 AI 的上下文质量------找到最相关的信息塞进有限的上下文窗口
-
技术栈:
- RAG(检索增强生成):LLM 不知道的事,从知识库检索后注入上下文
- MCP(Model Context Protocol):标准化的工具/资源接入协议
- Skill:封装好的领域能力模块
4.3 Loop Engineering(循环工程)
- 是什么:让 AI 在自主循环中持续工作------不只是单次问答,而是持续运行的系统
- 代表:Harness(如 Claude Code 的 Agent 循环框架)
- 本质:把 AI 从"一次调用"升级为"持续运行的工程系统"
五、chatHistory 的实际问题与优化
5.1 三个核心问题
arduino
问题1:完整性陷阱
chatHistory 不只是用户的问题,LLM 的回复(assistant 消息)同样是关键上下文
如果不维护完整的双向对话,上下文就会断掉
问题2:Token 膨胀
messages 数组越来越大 → 每次请求消耗的 Token 越来越多 → 费用越来越高
每次请求都在为"回忆历史"买单
问题3:LRU 策略与任务冲突
简单的"最近使用的留下,久远的删除"可能在长对话中出问题
任务还没完成,早期关键信息就被丢弃了
5.2 优化方向
markdown
容量上限(capacity)
↓
旧消息 ←------→ 最近消息(保留)
↓
任务完成度 vs Token 开销 的平衡
| 策略 | 思路 |
|---|---|
| 滑动窗口 | 只保留最近 N 轮对话 |
| 摘要压缩 | 用 LLM 把旧对话压缩成摘要,替代原文 |
| 关键信息提取 | 只保留事实性信息(名字、决策、约束),丢弃闲聊 |
| 分层存储 | 近期对话保留全文,远期对话只保留摘要 |
六、核心概念总结
ini
┌─────────────────────────────────────────────────────┐
│ Stateless 全景图 │
├─────────────────────────────────────────────────────┤
│ │
│ HTTP 无状态协议 │
│ ↓ │
│ LLM API = 无状态 HTTP 服务 │
│ ↓ │
│ 客户端维护 chatHistory[] │
│ ↓ │
│ 每次请求把全部 messages 发给服务端 │
│ ↓ │
│ 服务端处理完就忘 → 可水平扩展 │
│ ↓ │
│ 问题:Token 膨胀 → 需要优化策略 │
│ │
│ 演进方向: │
│ Prompt Engineering → 怎么问 │
│ Context Engineering → 给什么信息 │
│ Loop Engineering → 怎么让 AI 持续工作 │
│ │
└─────────────────────────────────────────────────────┘
总结
LLM 没有记忆,每次对话都是你把整个历史重新发给它。无状态是 LLM 服务端的架构基石,客户端通过手动维护 chatHistory 来给 LLM 制造"它记得"的假象。理解这个,就理解了所有 LLM 应用(ChatGPT、Claude、Agent 框架)的最底层工作方式。