一次 Request 中发生了什么:Input、Context、Inference 与 Response
一次 Request 中发生了什么:Input、Context、Inference 与 Response
上一篇区分了 LLM、模型服务和 Agent 系统。这一篇继续拆解“调用一次模型”:一条用户消息如何变成 Request,模型实际看到了什么,又返回了什么。
先看完整链路:
Input → Request → Context → Inference → Response
| 概念 | 本文中的含义 |
|---|---|
| Input | 用户、应用或工具提供的信息 |
| Request | 应用发给模型服务的一次调用 |
| Context | 模型本次推理实际可见的信息 |
| Inference | 模型计算并生成内容的过程 |
| Response | 模型服务返回的调用结果 |
界面上是一问一答,系统内部处理的却是五个不同概念。混用它们,很容易误判问题发生在哪一层。
从 Input 到 Request
继续使用这个系列的案例。用户输入:
调研三个主流 Agent Runtime,并给出选型建议。
这句话是 Input,不是完整的 Request。应用还会加入指令、历史消息、工具定义、输出格式、模型参数和追踪信息。
sequenceDiagram
participant U as 用户
participant A as 应用或 Runtime
participant S as 模型服务
participant M as 模型
U->>A: 用户 Input
A->>A: 合并指令、历史、工具与格式
A->>S: Request
S->>M: 准备本次 Context
M->>M: Inference
M-->>S: 生成内容
S-->>A: Response
A-->>U: 展示、校验或继续处理
这些信息有不同来源:用户提出需求,产品团队设定规则,Runtime 提供工具并记录元数据。只有被放进本次调用的内容,模型才有机会看到。
Request:应用发出了什么
Request 是应用提交给模型服务的调用数据。各家厂商的字段名不同,但大多包含以下内容:
| 内容 | 用途 | 通常来自哪里 |
|---|---|---|
| model | 选择模型 | 应用配置或路由策略 |
| instructions | 约束角色、规则和处理方式 | 产品或开发者 |
| input / messages | 提供用户内容与历史交互 | 用户、会话记录 |
| tools | 声明模型可选择的外部能力 | Tool Registry |
| output schema | 约束返回结构 | 下游程序契约 |
| generation config | 控制长度、采样或推理预算 | 应用配置 |
| metadata | 关联案例、日志和业务记录 | Runtime |
下面是 C02 使用的一份 OpenAI Request 样例:
{
"model": "gpt-5.6",
"instructions": "你是一名技术研究助手。只提炼研究问题、比较维度和当前限制。本次调用没有搜索工具,不得声称已经完成实时资料调研。",
"input": [
{
"role": "user",
"content": "调研三个主流 Agent Runtime,并给出选型建议。"
}
],
"tools": [],
"max_output_tokens": 2000,
"metadata": {
"case": "C02"
},
"stream": false
}
tools 在这里是空数组,因为 C02 只观察一次模型调用,不执行 Agent Loop。
Prompt 或 Messages 只是 Request 的一部分。Request 还可以包含图片、文件、工具定义和 JSON Schema,因此不能把两者画等号。
Request 也不是 Task。技术调研是用户要完成的 Task;上面的 JSON 只是为这个 Task 发起的一次调用。
Context:模型实际看到了什么
模型不会原样读取 Request。模型服务会套用消息模板、加入控制信息并完成 Tokenization,得到本次 Inference 使用的 Context。
Context 可能包含:
- 系统或开发者指令;
- 用户输入和选中的历史消息;
- 之前的模型输出、Tool Call 与 Tool Result;
- 工具描述、文件片段和输出格式说明。
聊天记录只是 Context 的来源之一。应用数据库里即使存着一百份资料,本次 Request 只放入三份,模型也只能根据这三份判断。工具定义虽然不显示成普通聊天消息,同样会占用 Context。
模型一次能处理的 Token 数量有限,这个上限通常称为 Context window。输入过长时,服务可能直接拒绝请求,应用也可能事先截断、摘要或删除部分历史。“模型没理会上轮要求”未必是它突然忘了,那条要求可能根本没有进入本次 Context。
所以排查“模型忘记了要求”时,先检查实际 Request 和 Trace。聊天界面不足以说明模型当时看到了什么。
Inference:模型如何生成内容
模型服务收到 Request 后,会校验参数、准备输入并执行计算。在文本生成场景中,大致过程如下:
Request 到达
→ 输入处理与 Tokenization
→ 模型计算下一个 Token
→ 按解码策略选取并追加 Token
→ 重复生成,直到停止
→ 组装 Response
这段计算过程就是 Inference。应用可以发送和记录 Request,但 Inference 发生在模型服务内部。
在最简单的调用中,一次 Request 可以看作一次逻辑生成。但工程实现未必严格一一对应:SDK 可能重试失败请求,服务可能执行路由或批处理,某些托管工具也会在背后做额外编排。审计时,应分别记录应用发出的 Request、模型服务返回的 Response,以及 Runtime 观察到的 Step。
Response:服务返回了什么
生成完成或中止后,模型服务返回 Response。除了文字,它还可能包含:
- Response ID、状态和模型信息;
- 结构化内容或 Tool Call;
- 拒绝、错误和未完成原因;
- Token usage 与其他元数据。
普通问答应用可以直接展示其中的文本。Agent Runtime 还要判断它是最终输出还是 Tool Call、结构是否合格、是否需要继续执行。一次调用成功,只代表应用收到了 Response,不代表 Task 已完成。

