Codex 一次对话到底会给模型发送什么?以 Spring Boot 项目为例讲清 Context、代码读取与 Token 消耗
最近在研究 Codex、AI Agent、Harness、Context、Token 这些概念时,我一直有一个疑问:
我在 Codex 里只输入一句话,比如“开发一个意见反馈模块”,那 Codex 真正给模型发送的到底是什么?会不会把整个 Spring Boot 项目的前后端代码全部发给模型?平时 Token 又是怎么消耗的?
如果把这个问题搞明白,基本就能把 Codex 的 Prompt、AGENTS.md、Context、Tool、Agent Loop、Token 这些概念串起来。
下面就用一个真实的 Spring Boot + 前端项目来讲。
一、先假设有这样一个项目
例如现在有一个项目:
mall-system/
│
├── AGENTS.md
│
├── backend/
│ ├── pom.xml
│ └── src/main/
│ ├── java/com/demo/
│ │ ├── controller/
│ │ │ ├── UserController.java
│ │ │ └── NoticeController.java
│ │ ├── service/
│ │ │ ├── UserService.java
│ │ │ └── NoticeService.java
│ │ ├── mapper/
│ │ ├── entity/
│ │ └── dto/
│ └── resources/
│ ├── application.yml
│ └── mapper/
│
└── frontend/
├── package.json
└── src/
├── views/
├── api/
└── components/
现在打开 Codex,在输入框里说:
增加一个“意见反馈”功能。
用户可以提交反馈;
管理员可以查看反馈列表;
管理员可以把反馈标记为已处理;
同时完成前端页面和后端接口。
表面上看,我只输入了这么几句话。
但 Codex 第一次真正调用模型时,给模型准备的内容会多得多。
二、第一次调用模型,到底会发什么?
可以把它理解成一个“大包裹”:
┌───────────────────────────────────────┐
│ 第一次给模型的内容 │
│ │
│ ① Codex 自己的基础 Instructions │
│ │
│ ② 当前 Sandbox / 权限说明 │
│ │
│ ③ Developer Instructions │
│ (如果配置了) │
│ │
│ ④ AGENTS.md │
│ │
│ ⑤ Skills 的说明 │
│ (如果配置了) │
│ │
│ ⑥ 当前环境信息 │
│ cwd、shell 等 │
│ │
│ ⑦ Tools 的定义 │
│ │
│ ⑧ 这个会话之前的聊天历史 │
│ (新会话基本没有) │
│ │
│ ⑨ 你当前输入的 Prompt │
│ │
│ “增加一个意见反馈模块……” │
└───────────────────────────────────────┘
↓
模型
也就是说:
Codex 每次给模型的不只是你输入框里的那一句话。
下面把每一部分分别讲清楚。
三、Codex 自己的基础 Instructions 是什么?
这个不是我们自己写的,而是 Codex 自带的。
可以把它理解成:
Codex 上岗前的“员工培训手册”。
概念上类似:
【Codex 内置 Instructions】
你是一个软件开发 Agent。
你的任务是帮助用户完成软件开发工作。
你可以使用提供给你的工具读取文件、
修改文件、执行命令……
在完成任务过程中应检查结果,
必要时使用工具验证修改……
……
注意:
上面的内容只是为了方便理解,不代表 Codex 内部真实提示词逐字就是这样。
我们只需要知道:
Codex 在真正把用户问题交给模型之前,会先告诉模型“你是谁、你应该怎么工作”。
这部分属于 Codex 自身 Harness 提供给模型的基础 Instructions。
四、当前 Sandbox / 权限说明是什么?
这个概念刚开始最容易看不懂。
其实它就是告诉模型:
“你现在有哪些权限,能操作到哪里。”
例如可以简单理解成:
【权限说明】
当前使用 Workspace Sandbox。
允许读取当前项目文件。
允许修改:
/Users/zmc/code/mall-system
不能随意修改项目目录之外的文件。
当前网络访问:
允许 / 受限制
某些 Shell 命令可以直接执行。
某些命令需要用户确认以后才能执行。
它不是在告诉 AI:
“意见反馈模块应该怎么开发。”
而是在告诉 AI:
“你干活时,这双手能伸到哪里。”
可以把它类比成公司的门禁卡权限。
例如:
普通办公区
→ 可以进入
服务器机房
→ 需要审批
某些敏感区域
→ 禁止进入
所以 Sandbox 和 Permission 主要解决的是:
Agent 实际拥有多大的操作权限。
五、AGENTS.md 会发什么?
假设项目根目录有:
mall-system/AGENTS.md
里面写:
# 项目开发要求
这是一个 Spring Boot + Vue 项目。
后端新增接口保持现有:
Controller → Service → Mapper 分层。
优先参考已有模块的代码风格。
不要随意修改现有接口。
数据库字段使用下划线命名。
前端优先复用现有组件。
后端修改完成运行:
mvn test
前端修改完成运行:
npm run build
这些内容会作为项目 Instructions 加入模型上下文。
所以第一次模型已经知道:
用户让我开发:
“意见反馈模块”
同时这个项目告诉我:
“这是 Spring Boot + Vue”
“后端遵循 Controller → Service → Mapper”
“不要乱改现有接口”
“修改完成以后要测试”
所以可以这样理解:
Prompt
=
这一次具体要干什么
AGENTS.md
=
这个项目平时应该怎么干
六、当前环境信息是什么?
这个其实非常简单。
例如:
【当前环境】
当前工作目录:
/Users/zmc/code/mall-system
Shell:
zsh
这里的:
cwd
就是:
Current Working Directory
中文就是:
当前工作目录。
相当于告诉 AI:
“你现在站在
mall-system这个项目目录里。”
而:
zsh
表示当前 Mac 使用的 Shell。
这部分通常不会很长,但它对 Agent 很重要。
因为 Agent 至少要知道:
自己现在在哪个目录下面干活。
七、Tools 是什么?
Codex 还必须告诉模型:
“你现在有哪些工具可以用。”
否则模型并不知道自己能读取文件、修改代码或者执行终端命令。
例如可以简单理解成:
【可用工具】
Tool:Shell
作用:
执行终端命令。
Tool:文件读取
作用:
读取项目文件。
Tool:文件修改
作用:
修改项目文件。
Tool:搜索
作用:
搜索项目文件和代码。
Tool:MCP
作用:
调用外部系统能力。
……
这里有一个很重要的区别:
给模型的是 Tool 的“说明书”,不是把 Tool 程序本身发给模型。
例如模型知道:
我有 Shell 工具。
以后它才会判断:
我要执行:
mvn test
所以:
模型
负责判断“我要不要用工具”
Harness
负责真正提供和执行工具
八、最关键的问题:项目代码第一次会全部发给模型吗?
答案是:
不会。
假设这个项目有:
1000 个文件
30 万行代码
Codex 一般不会在第一轮直接变成:
Codex 系统提示词
+
AGENTS.md
+
30 万行项目代码
+
你的 Prompt
如果真的这样做,会非常浪费 Context 和 Token。
第一次更接近:
Codex 基础 Instructions
+
权限说明
+
AGENTS.md
+
环境
+
Tools
+
历史聊天
+
你的 Prompt
然后模型自己判断:
“为了完成这个任务,我接下来应该看哪些代码?”
这也是 Agent 和普通聊天模型一个非常大的区别。
九、第一次模型可能先决定:看看项目结构
例如模型判断:
我要开发一个意见反馈模块,先看看这个项目现在有哪些模块。
于是调用工具去:
查看目录
搜索 Controller
搜索 Service
搜索 Mapper
查看 pom.xml
查看 package.json
Codex Harness 真正去本地硬盘执行这些操作。
假设返回:
backend/pom.xml
backend/src/main/java/com/demo/controller/UserController.java
backend/src/main/java/com/demo/controller/NoticeController.java
backend/src/main/java/com/demo/service/UserService.java
backend/src/main/java/com/demo/service/NoticeService.java
frontend/package.json
frontend/src/views/notice/NoticeList.vue
frontend/src/api/notice.js
这个时候:
模型才知道这些文件存在。
也就是说:
一个文件躺在你的电脑硬盘里,不代表模型自动看到了它。
Codex 一般需要通过 Tool 去:
搜索
查看
读取
以后,相应内容才会进入后续 Context。
十、第二次调用模型会多出什么?
第一次模型说:
我要先看看项目结构。
Tool 返回项目目录。
接下来 Codex 再次调用模型。
第二次可以简单理解成:
原来的上下文
+
刚才模型调用工具的记录
+
工具返回的项目目录和搜索结果
例如模型看到:
【用户需求】
开发意见反馈模块。
【搜索结果】
NoticeController.java
NoticeService.java
NoticeMapper.java
NoticeList.vue
notice.js
……
模型发现:
Notice 模块和我要开发的 Feedback 模块很像。
于是继续调用 Tool:
读取 NoticeController.java
读取 NoticeService.java
读取 NoticeMapper.java
读取 NoticeList.vue
读取 notice.js
十一、第三次模型才真正看到 Java / Vue 代码
假设工具读取:
@RestController
@RequestMapping("/notice")
public class NoticeController {
@Autowired
private NoticeService noticeService;
@GetMapping("/list")
public Result list(...) {
...
}
}
以及:
@Service
public class NoticeService {
...
}
再加上:
@Mapper
public interface NoticeMapper {
...
}
这些真实代码现在才进入模型 Context。
模型第三次看到的内容就会变成:
Codex 基础 Instructions
+
权限说明
+
AGENTS.md
+
环境
+
Tools
+
用户需求
+
之前的搜索结果
+
NoticeController.java 内容
+
NoticeService.java 内容
+
NoticeMapper.java 内容
这个时候模型才真正知道:
“原来这个项目后端代码是这样写的。”
所以:
项目代码不是一开始全部塞进去,而是在 Agent 工作过程中按需读取。
十二、前端代码会不会全部发?
还是:
不会。
因为当前需求明确要求:
同时完成前端和后端。
所以模型可能继续搜索:
frontend/src/views
frontend/src/api
frontend/src/router
frontend/src/components
然后挑一些真正相关的文件读取,例如:
NoticeList.vue
notice.js
router/index.js
Table.vue
这些被读取的文件内容,会进入后面的 Context。
但:
frontend 下面另外 500 个完全无关的 Vue 文件
不会因为它们存在,就自动全部发给模型。
所以要记住:
项目总代码量
≠
模型真正看到的代码量
真正影响 Context 的是:
这次任务过程中,Codex 到底搜索并读取了多少内容。
十三、如果我只让 Codex 改后端呢?
例如 Prompt 写:
只开发“意见反馈”后端接口,
不要修改前端。
那么 Codex 大概率主要围绕:
backend/
去查看:
pom.xml
Controller
Service
Mapper
Entity
DTO
SQL
配置
它没有理由大量读取:
frontend/
所以 Prompt 写得越明确,例如:
只改后端
只改 feedback 模块
参考 notice 模块
不要扫描无关业务模块
越有利于 Codex 收敛搜索范围。
这不只是让 Agent 更听话。
实际上也有利于减少:
无关代码读取
无关 Context
无意义 Token 消耗
十四、一个真实的 Codex 开发任务可能这样运行
整个过程可以简化成:
你:
开发意见反馈模块,
完成前后端。
↓
════════════════════
模型调用 ①
════════════════════
模型看到:
Codex Instructions
权限说明
AGENTS.md
环境
Tools
你的 Prompt
↓
模型判断:
“先看看项目结构”
↓
Codex 执行搜索
════════════════════
模型调用 ②
════════════════════
模型又看到:
前面的内容
+
目录和搜索结果
↓
模型判断:
“Notice 模块比较像,
读取它作为参考”
↓
Codex 读取:
NoticeController.java
NoticeService.java
NoticeMapper.java
NoticeList.vue
notice.js
════════════════════
模型调用 ③
════════════════════
模型看到:
前面内容
+
这些真实代码
↓
模型理解项目写法
↓
继续读取:
Entity
DTO
Router
数据库脚本
════════════════════
模型调用 ④
════════════════════
模型看到:
前面内容
+
更多相关代码
↓
开始创建:
FeedbackController
FeedbackService
FeedbackMapper
FeedbackEntity
FeedbackList.vue
feedback.js
════════════════════
模型调用 ⑤
════════════════════
模型判断:
“修改完成,需要测试”
↓
执行:
mvn test
npm run build
════════════════════
模型调用 ⑥
════════════════════
模型看到:
mvn test 输出
npm run build 输出
如果 ERROR:
分析错误
修改代码
重新测试
════════════════════
模型调用 ⑦
════════════════════
测试通过
↓
告诉你:
“意见反馈模块开发完成……”
所以:
你只发了一次消息,并不代表模型只被调用一次。
一次 Agent 任务内部,可能发生很多次:
模型推理
↓
Tool 调用
↓
Tool 返回
↓
再次模型推理
这就是:
Agent Loop。
十五、第二次继续聊天时,会发什么?
假设第一轮已经开发完成。
你没有新开会话,而是在同一个 Codex 对话里继续说:
列表再增加一个“反馈类型”筛选条件。
那么模型通常不会重新失忆开始。
可以简单理解成,新一轮会继续带上:
Codex 基础 Instructions
+
AGENTS.md
+
环境
+
Tools
+
上一轮 Conversation
+
之前相关 Tool Calls / Tool Results
+
你刚刚输入的新 Prompt
这也是为什么:
同一个 Codex 会话越聊越久,Context 往往会越来越大。
因为前面做过的事情:
用户说了什么
模型做了什么
读取过哪些代码
Tool 返回了什么
执行命令有什么结果
都会对后续任务产生影响。
十六、Context 会一直无限增长吗?
不会无限增长。
假设一个会话已经聊了很多轮:
大量 Prompt
+
大量 Java 代码
+
大量 Vue 代码
+
大量 Shell 输出
+
大量错误日志
当上下文越来越大时,Codex 需要进行:
Compaction(上下文压缩)。
可以把它理解成:
以前几十页甚至上百页的工作记录
↓
压缩成更短的总结
↓
例如:
“目前已完成意见反馈模块;
后端包含 Controller、Service、Mapper;
前端包含列表和新增页面;
当前剩余问题是筛选条件。”
然后继续后面的工作。
所以 Context 不会永远只增不减。
十七、Token 到底消耗在哪里?
现在再说 Token 就非常容易理解了。
假设第一次模型调用,使用一组完全为了方便理解的假数字:
| 内容 | 假设 Token |
|---|---|
| Codex 基础 Instructions | 8,000 |
| 权限 / 环境说明 | 1,000 |
| Tool 定义 | 5,000 |
| AGENTS.md | 1,000 |
| 你的 Prompt | 200 |
| 第一次输入 | 约 15,200 |
这里有一个非常重要的地方:
这时候还没有把整个 Spring Boot 项目算进去。
然后 Codex 搜索并读取了一些代码。
第二轮又增加:
| 新增加内容 | 假设 Token |
|---|---|
| 目录和搜索结果 | 1,000 |
| NoticeController | 1,500 |
| NoticeService | 2,000 |
| NoticeMapper | 1,000 |
| 增加 | 约 5,500 |
再读取前端:
| 新增加内容 | 假设 Token |
|---|---|
| NoticeList.vue | 3,000 |
| notice.js | 800 |
| router | 1,000 |
| 增加 | 约 4,800 |
然后执行:
mvn test
如果返回几百行错误日志,又会继续增加 Context。
所以:
你输入的 Prompt 可能只有几十个字,但整个 Agent 任务累计处理的 Token 可能达到几万、几十万,甚至更多。
十八、真正消耗 Token 的大头通常是什么?
对于项目开发场景,Token 消耗的大头通常不是那句中文 Prompt。
更常见的是:
| 内容 | 对 Token 的影响 |
|---|---|
| Codex 内置 Instructions / Tool 定义 | 每轮都有基础开销 |
| 很长的 AGENTS.md | 持续占 Context |
| 一次读取大量 Java / Vue 文件 | 很明显 |
| 大段 SQL / XML | 很明显 |
mvn test 超长错误日志 |
很明显 |
| 同一个会话聊几十轮 | 很明显 |
| MCP 工具很多 | Tool 描述增加 |
| 普通几十字中文 Prompt | 通常相对较少 |
所以:
Prompt 很短,不代表任务 Token 很少。
真正影响最大的,是 Agent 为了完成任务:
看了多少代码
读取了多少文件
产生了多少 Tool 结果
经历了多少轮模型推理
保留了多少历史上下文
十九、Cached Input Tokens 是什么?
以后可能会看到:
Input Tokens
Cached Input Tokens
Output Tokens
因为:
Codex 基础 Instructions
AGENTS.md
Tools 定义
前面的部分 Context
可能连续很多轮都基本一样。
这些重复内容可能命中缓存。
例如:
Input tokens: 50,000
Cached input tokens: 35,000
Output tokens: 3,000
可以简单理解成:
Input Tokens
=
这一轮模型总共需要处理的输入
Cached Input Tokens
=
其中有多少属于之前处理过、
这次可以利用缓存的内容
所以 Input 很大,并不意味着里面所有内容每次都完全按“全新内容”处理。
二十、怎么查看平时 Token 消耗?
如果使用 Codex CLI,可以在会话中尝试:
/status
它通常可以帮助查看当前会话的一些状态信息。
不同 Codex 版本实际展示字段可能有所不同,例如可能会看到:
Model
Directory
Permissions
Agents.md
Token usage
Context window
其中最值得关注两个概念:
Token Usage
Context Window
它们不是一回事。
二十一、Token Usage 和 Context Window 有什么区别?
假设看到:
Token usage:
800K total
Context window:
80K / 272K
不要觉得矛盾。
Token Usage
表示:
这个会话从开始到现在,模型累计处理过多少 Token。
因为一次用户问题可能触发:
模型调用 1
模型调用 2
模型调用 3
……
模型调用 20
所以累计 Token 完全可能超过模型 Context Window。
Context Window
表示:
当前这一刻,模型脑子里最多能同时放多少内容。
可以这样类比:
Context Window
=
你的办公桌一次最多能放多少资料
Token Usage
=
你从早到晚累计翻过多少页资料
例如:
办公桌一次只能放 500 页
但一天完全可以累计翻:
5000 页
两者并不冲突。
二十二、套餐 Usage 又是什么?
如果是 ChatGPT Plus / Pro 等方式使用 Codex,还可能看到:
5-hour usage
Weekly usage
Credits
这个和:
Token Usage
以及:
Context Window
又不是同一个概念。
可以简单分成:
| 指标 | 大白话 |
|---|---|
| Token Usage | 当前会话累计让模型处理了多少内容 |
| Context Window | 模型当前脑子里同时装了多少内容 |
| 5h / Weekly Usage | Codex 套餐额度还剩多少 |
套餐额度不能简单理解成:
1 万 Token = 扣 1%
因为实际使用还会受到:
模型
任务复杂度
Context
推理强度
Tool 调用
任务持续时间
等因素影响。
二十三、自己怎么做一个最直观的 Token 实验?
可以拿一个真实 Spring Boot 项目测试。
第一步:新开一个全新会话
输入:
只告诉我这个项目使用什么技术栈。
不要修改任何文件。
尽量不要读取无关文件。
然后查看:
/status
记录 Token。
第二步:让 Codex 分析一个模块
输入:
详细分析用户管理模块的:
Controller
Service
Mapper
Entity
这时候 Codex 会读取更多代码。
再查看:
/status
Token 通常会明显增加。
第三步:让它分析整个项目
输入:
分析整个项目所有业务模块。
再查看:
/status
通常会看到:
分析整个项目
>>
分析单个模块
>>
只看技术栈
通过这个实验,很容易真正理解:
读取的内容越多,模型处理的上下文越多,Token 也通常越多。
二十四、整个 Spring Boot 功能开发过程可以这样理解
最后把整个过程压缩成一张图:
你
│
“开发意见反馈模块”
│
▼
┌──────────────────────────────────┐
│ Codex Harness │
│ │
│ 基础 Instructions │
│ 权限 / Sandbox │
│ AGENTS.md │
│ Skills │
│ cwd / shell │
│ Tool 定义 │
│ 历史 Conversation │
│ 当前 Prompt │
└────────────────┬─────────────────┘
│
▼
模型①
│
“我要先看看项目”
│
▼
搜索项目文件
│
▼
返回相关文件列表
│
▼
模型②
│
“读取类似模块作为参考”
│
▼
┌──────────┴──────────┐
↓ ↓
NoticeController NoticeList.vue
NoticeService notice.js
NoticeMapper router
│ │
└──────────┬──────────┘
↓
相关代码进入 Context
↓
模型③
│
开始写代码
↓
创建 Feedback 模块
↓
模型④
│
mvn test / npm build
↓
返回构建日志
↓
模型⑤
│
有错 → 修改 → 再测试
│
▼
完成
二十五、最后总结
如果只记住几个结论,可以记下面这些。
1. Codex 不会默认把整个项目全部发给模型
第一次主要是:
Codex 基础 Instructions
+
权限 / Sandbox
+
AGENTS.md
+
环境信息
+
Tools
+
历史对话
+
你的 Prompt
2. 项目代码是“按需读取”的
模型先判断:
我需要看哪些文件?
然后通过 Tool:
搜索
读取
查看
只有真正读取到的代码,才会逐步进入后面的 Context。
3. 前后端不会天然全部发送
如果任务只涉及后端:
主要读取后端相关文件
如果任务同时涉及前后端:
才会分别查找相关前端、后端文件
但也不是把所有前后端代码一次性全部发进去。
4. 一次用户问题可能调用模型很多次
模型
↓
Tool
↓
模型
↓
Tool
↓
模型
……
这就是:
Agent Loop。
5. Token 最大的消耗通常不是你的 Prompt
更大的来源通常是:
代码
日志
Tool 结果
历史会话
多轮模型推理
长期 Context
6. Token Usage 和 Context Window 不是一回事
Token Usage
=
累计处理过多少内容
Context Window
=
当前模型脑子里能同时放多少内容
一句话总结
Codex 不是把整个 Spring Boot 项目一次性塞给模型,而是先把“基础指令、权限、AGENTS.md、环境、工具、历史和当前需求”等内容提供给模型,再由模型通过 Tool 按需搜索和读取相关代码;真正被读取的代码、日志、Tool 结果以及多轮 Agent Loop,才是日常 Token 消耗的重要来源。
理解这一点以后,再回头看:
Prompt
AGENTS.md
Context
Tool
Agent Loop
Token
Harness
这些概念其实就已经串到一起了。
本质上:
Harness 的一个重要工作,就是决定模型这一轮应该看到什么、能使用什么工具、能执行什么操作,以及如何在多轮 Agent Loop 中管理 Context。
浙公网安备 33010602011771号