AnyAIS

DASP/1.0 · NORMATIVE DOCUMENT

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

配套文档:

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。

浏览器客户端可从响应中读取以下公开响应头:

  • sid
  • has-more
  • next-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 字符,只允许可公开请求的 https URL;
  • 禁止 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 客户端必须重建投影

恢复是“从头回放”,不是“从断点继续”。正确流程:

  1. 清空该 Response ID 的临时输出投影;
  2. 按 item.id upsert;
  3. 按 delta.id 追加到对应 Item;
  4. 用最新 usage 覆盖旧值;
  5. 直到 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,但 reasoning Item 仍可能缺席。
  • 客户端不得把公开摘要描述成完整内部思维链。
  • 非流式响应也在 output 中使用同样的 reasoning Item。

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_file URL 和文本输入。
  • 非多模态路径的 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,避免重复执行

供应商可能返回本文未列出的具体错误码。客户端应:

  1. 优先按已知 code 处理;
  2. 未知 code 按 HTTP 状态类别处理;
  3. 保留完整错误用于诊断;
  4. 不因未知字段或未知错误码崩溃。

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/responses
  • PUT /v1/session/responses
  • POST /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 价格或内部账单

最小流程:

  1. 首次 POST 不传 SID;收到响应头立刻保存 SID。
  2. 流事件按类型处理,未知类型忽略但记录。
  3. 无终态掉线时用同一 SID GET 恢复,并重建当前 Response 投影。
  4. 新问题只提交新增 Input 和本次 Model Config。
  5. 停止按钮调用 cancel,不只关闭网络连接。
  6. 中途补充使用 interrupt,不并发普通 POST。
  7. 编辑先确保空闲,再 PUT;收到 cursor_stale 重启 History 分页。
  8. 多会话展示一次查询全部 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、事件和错误码不会使兼容客户端崩溃。