Schema 只约束结构
C02 接着用 Schema 约束同一个 Request 的输出。先定义 ResearchSummary:
const ResearchSummary = z.object({
research_question: z.string(),
comparison_dimensions: z.array(z.string()),
known_limitations: z.array(z.string()),
});
以下是一次实际返回的 Response:
{
"id": "resp_035b9f82fe1165ab006a71e04bd8f481998c61b4748a4650d8",
"status": "completed",
"model": "gpt-5.6-sol",
"format": {
"type": "json_schema",
"description": null,
"name": "research_summary",
"schema": {
"type": "object",
"properties": {
"research_question": {
"type": "string"
},
"comparison_dimensions": {
"type": "array",
"items": {
"type": "string"
}
},
"known_limitations": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"research_question",
"comparison_dimensions",
"known_limitations"
],
"additionalProperties": false
},
"strict": true
},
"output": [
{
"id": "rs_035b9f82fe1165ab006a71e04c7f30819987a7336810590d25",
"type": "reasoning",
"content": [],
"encrypted_content": "gAAAAABqceBVuIR0yNr***",
"summary": []
},
{
"id": "msg_035b9f82fe1165ab006a71e04e182c8199aaf5284204c8543d",
"type": "message",
"status": "completed",
"content": [
{
"type": "output_text",
"annotations": [],
"logprobs": [],
// 已截取
"text": "{\"research_question\":\"对三个主流 Agent Runtime 进行横向比较,评估其在目标业务场景中的适配性,并形成选型依据;需先明确候选范围(例如 LangGraph、Micros...]}",
"parsed": {
"research_question": "对三个主流 Agent Runtime 进行横向比较,评估其在目标业务场景中的适配性,并形成选型依据;需先明确候选范围(例如 LangGraph、Microsoft AutoGen、CrewAI,或云厂商托管型 Runtime)及具体使用场景。",
"comparison_dimensions": [
"产品定位与核心抽象",
"Agent 编排能力与多 Agent 协作",
"状态管理、持久化与断点恢复",
"工具调用、模型接入与协议兼容性(如 MCP)",
"工作流控制、人工介入与审批机制",
"记忆、上下文管理与知识检索集成",
"可靠性、容错、重试与幂等性",
"可观测性、调试、评测与追踪能力",
"部署方式、扩缩容与运行隔离",
"安全、权限、密钥管理与合规能力",
"性能、延迟、吞吐量与资源消耗",
"开发体验、学习成本与生态成熟度",
"商业授权、平台锁定与总体拥有成本",
"社区活跃度、版本稳定性与长期维护风险",
"与现有技术栈和团队能力的匹配程度"
],
"known_limitations": [
"本次调用没有搜索工具,无法完成实时资料调研或验证各产品的最新版本、功能、定价和活跃度。",
"“三个主流 Agent Runtime”缺少统一定义;框架型、运行时型和云托管型产品不宜在未分组的情况下直接比较。",
"尚未提供业务场景、负载规模、部署环境、合规要求、预算及团队技术栈,因此不能给出可靠的最终选型建议。",
"Agent Runtime 领域变化较快,基于非实时信息形成的结论可能存在时效性偏差。"
]
}
}
],
"phase": "final_answer",
"role": "assistant"
}
],
"usage": {
"input_tokens": 112,
"input_tokens_details": {
"cache_write_tokens": 0,
"cached_tokens": 0
},
"output_tokens": 489,
"output_tokens_details": {
"reasoning_tokens": 90
},
"total_tokens": 601
}
}
下游代码可以直接读取字段,不必从自然语言中猜测标题和列表。
Note:Schema 只检查结构。格式正确的 JSON 仍可能包含过时或错误的信息,事实需要另行验证。
Streaming:传输方式变了,调用没有变
Streaming 会把同一个 Response 拆成一串事件发送。每个 delta 都不是新的 Request,也不是新的 Agent Step。
非流式调用等生成结束后一次返回完整结果:
Request ─────────等待─────────> 完整 Response
流式调用则在生成期间持续发送事件或文本增量:
Request ─> response.created
─> output_text.delta
─> output_text.delta
─> ...
─> response.completed
这次调用收到的 Events 如下:

