本文收录于专栏agent智能体系列—— 专栏系统覆盖 AI Agent 的记忆、工具、插件与实战,点击订阅可跟踪后续更新。
本课定位:第 1 课给了判定框架,但要动手还得先补一块地基:Agent 眼中的 LLM 到底长什么样。本课从 Agent 视角重讲四个底层机制——消息结构、chat template、function calling 协议、上下文窗口与 token 账单。Agent 的每一层机制最终都落在对 LLM API 的理解上。本课不讲 transformer 结构,只讲写 Agent 时必须内化的四件事。它们是后面每一课代码的"物理定律",第 6 课的最小 Agent 会逐条用到。
阶段 1|概念与基础 | 来源:【补】(吸收微软课程微· 00 环境准备与微·04 Tool Use 设计模式的思想)
关键字:大模型基础、消息结构、Chat Template、Function Calling、上下文窗口、token计费、OpenAI兼容API、Agent底层机制
本节目录
- 2.1 消息结构:Agent 的一切状态都在 messages 数组里
- 2.2 Chat Template 与 special tokens:同一模型,千种格式
- 2.3 Function Calling / Tool Call:模型输出的是"意图",不是执行
- 2.4 上下文窗口与 token 计费:Agent 的资源约束
概念讲解
2.1 消息结构:Agent 的一切状态都在 messages 数组里
OpenAI 兼容 API(如今是事实行业标准,国内外模型几乎全部兼容)的最小请求是:
{"model":"...","messages":[{"role":"system","content":"你是一个助手"},{"role":"user","content":"你好"},{"role":"assistant","content":"你好!"},{"role":"user","content":"介绍一下你自己"},]}四个角色各司其职:
| 角色 | 谁写的 | Agent 中的用途 |
|---|---|---|
system | 开发者 | 人格、纪律、工具使用规则——模型"宪法" |
user | 最终用户 | 任务目标 |
assistant | 模型(历史) | 模型过去的回答与工具调用决策 |
tool | 运行时 | 工具执行结果回传(对应tool_call_id) |
关键认知:API 是无状态的。你以为的"模型记得上下文",实际是你(客户端)把完整历史messages数组每次重发一遍。这个事实推出 Agent 工程的三条铁律:
- 上下文不是免费的——数组越长,费用和延迟越高(见 2.4 节)。
- 历史是可编辑的——压缩、截断、重写历史都是合法操作,这是第 13 课"上下文工程"的全部基础。
- 谁控制 messages 数组,谁控制 Agent 的记忆——框架做的事无出其右。
2.2 Chat Template 与 special tokens:同一模型,千种格式
API 背后,服务端会把 messages 数组渲染成模型实际吃到的单个 token 序列,渲染规则就是chat template(对话模板)。例如某模型的模板可能是:
<|system|>你是一个助手<|user|>你好<|assistant|>你好!...其中<|system|>、<|user|>这类就是special tokens(特殊标记)——加入词表、被分词器强制切分、对模型有特殊语义的 token。三条工程含义:
- 不要手拼 prompt 绕过 template。直接把"System: xxx"写进 user 消息是常见错误——模型的对齐训练是在 template 格式上做的,格式不对,指令遵循显著退化。
- special token 注入是攻击面。用户输入里若含有
<|im_end|>之类标记,可能截断/伪造对话结构。生产系统要对输入做转义或过滤(第 29 课展开)。 - 开源模型换壳要换 template。本地部署 Qwen/Llama/GLM 系模型时,template 用错是"模型突然变笨"的头号原因;vLLM/Ollama 等推理框架已内置各家模板,走
/v1/chat/completions端点即可自动套用。
2.3 Function Calling / Tool Call:模型输出的是"意图",不是执行
微软第 4 课把 Tool Use 立为独立设计模式,其定义值得原文引用:“Tools are code that can be executed by an agent… tools are designed to be executed by agents in response tomodel-generated function calls”(工具是 Agent 执行的代码,其触发由模型生成的函数调用驱动)——注意 “model-generated”:决策在模型、执行在代码,这正是本节要拆开的机制。2023 年 6 月 OpenAI 引入 function calling,现演化为tools参数。当前 API 层的工作流(OpenAI 兼容口径,五步):
① 定义工具 JSON Schema ──► ② 连同 messages 发给模型 ▲ │ │ ▼ ⑤ 模型给出最终回答 ◄──── ④ 结果以 role:"tool" 消息回传 ▲ ▲ │ │ └── ③ 模型返回 tool_calls(你要自己执行!)关键点逐条:
① 工具定义是 JSON Schema。name+description+parameters,其中 description 是写给模型看的——它就是工具的"prompt"(第 8 课 ACI 的核心议题)。
② 模型返回的是结构化意图:
{"role":"assistant","tool_calls":[{"id":"call_abc123","type":"function","function":{"name":"calculator","arguments":"{\"expr\": \"12*34+5\"}"}}]}注意arguments是JSON 字符串(不是对象),解析时要json.loads。
③ 模型不执行任何东西。它只是说"我想调 calculator,参数是这"。执行永远发生在你的代码里——这就是为什么同一个协议能对接数据库、HTTP API、本地脚本:执行器是你写的。
④ 结果回传要带tool_call_id。模型靠 id 把结果和请求配对。一次返回多个tool_calls(并行调用)时逐个配对回传。
⑤ 停止信号。当模型不再返回tool_calls而返回纯文本,循环结束——这就是 Agent loop 的终止条件(第 6 课)。
最后一条容易被忽略:function calling 不是魔法,是受控输出格式训练。模型被训练成"该调工具时输出这种 JSON 结构",但"何时该调"仍由概率决定——所以参数可能错、可能编造不存在的工具参数值、可能在不需要时硬调。工程上永远要校验(schema 验证 + 参数合法性检查)。
2.4 上下文窗口与 token 计费:Agent 的资源约束
Token 是什么:文本的计量单位。英文约 1 token ≈ 0.75 个单词;中文常见 1 汉字 ≈ 1~2 token(不同分词器差异大)。模型输入输出都按 token 计费。
上下文窗口= 单次请求 messages 的最大 token 数。2026 年主流旗舰模型窗口普遍在 128K~1M token 量级,但注意三个坑:
- 窗口 ≠ 记忆。长上下文中间部分的信息利用率显著低于两端(“lost in the middle” 现象),关键信息要放头尾。
- 计费按输入+输出全算。Agent 每轮循环都重发全部历史——一个 20 轮工具循环,累计输入 token 是 O(N²) 增长的(第 1 轮发 1k,第 20 轮发 20k+,总计远超单轮 20k)。
- 输出上限 ≠ 窗口余量。
max_tokens受剩余窗口约束,长历史会挤压单步可输出长度,工具结果(如整页网页文本)是隐性大户。
粗算一笔账(示意数字):假设每轮工具往返增加 2k token,20 轮循环的累计输入 ≈ 2k×(1+2+…+20) =420k token——是单轮对话的 20 倍以上。这是"Agent 很贵"的数学根源,也是第 13 课(压缩/截断)与第 26 课(成本控制)存在的原因。
案例实战:用裸 API 完成一次最小工具调用
目标:不用任何框架,用标准库urllib走完 2.3 节的五步流程,看清"模型出意图、代码去执行、结果再回传"的完整环。
运行环境(两平台任选其一):
| 环境 | 组成 | 获取方式 |
|---|---|---|
| A. 云端 API(Windows/Linux 通用,推荐入门) | Python 3.8+;任意 OpenAI 兼容 API key | 装 Python 后无需pip install任何包——本例只用标准库;改API_URL/MODEL/API_KEY三行 |
| B. 本地模型(离线可跑) | Ollama 0.34 + glm-4.7-flash(官方 tag,约 19GB)或任意支持 tools 的对话模型 | Ollama 官网下载安装包(Windows/macOS/Linux 均有),ollama pull glm-4.7-flash拉模型 |
国外主流模型速览(先全球视野,国内落地见下表;详细来头/价格/合规路线见第 16 课 16.6 节,2026-10-01 检索口径):
| 厂商 | 当前旗舰(API 模型 ID) | OpenAI 兼容 | 国内可及性 |
|---|---|---|---|
| OpenAI | gpt-6-astra/gpt-6-sol/gpt-6-luna | ✅ 原生(行业模板) | 需代理+境外支付;企业走 Azure OpenAI |
| Anthropic | Claude Opus 5.5 | ❌ 自有 Messages API | 需代理+境外支付;MCP 协议发起方 |
gemini-3.1-pro(1M 上下文) | ❌ 自有 Gemini API | 企业走 Vertex AI | |
| xAI(马斯克) | Grok 4.5 / 4.6 | ✅api.x.ai/v1 | 需代理 |
| Mistral(法国) | Mistral Large 3(675B MoE) | 部分 | ✅开放权重 Apache 2.0,Ollama 自托管无门槛 |
| Meta | Llama 开放系(新一代命名调整中) | — | ✅开放权重,Ollama/HF 国内直用 |
国内读者记两条:① 前四家 API 有网络+支付双重门槛,个人直接开通隐性成本常高于 token 费;② 后两家走开放权重路线,
ollama pull即用、零账号——这是国内体验海外模型的主要路径。
国内模型接入速查(面向国内读者;以下端点/模型名/鉴权方式均于 2026-10-01 逐一实测或验证):
| 提供商 | 端点(API_URL) | 模型名(MODEL) | 鉴权方式 | 本课验证 |
|---|---|---|---|---|
| DeepSeek | https://api.deepseek.com/v1/chat/completions | deepseek-chat | Bearer | ✅ 实测:正确返回 tool_calls |
| 智谱 GLM | https://open.bigmodel.cn/api/paas/v4/chat/completions | glm-4.7-flash | Bearer | ✅ 实测:正确返回 tool_calls |
| MiniMax | https://api.minimaxi.com/v1/text/chatcompletion_v2 | MiniMax-M2.5 | Bearer | ✅ 实测:正确返回 tool_calls |
| 小米 MiMo | 按量付费sk-key:https://api.xiaomimimo.com/v1/chat/completions;Token Plantp-key:https://token-plan-cn.xiaomimimo.com/v1/chat/completions(key 前缀决定端点,打错一律 401) | mimo-v2.5-pro(V2 系列 2026-06-30 已下线) | Bearer(主端点亦接受api-key头) | ✅ 实测:Token Plan 端点正确返回 tool_calls(2026-10-01,详见第 7 课) |
| Kimi | https://api.kimi.com/coding/v1/chat/completions | kimi-k2.7(亦验证kimi-latest) | Bearer | ✅ 实测:正确返回 tool_calls(2026-10-01,sk-kimi- 前缀 key;裸/v1端点 404,必须带/coding段) |
| Qwen(阿里) | https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions | qwen-plus/qwen-max(模型 ID 以百炼控制台当日为准) | Bearer | ◐ 端点规格验证(无 key 探测返回 401 invalid_api_key,说明端点与鉴权口径正确);tool_calls 行为按官方文档口径,本机无 key 未跑通——有 DASHSCOPE_API_KEY 的读者可按第 7 课code/07b_cn_models.py探针一行复测 |
两个真实的坑("OpenAI 兼容"并不完全统一):① MiniMax 官方文档主推的端点是/v1/text/chatcompletion_v2,实测标准路径/v1/chat/completions也能用(2026-10-01 双路径均返回 200)——但文档主推路径才是承诺支持的接口,换模型前以官方文档当日为准;② 小米 MiMo 按 key 前缀分两套端点(tp-打主端点一律 401),且 V2 系列模型名已下线——第 7 课有完整排坑记录。上表就是"换三行跑通"的全部成本。
本课实测环境为 B(Linux + 消费级 GPU,输出见下)。A 路线在 Windows 上零差异(纯 HTTP 调用,无平台相关依赖);B 路线在 Windows 上装好 Ollama 后命令完全一致,第 6 课附 Windows 具体步骤。
# -*- coding: utf-8 -*-# 裸 API 最小工具调用:仅标准库,无框架# 依赖:Python 3.8+;实测环境:Ollama 0.34 + glm-4.7-flash(64K 变体 tag),2026-10-01importjsonimporturllib.request API_URL="http://localhost:11434/v1/chat/completions"API_KEY="YOUR_API_KEY"MODEL="glm-4.7-flash:latest"# 官方 tag;实测用同权重 64K 变体 -100kdefchat(messages,tools=None):body={"model":MODEL,"messages":messages,"temperature":0.2}iftools:body["tools"]=tools req=urllib.request.Request(API_URL,data=json.dumps(body).encode(),headers={"Content-Type":"application/json","Authorization":f"Bearer{API_KEY}"})withurllib.request.urlopen(req,timeout=120)asr:returnjson.load(r)["choices"][0]["message"]tools=[{"type":"function","function":{"name":"calculator","description":"计算四则运算表达式,如 '12*34+5'","parameters":{"type":"object","properties":{"expr":{"type":"string"}},"required":["expr"]}}}]# ①② 发起带工具的请求messages=[{"role":"user","content":"用计算器算 12*34+5"}]msg=chat(messages,tools)# ③ 模型返回意图(注意 arguments 是 JSON 字符串)print("tool_calls:",json.dumps(msg.get("tool_calls"),ensure_ascii=False))tc=msg["tool_calls"][0]args=json.loads(tc["function"]["arguments"])# {"expr": "12*34+5"}# ★ 执行发生在你的代码里——模型只是"想"调用expr=args["expr"]assertall(c.isdigit()orcin"+-*/(). "forcinexpr),"非法字符"result=eval(expr,{"__builtins__":{}},{})# ④ 结果以 role:"tool" 回传messages.append(msg)messages.append({"role":"tool","tool_call_id":tc["id"],"content":str(result)})# ⑤ 模型给出最终回答final=chat(messages,tools)print("最终回答:",final.get("content"))【已实测】(2026-10-01,Ollama 0.34 + glm-4.7-flash-100k〔64K 变体〕,Linux/消费级 GPU)实际输出:
tool_calls: [{"id": "call_crevitxs", "index": 0, "type": "function", "function": {"name": "calculator", "arguments": "{\"expr\": \"12*34+5\"}"}}] 本地执行结果: 413 最终回答: 计算结果是 **413**。 计算过程: - 12 × 34 = 408 - 408 + 5 = 413观察三个细节:模型正确地把意图装进了 JSON(finish_reason: "tool_calls");arguments是字符串需要二次解析;执行后一轮模型就停止调用、转入文本回答——没有循环,只有一次往返。把这次往返套上 while 循环和终止条件,就是第 6 课的 Agent。
预期输出:同上。若你换用其他 OpenAI 兼容服务,注意个别实现要求tool_choice显式传"auto";云端 API 下首个 tool_call 的 id 格式会不同(如call_abc123),不影响逻辑。
小结
- messages 数组是 Agent 的全部状态:无状态 API + 客户端重发历史 = "记忆"的真相。
- chat template 决定模型实际吃什么;绕过 template 手拼 prompt、special token 注入,都是工程雷区。
- function calling 五步环:定义 schema → 发请求 → 收 tool_calls 意图 → 本地执行 → role:“tool” 回传;执行永远在模型之外。
- arguments 是 JSON 字符串;配对靠 tool_call_id;终止信号是"不再返回 tool_calls"。
- Agent 成本按 O(轮数²) 累计输入 token 增长——上下文管理不是优化项,是生存项。
交叉引用:工具定义怎么写才"好用"参见第 8 课《工具设计与 ACI 工程规范》;把五步环升级为完整循环参见第 6 课《从零写一个最小 Agent Loop》。
扩展阅读
🌐 网络提示:openai.com 系需代理(协议层知识已进正文,实测均可用国产端点替代);huggingface.co 走镜像:
export HF_ENDPOINT=https://hf-mirror.com;pip 安装建议加清华镜像-i https://pypi.tuna.tsinghua.edu.cn/simple。详见总纲附录《国内网络访问与国产适配指南》。
- OpenAI Function Calling 官方指南(五步流程与各语言示例):https://developers.openai.com/api/docs/guides/function-calling (2026-10-01 检索;部分网络环境可能 403,可检索"OpenAI function calling guide"替代入口)
- HuggingFace Chat Templates 文档(special tokens 与模板机制):https://huggingface.co/docs/transformers/chat_templating (中国大陆网络建议镜像入口 hf-mirror.com 同路径访问)
- Liu et al.《Lost in the Middle: How Language Models Use Long Contexts》(arXiv:2307.03172)
我的专栏
| 蛋白 / 抗体 / 多肽 / 核酸 | 分子模拟 / 动力学 / 对接 | 药物 / 设计 / 案例 | AI / Agent / 大模型 |
|---|---|---|---|
| 开源蛋白结构预测 | 分子模拟基础 | 小分子药物设计案例 | AI Agent系列 |
| 开源蛋白生成方法实践 | 分子动力学模拟-Amber | 蛋白药物设计案例 | 化学大模型 |
| 开源多肽设计模型 | 分子动力学模拟-Gromacs | 多肽药物设计案例 | 《AI Agent原理与实战》 |
| 开源多肽性质预测 | 分子动力学模拟-OpenMM | 开源小分子生成设计 | CADD中的机器学习模型 |
| DNA/RNA药物设计 | 結合自由能 | 开源药代动力学软件 | 高效计算配置 |
| siRNA药物设计模型 | UCSF DOCK系列 | 我胡师兄说药 | |
| ASO药物设计模型 | rDock系列 LeDock系列 gnina系列 |