DASP/1.0 API 事实参考
1. 文档用途与稳定性
本文是 DASP/1.0 的接口事实参考,面向客户端、SDK、测试和服务端实现人员。它回答以下问题:
- 某个业务场景应调用哪个接口;
- 请求头和请求体必须传什么;
- HTTP、JSON 或 SSE 会返回什么;
- 会话状态、上下文和 History 会怎样变化;
- 客户端在并发、断线和错误情况下必须怎样处理。
本文不是产品介绍,也不描述具体数据库、Redis、模型供应商或计费系统的内部实现。DASP 是独立于 AnyAPI 的公开协议名称。示例中的域名、模型名、SID、Response ID 和文本均为占位值。
协议版本:DASP/1.0
事实校验日期:2026-08-27
配套文档:
stateful-session-protocol-v1.md:协议语义、存储、执行、容灾和演进约束;stateful-session-client-guide-v1.md:客户端状态机和接入实现建议。
1.1 稳定承诺
DASP/1.0 发布后,以下内容不得在同一主版本中改变既有含义:
- HTTP 方法和路径;
sid请求头及其创建、定位和所有权语义;- 请求字段、响应字段和 Item 类型;
- SSE 事件类型、终止条件和回放语义;
- 状态、错误码、分页顺序和 Cursor 失效规则。
服务端可以增加可选字段、增加新的 Item 或事件类型,也可以提升内部性能,但客户端必须忽略未知字段、 未知 Item 和未知事件。破坏性变化只能发布新的主版本。
1.2 规范词
本文中的“必须”“不得”“应”“可以”具有规范意义:
- 必须/不得:兼容 DASP/1.0 所必需;
- 应:强烈建议,偏离时需要有明确理由;
- 可以:可选能力,不影响基础兼容。
2. 协议总览
2.1 SID 是什么
SID 是 Session Identifier(会话标识符) 的缩写。它是服务端为一次持续 AI 会话生成的唯一编号, 作用类似“订单号”:客户端用它告诉服务端“这次消息属于哪一个会话”,服务端再通过 SID 找回该会话的 最新上下文、历史消息、System Prompt、正在生成的响应和恢复位置。
一个 SID 的默认格式是 GUID 字符串,例如:
91ec4b23-600d-4a78-b46c-01aa46649ca4
客户端不需要生成 SID:首次创建会话时不发送 sid,服务端创建后通过响应头返回;后续续写、恢复、
取消或查询时,客户端只需把已保存的 SID 放回 sid 请求头。SID 不是聊天内容,也不是 User ID、API Key、
Response ID 或工具调用的 call_id,更不能单独作为访问凭证。
首次请求(无 sid) → 服务端创建 SID → 客户端保存 SID → 后续请求携带同一 SID
2.2 基础请求
Authorization: Bearer <API_KEY>
Content-Type: application/json
sid: <GUID,仅在需要时发送>
sid 只定位会话,不能代替 API Key、用户身份或租户授权。服务端必须同时校验认证主体与会话所有权,
并用 404 session_not_found 隐藏无权访问的 SID。
浏览器客户端可从响应中读取以下公开响应头:
sidhas-morenext-cursor
HTTP 头名称大小写不敏感。本文统一使用小写名称。
2.3 接口表
| 场景 | 方法 | 路径 | 请求参数 | 协议类型 |
|---|---|---|---|---|
| 创建或续写 | POST |
/v1/session/responses |
sid: GUID(创建不传,续写必填);Body:SessionRequest |
JSON 或 SSE |
| 编辑最近用户消息 | PUT |
/v1/session/responses |
sid: GUID(必填);Body:SessionRequest,仅文本输入 |
JSON 或 SSE |
| 中途打断并补充 | POST |
/v1/session/responses/interrupt |
sid: GUID(必填);Body:SessionRequest |
JSON 或 SSE |
| 恢复当前响应 | GET |
/v1/session/responses |
sid: GUID(必填) |
SSE |
| 安全取消 | POST |
/v1/session/responses/cancel |
sid: GUID(必填) |
JSON |
| 获取或合并 History | GET |
/v1/session/history |
sid: GUID[](必填,1~100);Query:cursor、limit、order、include_system_prompt |
JSON |
| 更新 System Prompt | PATCH |
/v1/session |
sid: GUID(必填);Body:SessionUpdateRequest |
JSON |
| 删除会话 | DELETE |
/v1/session |
sid: GUID(必填) |
HTTP 204 |
多会话 History 的 sid 是数组。文档中的默认逻辑格式为:
{
"sid": [
"91ec4b23-600d-4a78-b46c-01aa46649ca4",
"6b43e1b5-330e-46dd-81a4-94d36aee74a9"
]
}
由于 GET 不使用请求体,HTTP 默认编码为同一个 sid 请求头中的逗号分隔值:
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4,6b43e1b5-330e-46dd-81a4-94d36aee74a9
两种写法表达的是同一个逻辑数组;实际 HTTP 请求使用后一种格式。完整请求和响应示例见后续各场景。
所有接口的最大 HTTP 请求体为 4 MiB。文件内容不随请求上传,文件输入只接受公开 HTTP/HTTPS URL。
2.4 SID 事实
- SID 是服务端生成的 GUID,不编码用户、模型、时间或业务关系。
- 首次
POST省略或发送空白sid时,服务端创建 SID,并在响应头返回它。 - 续写、恢复、取消、打断、编辑、更新和删除必须发送一个 SID。
- History 可以在一个
sid请求头中发送 1~100 个逗号分隔的 SID。 - 客户端应在收到响应头后立即持久化 SID,不应等待回答结束。
- SID 不是密钥。泄漏 SID 不应导致跨认证主体访问,但客户端仍应避免把 SID 写入公开日志。
2.5 会话单写者规则
一个 SID 同一时刻最多有一个活动响应。普通续写、编辑和删除只允许在会话空闲时进行;System Prompt 可以在任意响应状态下切换,由服务端完成版本冻结与并发合并。其他并发冲突返回:
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"error": {
"code": "session_busy",
"message": "The session already has a running response."
}
}
客户端不得用并发重试绕过该约束。应恢复当前响应、等待终态,或先取消再执行后续操作。
3. 通用数据结构
3.1 SessionRequest
{
"model": "example-model",
"input": "你好",
"stream": false,
"think": false,
"max_output_tokens": 1024
}
| 字段 | 类型 | 必须 | 规则 |
|---|---|---|---|
model |
string | 是 | 非空,去除首尾空白后不超过 200 字符 |
input |
string 或 Input Item 数组 | 是 | 字符串非空;数组包含 1~64 项 |
stream |
boolean | 否 | 默认 false;true 返回 SSE |
system_prompt |
string 或 null | 否 | 仅无 SID 创建时允许;最大 65,536 字符;续写改用 PATCH |
think |
boolean | 否 | 模型能力开关;是否有效取决于模型 |
reasoning |
object | 否 | 可含 effort: low|medium|high|xhigh |
max_output_tokens |
positive integer | 否 | 必须大于 0 |
| 其他模型配置 | JSON | 否 | 例如 temperature、top_p、tools、tool_choice;是否支持取决于模型 |
think 与 reasoning 都是模型配置,不保证所有模型都支持。客户端应优先使用所选模型公开声明的方式,
不应同时发送互相矛盾的思考配置。
3.2 Input Item
输入数组只接受以下三类 Item。
text
{
"type": "text",
"data": "检查订单表索引"
}
data 必须是非空字符串。
function_call_output
{
"type": "function_call_output",
"call_id": "call_123",
"output": {
"result": "检查完成"
}
}
call_id 必须匹配当前上下文中尚未关闭的 function_call。output 可以是任意非 null JSON 值。
input_file
{
"type": "input_file",
"file_url": "https://files.example.com/report.pdf",
"filename": "report.pdf",
"detail": "auto"
}
规则:
- 每个请求最多 8 个文件;
file_url不超过 2048 字符,只允许可公开请求的httpsURL;- 禁止 URL 用户信息、非默认端口、回环、私网、链路本地和保留地址;
filename可选,非空且不超过 255 字符;detail可选,只能是auto、low或high;- 不接受
file_id、Base64、二进制表单或file_data。
文件执行路径由网关统一决定。公开模型原生支持文件时直接传递;平台透明路由存在可用的原生文件能力时,
也可在不改变公开模型身份的前提下直接处理。有效路由不具备文件能力或健康探测失败时,网关自动调用
mcp/multimodal-analysis,再把分析结果交给文本模型。客户端始终提交相同的 input_file,不得依赖或
推断内部路由;响应模型、鉴权、日志和计费均以请求的公开模型为准。
URL 通过语法检查不代表远端文件一定可下载。DNS 解析、重定向、超时、内容类型和上游分析仍可能失败。
3.3 非流式响应
{
"id": "resp_01K...",
"status": "completed",
"model": "example-model",
"output": [
{
"id": "msg_01K...",
"type": "text",
"status": "completed",
"data": "你好,有什么可以帮你?"
}
],
"usage": {
"input_tokens": 18,
"output_tokens": 12,
"total_tokens": 30,
"cached_tokens": 0,
"reasoning_tokens": 0
}
}
| 字段 | 事实 |
|---|---|
id |
当前模型响应的稳定 ID,不是 SID |
status |
成功通常为 completed;安全取消为 canceled |
model |
本次请求使用的模型标识 |
output |
与流式协议相同语义的输出 Item 数组 |
usage |
本次响应累计 Token 用量;只表达用量,不表达金额或币种 |
失败的非流式请求返回标准错误对象,不返回上述成功对象。
3.4 Usage
{
"input_tokens": 120,
"output_tokens": 40,
"total_tokens": 160,
"cached_tokens": 20,
"reasoning_tokens": 12
}
- 所有字段均为非负整数。
total_tokens = input_tokens + output_tokens。cached_tokens是输入 Token 的子集,不应再次加入total_tokens。reasoning_tokens是输出用量的细分,不应再次加入total_tokens。- 文件分析和上下文压缩产生的 Token 必须计入当前响应最终累计 Usage。
- 流中的多次
usage是累计快照,客户端必须以最新事件覆盖旧值,不能求和。 - DASP 公开协议不提供金额、币种、价格表或内部结算字段。
3.5 输出 Item
text
{
"id": "msg_01K...",
"type": "text",
"status": "completed",
"data": "订单表缺少 CreatedAt 和 Status 联合索引。"
}
reasoning
{
"id": "reason_01K...",
"type": "reasoning",
"status": "completed",
"output": "先检查查询条件,再比较现有索引。"
}
reasoning 只包含模型或供应商允许公开的思考摘要,不承诺返回隐藏思维链。没有公开摘要时,该 Item
可以缺席,即使模型内部进行了推理。
compaction
{
"id": "compact_01K...",
"type": "compaction",
"status": "completed",
"output": "保留了目标、约束、已确认结论和未完成工具调用。",
"usage": {
"input_tokens": 2200,
"output_tokens": 180,
"total_tokens": 2380,
"cached_tokens": 0,
"reasoning_tokens": 0
}
}
compaction 是可见状态投影,不是用户消息。History 仍保留未压缩的真实问答;压缩只改变下一次模型
调用使用的 Context。
file_analysis
{
"id": "file_01K...",
"type": "file_analysis",
"status": "completed",
"file_url": "https://files.example.com/report.pdf",
"source": "mcp/multimodal-analysis",
"data": "文件包含 12 页,核心结论是……",
"usage": {
"input_tokens": 810,
"output_tokens": 95,
"total_tokens": 905,
"cached_tokens": 0,
"reasoning_tokens": 0
}
}
function_call
{
"id": "tool_01K...",
"type": "function_call",
"status": "completed",
"call_id": "call_123",
"name": "database_inspect",
"arguments": {
"table": "Orders"
}
}
function_call 放在回答 answer 中;客户端执行工具后,把对应的 function_call_output 作为下一次
请求的 question 提交。工具输出不能塞进同一条 Answer 字符串。
3.6 Item 状态
| 状态 | 含义 |
|---|---|
running |
Item 已创建,仍可能收到更新或 Delta |
completed |
Item 完整结束 |
failed |
该投影步骤失败;整体响应是否失败以终止事件为准 |
同一个 Item ID 可能被多个 item 事件更新。客户端必须按 ID upsert,不能把每个事件都追加成新气泡。
4. 场景一:创建会话,非流式回答
4.1 请求
首次请求不发送 sid:
POST /v1/session/responses HTTP/1.1
Host: api.example.com
Authorization: Bearer <API_KEY>
Content-Type: application/json
Idempotency-Key: agent-session/42/controller
{
"model": "example-model",
"system_prompt": "你是严谨的数据库助手。",
"input": "你好",
"stream": false
}
4.2 成功响应
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
{
"id": "resp_01K...",
"status": "completed",
"model": "example-model",
"output": [
{
"id": "msg_01K...",
"type": "text",
"status": "completed",
"data": "你好,有什么可以帮你?"
}
],
"usage": {
"input_tokens": 8,
"output_tokens": 11,
"total_tokens": 19,
"cached_tokens": 0,
"reasoning_tokens": 0
}
}
4.3 服务端事实
- 服务端在同一事务内创建 SID、首个 Response、System Prompt 和真实 Question。
- 创建时的非空 System Prompt 同时形成 Prompt Revision 1;使用
include_system_prompt=true可按创建 时间读取。创建时未提供 Prompt 不伪造空 Revision。 - 响应成功后,Question 和 Answer 进入可见 History,并把适合后续模型调用的交换写入 Context。
- 客户端只保存 SID;续写时不需要回传先前问答。
- 首次请求可发送
Idempotency-Key。同一认证主体、同一 Key、同一请求摘要会重放同一个 SID 和 Response,并返回Idempotency-Replayed: true;同 Key 不同摘要返回409 idempotency_conflict。
4.4 注意事项与错误
- 网络可能在客户端收到 SID 前断开。需要强可恢复创建的业务必须用稳定
Idempotency-Key重试首个 请求,不能生成新 Key。 model或input无效返回400 invalid_request。- 模型不可用可返回
404 model_not_available。 - 存储暂不可用返回
503 session_storage_unavailable或503 session_unavailable。
5. 场景二:创建会话,流式回答
5.1 请求
POST /v1/session/responses HTTP/1.1
Host: api.example.com
Authorization: Bearer <API_KEY>
Content-Type: application/json
{
"model": "example-model",
"input": "解释一下 B+ 树索引",
"stream": true
}
5.2 响应头
HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-store
X-Accel-Buffering: no
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
客户端必须在读取第一条 SSE 数据前保存 sid。
5.3 SSE 示例
data: {"type":"start","id":"resp_01K...","model":"example-model"}
data: {"type":"item","item":{"id":"msg_01K...","type":"text","status":"running","data":""}}
data: {"type":"delta","id":"msg_01K...","data":"B+ 树通过"}
data: {"type":"delta","id":"msg_01K...","data":"有序多路节点降低磁盘读取次数。"}
data: {"type":"item","item":{"id":"msg_01K...","type":"text","status":"completed","data":"B+ 树通过有序多路节点降低磁盘读取次数。"}}
data: {"type":"usage","input_tokens":18,"output_tokens":24,"total_tokens":42,"cached_tokens":0,"reasoning_tokens":0}
data: {"type":"done","id":"resp_01K...","status":"completed"}
5.4 客户端完成条件
- 收到
done:正常或取消终态;读取done.status。 - 收到
error:失败终态;不得继续等待done。 - 连接断开但没有终态:结果未知,必须用同一 SID 调用恢复接口,不能重新
POST原输入。 : ping是 15 秒级保活注释,不是业务事件,必须忽略。
6. 场景三:续写已有会话
6.1 请求
POST /v1/session/responses HTTP/1.1
Authorization: Bearer <API_KEY>
Content-Type: application/json
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
{
"model": "example-model",
"input": "给一个 SQL 查询例子",
"stream": false
}
6.2 行为
服务端从持久层或缓存读取该 SID 的有效 Context,追加本次输入,必要时先自动压缩,再调用模型。
客户端不得把 History 重新拼到 input,否则会造成重复上下文、额外 Token 和错误的工具边界。
6.3 注意事项与错误
- SID 不存在或不属于当前认证主体:
404 session_not_found。 - 会话仍有活动响应:
409 session_busy。 - 已有 SID 的普通续写不接受
Idempotency-Key;重复POST会产生两个顺序响应。 - 对超时或断线请求,先
GET /responses判断原任务,不得盲目重发。
7. 场景四:刷新、掉线和恢复正在输出的响应
7.1 请求
GET /v1/session/responses HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
Accept: text/event-stream
GET 没有请求体,始终返回 SSE,不受原请求 stream 值影响。
7.2 恢复语义
- 服务端从当前响应开头回放稳定快照,然后在响应仍运行时继续转发新事件。
- 已完成响应回放到
done;已失败响应回放到error;已取消响应回放到done(canceled)。 - 恢复不会重新调用模型,不会因为重复连接而重复计算或重复计费。
- 服务端可能从持久层回放,也可能命中 Redis;两者协议结果相同。
7.3 客户端必须重建投影
恢复是“从头回放”,不是“从断点继续”。正确流程:
- 清空该 Response ID 的临时输出投影;
- 按
item.idupsert; - 按
delta.id追加到对应 Item; - 用最新
usage覆盖旧值; - 直到
done或error。
如果直接在旧文本后追加回放 Delta,会产生重复内容。
7.4 错误
- 会话不存在:
404 session_not_found。 - 会话存在但从未创建响应:
404 response_not_found。 - 临时无法读取持久状态:
503 session_storage_unavailable。
客户端可对网络错误和 503 使用指数退避恢复。不要在恢复失败时改为重新提交原问题。
8. 场景五:普通回答与深度思考
8.1 非思考请求
{
"model": "example-model",
"input": "只给结论",
"stream": true,
"think": false
}
8.2 深度思考请求
{
"model": "example-reasoning-model",
"input": "比较三种索引方案并说明取舍",
"stream": true,
"reasoning": {
"effort": "high"
},
"max_output_tokens": 4096
}
可能的流式投影:
data: {"type":"item","item":{"id":"reason_01K...","type":"reasoning","status":"running","output":""}}
data: {"type":"delta","id":"reason_01K...","data":"先比较写入成本和查询选择性。"}
data: {"type":"item","item":{"id":"reason_01K...","type":"reasoning","status":"completed","output":"先比较写入成本和查询选择性。"}}
data: {"type":"item","item":{"id":"msg_01K...","type":"text","status":"completed","data":"推荐方案二……"}}
data: {"type":"usage","input_tokens":120,"output_tokens":180,"total_tokens":300,"cached_tokens":0,"reasoning_tokens":92}
data: {"type":"done","id":"resp_01K...","status":"completed"}
8.3 事实与注意事项
reasoning.effort只接受low、medium、high、xhigh。- 不支持该配置的模型可能拒绝或忽略它,具体取决于模型能力。
reasoning_tokens可大于 0,但reasoningItem 仍可能缺席。- 客户端不得把公开摘要描述成完整内部思维链。
- 非流式响应也在
output中使用同样的reasoningItem。
9. 场景六:自动上下文压缩
9.1 触发
客户端不提交压缩命令。服务端在 Context 接近模型上限时自动判断并执行压缩。
9.2 流式投影
data: {"type":"item","item":{"id":"compact_01K...","type":"compaction","status":"running","output":""}}
data: {"type":"item","item":{"id":"compact_01K...","type":"compaction","status":"completed","output":"已保留目标、约束、结论和开放工具调用。","usage":{"input_tokens":2200,"output_tokens":180,"total_tokens":2380,"cached_tokens":0,"reasoning_tokens":0}}}
9.3 服务端事实
- 压缩只修改模型使用的 Context,不删除或改写真实 History。
- System Prompt、上一轮 Summary、最近三条用户消息、未关闭工具调用边界和当前问题必须保留。
- 压缩优先使用原会话模型;大输入按统一 Token 预算分块,原模型失败后按服务端配置的候选模型顺序切换。
- 压缩用量进入本次响应累计 Usage。
- 客户端把
compaction显示为过程状态,不应伪装成 AI 文本回答。
9.4 失败
- 如果压缩失败但原 Context 仍能安全调用模型,服务端可以发出
failed的 compaction Item 后继续。 - 如果 Context 已无法安全调用且不能压缩,响应以
500 context_compaction_failed终止。 - 客户端不得自动删除 History 或裁剪工具调用来修复该错误。
10. 场景七:工具调用闭环
10.1 第一次请求
{
"model": "example-tool-model",
"input": "检查订单表索引",
"stream": false,
"tools": [
{
"type": "function",
"name": "database_inspect",
"description": "检查数据库表结构和索引",
"parameters": {
"type": "object",
"properties": {
"table": { "type": "string" }
},
"required": ["table"]
}
}
]
}
10.2 模型返回工具调用
{
"id": "resp_01K...",
"status": "completed",
"model": "example-tool-model",
"output": [
{
"id": "tool_01K...",
"type": "function_call",
"status": "completed",
"call_id": "call_123",
"name": "database_inspect",
"arguments": {
"table": "Orders"
}
}
],
"usage": {
"input_tokens": 52,
"output_tokens": 18,
"total_tokens": 70,
"cached_tokens": 0,
"reasoning_tokens": 0
}
}
10.3 客户端执行工具后续写
POST /v1/session/responses HTTP/1.1
Authorization: Bearer <API_KEY>
Content-Type: application/json
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
{
"model": "example-tool-model",
"stream": false,
"input": [
{
"type": "function_call_output",
"call_id": "call_123",
"output": {
"result": "Orders 有 IX_Orders_CustomerId,无 CreatedAt+Status 联合索引"
}
}
]
}
10.4 事实与错误
- History 第一条交换的
answer包含function_call。 - 下一条交换的
question包含function_call_output,最终回答位于其answer。 - 相同
call_id的工具结果不能提交两次。 - 没有匹配开放调用的结果返回
400 function_call_mismatch。 - 编辑只接受文本,因此不能用编辑接口改写已提交的工具输出。
- 客户端负责工具权限、审批和执行;DASP 只保存并还原协议 Item。
11. 场景八:文件输入
11.1 请求
{
"model": "example-model",
"stream": true,
"input": [
{
"type": "text",
"data": "总结附件并指出风险"
},
{
"type": "input_file",
"file_url": "https://files.example.com/audit.pdf",
"filename": "audit.pdf",
"detail": "high"
}
]
}
11.2 原生多模态模型
模型能原生处理该文件类型时,服务端把 input_file 按模型协议传入。模型产生的文本、推理或工具调用
按普通输出 Item 返回。
11.3 非多模态模型
模型不能原生处理时,服务端自动调用 mcp/multimodal-analysis,将分析结果注入本次 Context,并返回
file_analysis 投影:
data: {"type":"item","item":{"id":"file_01K...","type":"file_analysis","status":"running","file_url":"https://files.example.com/audit.pdf"}}
data: {"type":"item","item":{"id":"file_01K...","type":"file_analysis","status":"completed","file_url":"https://files.example.com/audit.pdf","source":"mcp/multimodal-analysis","data":"审计报告识别出三个高风险项……","usage":{"input_tokens":810,"output_tokens":95,"total_tokens":905,"cached_tokens":0,"reasoning_tokens":0}}}
客户端不需要判断模型是否多模态,也不需要自己调用 MCP。
11.4 History 还原
question保留原始input_fileURL 和文本输入。- 非多模态路径的
answer保留file_analysis投影及最终模型输出。 - History 保存的是原始 AI 输入输出投影,不下载并复制远端文件内容。
11.5 错误与安全
- URL 语法或网络目标不安全:
400 invalid_file_url。 - 自动文件分析失败:
502 file_analysis_failed。 - 远端 URL 应使用短期、只读、最小权限的签名链接,且有效期应覆盖排队、执行和故障恢复时间。
- 客户端不得在 URL 中放长期密钥;服务端日志不得完整记录敏感查询参数。
12. 场景九:安全取消
关闭浏览器、断开 SSE 或取消本地读取只会断开客户端连接,不会取消服务端任务。
12.1 请求
POST /v1/session/responses/cancel HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
无请求体。
12.2 已接受取消
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
{
"status": "canceling"
}
202 表示取消请求已记录,不表示供应商已确认停止。客户端随后应调用恢复接口,直到:
data: {"type":"usage","input_tokens":120,"output_tokens":31,"total_tokens":151,"cached_tokens":0,"reasoning_tokens":0}
data: {"type":"done","id":"resp_01K...","status":"canceled"}
12.3 已经终止
重复取消或响应已经结束时,服务端返回 200 和当前终态,例如:
{
"status": "completed"
}
取消是幂等的状态操作,不是删除:
- 已生成的部分输出和已确认 Usage 被持久化;
- canceled 交换可以进入 History;
- 会话仍存在,可以在终态后继续提问;
- 要删除会话必须另行调用
DELETE /v1/session。
13. 场景十:中途打断并提交补充信息
此场景用于“先停下,把这条补充也考虑进去”,不是普通续写。
13.1 请求
POST /v1/session/responses/interrupt HTTP/1.1
Authorization: Bearer <API_KEY>
Content-Type: application/json
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
{
"model": "example-model",
"stream": true,
"input": "补充:只检查最近 30 天的慢查询"
}
13.2 流式交接示例
data: {"type":"interrupt","id":"resp_old","status":"canceling"}
data: {"type":"interrupt","id":"resp_old","status":"canceled","usage":{"input_tokens":90,"output_tokens":22,"total_tokens":112,"cached_tokens":0,"reasoning_tokens":0}}
data: {"type":"start","id":"resp_new","model":"example-model"}
data: {"type":"item","item":{"id":"msg_new","type":"text","status":"running","data":""}}
data: {"type":"delta","id":"msg_new","data":"结合最近 30 天范围,发现……"}
data: {"type":"usage","input_tokens":145,"output_tokens":36,"total_tokens":181,"cached_tokens":0,"reasoning_tokens":0}
data: {"type":"done","id":"resp_new","status":"completed"}
13.3 原子语义
- 服务端原子地把旧响应标记为取消中,并创建唯一后继响应。
- 后继响应的 Question 是“旧响应原始 Question + 本次补充 Input”。
- 旧响应的部分 Answer 以
canceled保存到 History,但不会注入后继 Context,避免把半截结论当事实。 - 后继完成后,其完整交换进入 Context。
- 客户端掉线后,对同一 SID 调用恢复接口,会从头回放整个交接链。
13.4 竞态与错误
- 如果旧响应在打断被接受前已经完成,服务端可以把补充退化为普通续写;客户端不应假设一定出现
interrupt事件。 - 同一会话已经处于打断、取消或其他冲突状态时返回
409 session_busy。 interrupt.status = outcome_unknown表示旧供应商任务的最终执行状态无法确认;客户端不得据此重复提交旧问题。
14. 场景十一:编辑最近一条用户消息并剪切后续分支
编辑的业务定义是:替换最近一条仍可见的用户文本消息,把它以及之后产生的可见消息标记为旧分支, 然后从替换文本重新生成。它不是修改数据库中某个孤立字符串。
14.1 请求
PUT /v1/session/responses HTTP/1.1
Authorization: Bearer <API_KEY>
Content-Type: application/json
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
{
"model": "example-model",
"stream": false,
"input": [
{
"type": "text",
"data": "检查订单表最近 30 天查询使用的索引"
}
]
}
成功响应与普通提交相同。
14.2 事实
- 只允许
text替换 Item,不接受文件或工具结果。 - 会话必须空闲;正在生成时先安全取消并等待终态。
- 被剪切内容保留为内部可审计旧版本,但不再出现在普通 History,也不进入后续 Context。
- 编辑完成后,History Cursor 的快照发生变化;旧 Cursor 返回
409 cursor_stale。 - 会话没有可编辑用户消息时返回
409 no_editable_message。
15. 场景十二:查询单会话 History 和分页
15.1 第一页
GET /v1/session/history?limit=2 HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
has-more: true
next-cursor: <OPAQUE_CURSOR>
[
{
"sid": "91ec4b23-600d-4a78-b46c-01aa46649ca4",
"status": "completed",
"question_at": "2026-08-27T02:14:03.125Z",
"question": [
{
"type": "text",
"data": "检查订单表索引"
}
],
"answer_at": "2026-08-27T02:14:05.941Z",
"answer": [
{
"id": "msg_01K...",
"type": "text",
"status": "completed",
"data": "订单表缺少 CreatedAt 和 Status 联合索引。"
}
]
}
]
默认响应体是数组,不包 data、items 或分页对象。只有显式请求 System Prompt 时间线时才使用
第 15.5 节定义的可选 Envelope。分页信息始终在响应头。
15.2 下一页
GET /v1/session/history?limit=2&cursor=<URL_ENCODED_CURSOR> HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
Cursor 是不透明字符串。客户端只能原样保存并进行 URL 编码,不能解析、拼接或修改。
需要首屏先展示最近消息、再向上加载更早内容时,显式使用倒序分页:
GET /v1/session/history?limit=50&order=desc HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
该模式的第一页从最新边缘开始,后续 next-cursor 持续指向更早的 Exchange。响应页按倒序返回;聊天
界面可以把每页按时间正序渲染,并在上拉时把下一页插入现有内容前面。
15.3 字段事实
| 字段 | 类型 | 含义 |
|---|---|---|
sid |
GUID string | 该消息所属会话 |
status |
string | 该交换终态,当前可见 History 为 completed 或 canceled |
question_at |
UTC ISO 8601 string | 服务端接受 Question 的时间 |
question |
Item array | 原始用户/工具/文件输入投影 |
answer_at |
UTC ISO 8601 string | Answer 进入终态的时间 |
answer |
Item array | AI 文本、推理、压缩、文件分析和工具调用投影 |
question 和 answer 始终是数组。即使只有一段文本,也不退化为字符串或单个对象。
15.4 分页事实
limit默认 50,允许 1~100。order允许asc或desc,默认asc;其他值返回400 invalid_order。- 稳定排序键是
question_at加服务端内部行 ID;asc两者均升序,desc两者均倒序,内部 ID 不公开。 has-more: true时才有next-cursor。- Cursor 绑定当前 SID 集合、History 修订快照、
include_system_prompt模式和order;续页必须保持这些参数不变。 - 续页更换
order或include_system_prompt时返回400 invalid_cursor。 - 分页期间发生新消息、编辑或删除等历史变化时,旧 Cursor 返回
409 cursor_stale。 - 收到
cursor_stale后必须丢弃本轮已拼接页面和 Cursor,从无 Cursor 的第一页重新读取,避免重复或缺失。 - History 只返回已经持久化的可见终态交换;运行中响应和 failed 响应不在 History。
- 运行中、失败或结果未知的当前响应应使用
GET /v1/session/responses恢复。
15.5 可选返回 System Prompt 时间线
默认 History 不查询或返回 Prompt 历史。需要还原 Prompt 变更时间点时显式发送:
GET /v1/session/history?limit=50&include_system_prompt=true HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
此模式返回固定 Envelope:
{
"history": [
{
"sid": "91ec4b23-600d-4a78-b46c-01aa46649ca4",
"status": "completed",
"question_at": "2026-08-27T02:14:03.125Z",
"question": [{ "type": "text", "data": "检查订单表索引" }],
"answer_at": "2026-08-27T02:14:05.941Z",
"answer": [{ "id": "msg_01K...", "type": "text", "status": "completed", "data": "检查完成。" }]
}
],
"system_prompts": [
{
"sid": "91ec4b23-600d-4a78-b46c-01aa46649ca4",
"revision": 1,
"system_prompt_at": "2026-08-27T02:13:58.400Z",
"system_prompt": "你是数据库审计助手。"
},
{
"sid": "91ec4b23-600d-4a78-b46c-01aa46649ca4",
"revision": 2,
"system_prompt_at": "2026-08-27T02:18:20.100Z",
"system_prompt": null
}
]
}
事实规则:
system_prompt: null表示该时间点清空 Prompt;- 重复 PATCH 与当前值完全相同是幂等 no-op,不新增 Revision 或时间线记录;
revision在单个 SID 内单调递增,时间使用服务端 UTC;每条 Prompt 记录都包含来源 SID;limit统计本页history与system_prompts的合计数量,而不是分别限制两个数组;- 服务端按
system_prompt_at/question_at + 类型 + 内部行键对两类事实做全局稳定 Keyset 分页,再分别 放入两个数组;客户端按时间字段合并显示; has-more和next-cursor仍位于响应头;此模式使用独立 Cursor,不能与默认 History Cursor 混用;- 下一页必须继续发送相同 SID 集合、
include_system_prompt=true和返回的 Cursor; - Prompt 更新、消息新增、编辑或删除会使旧 Cursor 返回
409 cursor_stale; - 内部 Prompt State 只保留最新值;
ContextJson不保存 Prompt 快照或 Prompt 历史。完整变更序列只 存在于不可变 Prompt History,并且不会作为对话消息注入模型 Context。
16. 场景十三:一次拉取多个会话并按时间自动合并
主会话和子会话不需要在 DASP 内维护父子关系。业务客户端持有 SID 集合,查询时一次提交,服务端按真实 时间稳定合并。
16.1 请求
GET /v1/session/history?limit=50 HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4,6b43e1b5-330e-46dd-81a4-94d36aee74a9,1329db31-d7f3-43aa-bb65-61e299ddcfa8
也可以由 HTTP 客户端发送多个同名 sid 请求头;服务端会拆分逗号、去重并保持 1~100 个有效 GUID。
16.2 响应
[
{
"sid": "91ec4b23-600d-4a78-b46c-01aa46649ca4",
"status": "completed",
"question_at": "2026-08-27T02:14:03.125Z",
"question": [{ "type": "text", "data": "规划检查任务" }],
"answer_at": "2026-08-27T02:14:04.410Z",
"answer": [{ "id": "msg_parent", "type": "text", "status": "completed", "data": "开始检查三个模块。" }]
},
{
"sid": "6b43e1b5-330e-46dd-81a4-94d36aee74a9",
"status": "completed",
"question_at": "2026-08-27T02:14:04.002Z",
"question": [{ "type": "text", "data": "检查订单表索引" }],
"answer_at": "2026-08-27T02:14:07.805Z",
"answer": [{ "id": "msg_child", "type": "text", "status": "completed", "data": "发现缺少联合索引。" }]
}
]
16.3 合并事实
- 返回结果按
question_at全局稳定排序,不是先按 SID 分组;默认升序,order=desc时倒序。 - 每条结果必须带
sid,客户端据此显示所属主/子员工、线程或业务节点。 - 时间来自服务端 UTC 时间,不依赖客户端本地时钟,也不依赖各 SID 内的局部 Sequence。
- 相同时间戳由服务端稳定排序;客户端不得自行以数组下标生成永久顺序 ID。
- 请求中的任一 SID 不存在或不属于调用方时,整个请求返回
404 session_not_found,不返回部分结果。 - Cursor 绑定完整 SID 集合。下一页必须发送完全相同的 SID 集合和 Cursor。
- 倒序合并查询从所有 SID 的全局最新边缘开始,后续页继续向更早时间移动。
include_system_prompt=true对多 SID 同样有效;system_prompts每项携带所属 SID,并与所有消息按 服务端时间参与同一个分页序列。
17. 场景十四:更新或移除 System Prompt
17.1 设置
PATCH /v1/session HTTP/1.1
Authorization: Bearer <API_KEY>
Content-Type: application/json
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
{
"system_prompt": "你是数据库审计助手。回答必须区分事实和建议。"
}
{
"status": "updated"
}
17.2 移除
以下任一种会移除已有 System Prompt:
{ "system_prompt": null }
{ "system_prompt": "" }
纯空白字符串也视为移除。
17.3 事实与错误
- 最大 65,536 字符,超出返回
400 invalid_system_prompt。 - 运行中、取消中和结果未知状态都允许 PATCH,不返回
409 session_busy。 - PATCH 成功是一个原子版本切换;同一 SID 的并发 PATCH 由服务端重试并串行化,最后成功提交的值生效。
- 每个响应在被接受时冻结 Prompt 快照。已经接受或正在输出的响应继续使用旧快照,不会中途改变 Provider 输入、重启模型调用或重复产生 Usage。
- PATCH 成功之后才被接受的普通续写、编辑或中途补充响应使用新 Prompt。
- 如果业务要求“当前问题立即使用新 Prompt 重新回答”,先 PATCH,再调用中途打断接口提交必要补充; 该后继响应在 PATCH 之后被接受,因此使用新 Prompt。
- 更新只影响之后被接受的模型响应,不回写既有 History,也不生成新的 History 消息或 Usage。
- 每次实际值变化会追加一条不可变 Prompt History;重复设置相同值不追加。客户端仅在查询时通过
include_system_prompt=true获取该数组。 - Prompt 有独立的持久修订和并发版本;模型请求在组装 Context 时把冻结快照放在摘要与历史之前。
- 冻结快照只服务于其所属 Response,不写回
ContextJson;该 Response 结束时也不能覆盖并发 PATCH 已提交的最新 Prompt State。 - Prompt 更新会使现有 History Cursor 返回
409 cursor_stale;客户端按通用规则从第一页重启分页。 - System Prompt 是模型 Context 的会话级配置,但不是 History 中的普通 Question。
18. 场景十五:删除会话
18.1 请求和响应
DELETE /v1/session HTTP/1.1
Authorization: Bearer <API_KEY>
sid: 91ec4b23-600d-4a78-b46c-01aa46649ca4
HTTP/1.1 204 No Content
18.2 事实
- 删除是会话级操作,不是取消。
- 删除采用服务端持久删除标记;对调用方表现为会话不存在。
- 重复删除是幂等的,只要 SID 格式和所有权检查可以完成。
- 活动或取消中的会话不能删除,返回
409 session_busy;必须先取消并恢复到终态。 - 删除后,不得继续使用该 SID;后续访问返回
404 session_not_found。
19. SSE 完整契约
19.1 帧格式
data: <一个紧凑 JSON 对象>\n
\n
保活帧:
: ping\n
\n
客户端必须使用 SSE 分帧规则解析,不能假设一个 TCP 数据块等于一个事件,也不能逐网络行直接解析 JSON。
19.2 事件表
type |
关键字段 | 是否终止 | 客户端行为 |
|---|---|---|---|
start |
id, model |
否 | 建立或重置 Response 投影 |
item |
item |
否 | 按 item.id upsert;未知 Item 保留或忽略 |
delta |
id, data |
否 | 向同 ID Item 追加文本片段 |
usage |
Usage 五字段 | 否 | 用最新累计快照覆盖 |
interrupt |
id, status, 可选 usage |
否 | 更新旧响应交接状态 |
done |
id, status |
是 | 完成;状态为 completed 或 canceled |
error |
code, message |
是 | 失败;停止读取,不等待 done |
19.3 start
{
"type": "start",
"id": "resp_01K...",
"model": "example-model"
}
19.4 item
{
"type": "item",
"item": {
"id": "msg_01K...",
"type": "text",
"status": "running",
"data": ""
}
}
19.5 delta
{
"type": "delta",
"id": "msg_01K...",
"data": "新增片段"
}
19.6 usage
{
"type": "usage",
"input_tokens": 120,
"output_tokens": 40,
"total_tokens": 160,
"cached_tokens": 20,
"reasoning_tokens": 12
}
19.7 interrupt
{
"type": "interrupt",
"id": "resp_old",
"status": "canceled",
"usage": {
"input_tokens": 90,
"output_tokens": 22,
"total_tokens": 112,
"cached_tokens": 0,
"reasoning_tokens": 0
}
}
19.8 done
{
"type": "done",
"id": "resp_01K...",
"status": "completed"
}
19.9 error
{
"type": "error",
"code": "provider_timeout",
"message": "The provider invocation timed out."
}
19.10 HTTP 错误和 SSE 错误的边界
- 在 SSE 响应头发送前发现错误:返回普通 HTTP 状态和 JSON 错误对象。
- SSE 已开始后发现错误:HTTP 状态不能再改变,发送终止
error事件。 - 客户端必须同时实现 HTTP 错误解析和 SSE
error解析。
20. 错误对象
所有进入 DASP 业务处理后、能以普通 HTTP JSON 返回的协议错误使用:
{
"error": {
"code": "invalid_request",
"message": "input cannot be empty."
}
}
客户端逻辑必须判断 error.code,不得解析英文 message。message 用于日志和用户可读诊断,未来可优化措辞。
20.1 传输层和网关错误
以下错误可能在请求到达 DASP 控制器前由 Web 框架、反向代理、WAF 或负载均衡器产生,因此不保证使用 DASP 错误对象:
- JSON 语法损坏或
Content-Type不受支持; - 请求体超过 4 MiB,通常为
413 Payload Too Large; - 代理连接超时、TLS 失败、网关无健康上游;
- 平台级认证或限流在 DASP 路由之前拒绝请求。
客户端必须先按 HTTP 状态判断成功与否,再尝试解析 DASP 错误对象;解析不到时保留状态、响应头和经过 脱敏的响应摘要。对状态变更请求,传输层超时同样不能直接重提,应先按 SID 恢复或查询。
21. 错误目录与处理规则
| HTTP | code |
常见场景 | 是否可自动重试 | 客户端处理 |
|---|---|---|---|---|
| 400 | invalid_request |
缺少 model/input、Item 不支持、数量或字段非法 | 否 | 修正请求,不原样重试 |
| 400 | invalid_sid |
缺少 SID、不是 GUID、History 超过 100 个 | 否 | 修正本地 SID |
| 400 | invalid_reasoning_effort |
effort 不在允许集合 | 否 | 改为允许值或移除 |
| 400 | invalid_file_url |
非 HTTP(S)、私网/保留地址、不安全 URL | 否 | 换公开安全 URL |
| 400 | invalid_cursor |
Cursor 格式非法或被修改 | 否 | 丢弃 Cursor,从第一页开始 |
| 400 | invalid_limit |
limit 不在 1~100 | 否 | 修正 limit |
| 400 | invalid_order |
order 不是 asc 或 desc | 否 | 修正 order;续页保持与 Cursor 一致 |
| 400 | invalid_system_prompt |
Prompt 超过 65,536 字符 | 否 | 缩短后重试 |
| 400 | invalid_idempotency_key |
Key 无效或用于已有 SID | 否 | 修正创建请求 |
| 409 | idempotency_conflict |
同一 Key 的主体或摘要不同 | 否 | 使用原请求或新 Key |
| 400 | function_call_mismatch |
call_id 不开放、重复提交工具结果 | 否 | 修正工具状态机 |
| 401 | unauthorized |
API Key 缺失、无效或过期 | 否 | 刷新或更换认证 |
| 404 | session_not_found |
SID 不存在、已删除或不属于当前主体 | 否 | 停止使用 SID;不要自动新建替代 |
| 404 | response_not_found |
会话尚无可恢复响应 | 否 | 允许用户首次提交 |
| 404 | model_not_available |
模型未配置或当前不可用 | 条件重试 | 选择可用模型 |
| 409 | session_busy |
同 SID 已有活动响应或发生并发变化 | 条件重试 | 先恢复/等待;需要停止时先取消 |
| 409 | no_editable_message |
没有可编辑的用户文本 | 否 | 禁用编辑操作 |
| 409 | cursor_stale |
History 在分页期间发生变化 | 是,重启分页 | 丢弃整轮分页结果和 Cursor |
| 429 | rate_limit_exceeded |
请求过快或配额限制 | 是 | 遵守 Retry-After;指数退避加抖动 |
| 500 | context_compaction_failed |
Context 无法安全压缩到模型上限内 | 否 | 提示用户新建会话或缩小当前任务 |
| 500 | provider_outcome_unknown |
供应商可能已执行,但终态无法确认 | 不得自动重提 | 保留 SID/Response,提示人工确认或继续恢复 |
| 500 | session_internal_error |
未分类服务端会话错误 | 条件重试 | 先恢复原响应;记录关联信息 |
| 502 | provider_error |
供应商返回无效结果或执行失败 | 条件重试 | 先恢复;确认终态后再由用户决定 |
| 502 | file_analysis_failed |
自动文件分析失败 | 条件重试 | 检查 URL 有效期、类型和可达性 |
| 503 | session_storage_unavailable |
持久存储暂不可用 | 是 | 指数退避;不要形成重试风暴 |
| 503 | session_unavailable |
新会话接受暂时关闭 | 是 | 延迟新建;已有会话优先尝试恢复 |
| 504 | provider_timeout |
供应商超时 | 条件重试 | 先恢复同一 SID,避免重复执行 |
供应商可能返回本文未列出的具体错误码。客户端应:
- 优先按已知
code处理; - 未知
code按 HTTP 状态类别处理; - 保留完整错误用于诊断;
- 不因未知字段或未知错误码崩溃。
22. 重试、重复提交与结果未知
22.1 安全重试的操作
GET /v1/session/responses:只读恢复,可重连;GET /v1/session/history:只读查询,可重试;发生cursor_stale时必须从第一页重启;POST /responses/cancel:幂等状态请求,可重试;PATCH /v1/session:设置目标 Prompt 的幂等赋值,可用相同请求体重试;DELETE /v1/session:终态且所有权不变时可幂等重试。- 无 SID 的首个
POST /v1/session/responses:携带同一Idempotency-Key和完全相同请求时可重试。
22.2 不能盲目重试的操作
POST /v1/session/responsesPUT /v1/session/responsesPOST /v1/session/responses/interrupt
这些操作会改变状态。除“无 SID 的首次创建”外,Idempotency-Key 不适用;连接断开后,客户端必须
先用 SID 查询或恢复。
22.3 provider_outcome_unknown
该错误的含义不是“供应商一定没执行”,而是“服务端无法证明最终结果”。原调用可能已经消耗 Token、触发 外部工具或产生副作用。客户端必须:
- 保存 SID 和已知 Response ID;
- 不自动重复提交相同问题或工具结果;
- 继续尝试恢复,或交给用户/业务系统确认;
- 若工具具有副作用,使用业务自己的幂等键和审计机制。
23. 状态与操作矩阵
| 当前状态 | 普通续写 | 恢复 | 取消 | 打断补充 | 编辑 | 更新 Prompt | 删除 |
|---|---|---|---|---|---|---|---|
running |
session_busy |
可以 | 可以 | 可以 | session_busy |
可以,影响后续接受的响应 | session_busy |
canceling |
session_busy |
可以 | 可重复 | session_busy |
session_busy |
可以,影响后续接受的响应 | session_busy |
completed |
可以 | 回放完成响应 | 返回 completed | 按接受时竞态处理 | 可以 | 可以 | 可以 |
canceled |
可以 | 回放 canceled 响应 | 返回 canceled | 作为普通后续输入处理 | 可以 | 可以 | 可以 |
failed |
可以 | 回放 error | 返回 failed | 作为普通后续输入处理 | 视是否有可编辑消息 | 可以 | 可以 |
outcome_unknown |
应先确认 | 回放 error | 返回当前状态 | 不应自动补交 | 不应自动编辑 | 可以 | 可以,需业务确认 |
具体竞态以原子接受时的服务端状态为准。客户端界面状态只是缓存,不能替代服务端判定。
24. 客户端最小实现
客户端只需要持有:
sid
当前 Response 投影
History 当前页及不透明 cursor
不需要持有或管理:
完整模型上下文
上下文压缩结果
服务端执行租约
Redis Key
供应商路由
Token 价格或内部账单
最小流程:
- 首次
POST不传 SID;收到响应头立刻保存 SID。 - 流事件按类型处理,未知类型忽略但记录。
- 无终态掉线时用同一 SID
GET恢复,并重建当前 Response 投影。 - 新问题只提交新增 Input 和本次 Model Config。
- 停止按钮调用 cancel,不只关闭网络连接。
- 中途补充使用 interrupt,不并发普通
POST。 - 编辑先确保空闲,再
PUT;收到cursor_stale重启 History 分页。 - 多会话展示一次查询全部 SID,直接使用返回顺序,并依据每条记录的
sid标注来源。
25. 服务端持久性与故障转移的可观察事实
实现可以使用 SQL、Redis、队列和多实例执行器,但对客户端必须满足:
- SQL 或等价持久层是真实状态来源;缓存丢失不能丢会话、History 或最终 Usage。
- Redis 只做 Context、回放和通知加速;缓存未命中必须回源持久层。
- HTTP 连接不是任务所有者;客户端断开后,服务端任务继续运行,除非明确取消。
- 同一响应只能有一个执行所有者;故障接管不得产生两个可见 Answer 或重复结算。
- 终态、Output、Usage、History 和会话活动指针必须以原子或可恢复方式提交。
- 供应商用量必须按稳定调用记录归集,重放和恢复不能生成第二笔调用用量。
- 超过 500 ms 的关键持久层、队列、供应商、MCP、压缩和恢复阶段应记录结构化慢日志;日志不得包含 API Key、 完整 System Prompt、敏感文件签名参数或完整对话正文。
这些是协议的服务质量约束,不会向公开 JSON 注入 billing、内部租约或数据库字段。
26. 安全和隐私注意事项
- 所有接口必须使用 HTTPS。
- API Key 不得放在 URL、SID 或日志中。
- SID 必须用不可预测 GUID 生成,但仍不能作为授权凭证。
- 多 SID History 必须对全部 SID 做同一认证主体的所有权检查,不能返回部分授权结果。
- 文件 URL 必须执行 SSRF 防护,并在重定向和 DNS 变化后重新验证目标。
- History 是真实输入输出投影,可能包含敏感数据;客户端缓存应遵守业务数据保留和加密要求。
DELETE的法律或物理清理周期属于服务端数据治理策略;204表示协议上已不可访问,不代表所有备份立即物理擦除。
27. 限制速查
| 项目 | DASP/1.0 限制 |
|---|---|
| 请求体 | 4 MiB |
model |
200 字符 |
| Input Item 数量 | 1~64 |
| 单请求文件数 | 0~8 |
file_url |
2048 字符,公开 HTTP/HTTPS |
filename |
255 字符 |
system_prompt |
65,536 字符 |
Idempotency-Key |
200 个可见字符;仅无 SID 创建 |
History limit |
1~100,默认 50 |
| History SID 数量 | 1~100 |
| Cursor | 不透明,最大输入长度 256 字符 |
| SSE 保活 | 空闲时约 15 秒发送注释帧 |
模型自身的上下文、输出、工具和多模态限制仍然适用,且可能小于协议上限。
28. 接口级验收清单
一个实现宣称兼容 DASP/1.0 前,至少必须验证:
- 首次无 SID 提交能返回新 GUID,流式和非流式都可用;
- 续写只提交新消息,模型能获得先前有效 Context;
- 同 SID 并发提交只接受一个,其他请求稳定返回
session_busy; - 流中断后 GET 能从头回放并继续,不重复供应商调用和 Usage;
- cancel 不等于断连或删除,并能恢复到明确终态;
- interrupt 只有一个后继,旧部分输出不进入后继 Context;
- edit 只替换最近可见用户文本并剪切下游分支;
- 单 SID 和多 SID History 都按
question_at稳定分页,每条都返回所属 SID; include_system_prompt=true使用独立 Cursor 对消息与 Prompt 变更共同分页,并能还原 Prompt 清空;- History 变化使旧 Cursor 返回
cursor_stale; - System Prompt 可在任意响应状态下设置或移除;当前响应保持接受时快照,后续响应使用新版本;
- 并发 Prompt 更新不会覆盖 Context、产生双响应或导致当前响应进入结果未知;
- 原生多模态和自动 MCP 文件分析路径都能还原 History;
- 工具调用的
call_id可以闭环,重复或悬空结果被拒绝; - reasoning、compaction、file_analysis、usage 和错误均有流式与非流式覆盖;
- Redis 清空、实例重启和执行者故障后仍能从持久层恢复;
- 终态 Usage 与内部稳定调用记录一致,恢复不会重复结算;
- 超过 500 ms 的关键阶段产生脱敏结构化慢日志;
- 未知字段、Item、事件和错误码不会使兼容客户端崩溃。