聚合后的概要如下:
{
"mode": "stream",
"response_id": "resp_05803925ca8f9b36006a71e02fc7d48198a67314caafd8737b",
"status": "completed",
"model": "gpt-5.6",
"elapsed_ms": 27889,
"event_count": 1184,
"event_type_counts": {
"response.created": 1,
"response.in_progress": 1,
"response.output_item.added": 2,
"response.output_item.done": 2,
"response.content_part.added": 1,
"response.output_text.delta": 1174,
"response.output_text.done": 1,
"response.content_part.done": 1,
"response.completed": 1
},
"first_event": "response.created",
"last_event": "response.completed",
"usage": {
"input_tokens": 60,
"input_tokens_details": {
"cache_write_tokens": 0,
"cached_tokens": 0
},
"output_tokens": 1611,
"output_tokens_details": {
"reasoning_tokens": 408
},
"total_tokens": 1671
},
// 已被截取
"aggregated_text": "以下为**非实时、待验证的初步选型框架**;本次未进行联网检索或最新版本核验。这里暂将 Agent Runtime 定义为:支..."
}
Streaming 的价值是让用户更早看到内容,也让应用提前开始处理。屏幕上的文字逐个出现,并不表示模型被反复调用。
代价是应用要处理流式连接的状态。连接中断后,必须分清收到的是完整结果还是半截文本;如果 SDK 自动重连或重试,还要防止重复写入。只保存界面最终显示的字符串,无法还原完整过程。
C02:把一次调用完整记录下来
C02 不接搜索工具,只验证一次调用的边界:Request 从哪里来、两种传输方式如何返回 Response、结构化输出能否通过校验。
代码已上传至 GitHub 仓库 AgentLoopExperienceSample。
运行命令:
npm run c02 2>&1 | tee artifacts/00-terminal-output.txt
非流式与流式输出不必逐字相同。采样和服务执行会带来差异。验证重点是两种方式都能得到一个逻辑 Response,并且流式事件可以聚合、记录和校验。
调节 max_output_tokens 等参数,还可以主动制造几类失败:
| 失败 | 可观察现象 | 责任位置 |
|---|---|---|
| Context 超限 | Request 被拒绝或历史被截断 | 应用的 Context 策略与服务限制 |
| 流连接中断 | 只收到部分事件,没有完成状态 | 传输与 Runtime 恢复逻辑 |
| Schema 校验失败 | Response 存在,但下游无法消费 | 输出契约与校验代码 |
C02 的结构化结果还不是“调研报告”。系统没有读取当前的官方资料,也没有保存证据。这个实验只证明应用能够构造、记录和检查一次模型调用。
小结
应用把 Input 组装成 Request,模型服务据此准备 Context、执行 Inference,再返回 Response。Streaming 只改变 Response 的传输方式,Schema 只约束输出结构。它们都不会自动完成整个 Task。
下一篇再回到 Task:用户交给系统的是一个任务,为什么一次 Request 往往不够?
案例实践清单
- 保存一份 C02 Request,并标注每个字段的来源。
- 在不启用工具的前提下分别执行非流式和流式调用。
- 保存流式事件,并聚合出最终 Response。
- 使用
research_summarySchema 约束和校验输出。 - 记录 Response ID、状态、Token usage、耗时与失败原因。
- 确认 C02 只验证调用边界,不宣称完成调研 Task。
参考资料
- OpenAI API:Responses API:Responses API 的输入、输出和调用方式。
- OpenAI API:Streaming responses:SSE 流式传输和响应事件。
- OpenAI API:Structured model outputs:使用 JSON Schema 约束模型输出。
- OpenAI API:Conversation state:
previous_response_id与 Conversation 的上下文续接方式。 - Anthropic API:Messages:另一种 Messages API 的 Request/Response 设计。
- Hugging Face Transformers:Chat templates:消息、控制 Token 与模型输入序列之间的转换。