// developer docs

DomPaw API 参考手册

DomPaw 对外提供一组 OpenAI 兼容的 HTTP 接口。你用熟悉的 Chat Completions 协议发请求,但请求的执行者不是裸模型,而是你在 DomPaw 控制台创建的智能体:它自带人设、技能、长期记忆和安全围栏,运行在你租户独享的实例里。

本手册覆盖全部公开接口:对话补全(含流式)、模型列表、会话管理,以及认证、计费、限流和错误处理。如果你只想尽快跑通第一个请求,请先看快速入门;想在浏览器里直接试调用,请打开 Swagger 文档。

接口一览

方法 路径 用途
POST /v1/chat/completions 与智能体对话(支持流式)
GET /v1/models 列出智能体正在使用的模型
GET /v1/chat/sessions 列出会话
GET /v1/chat/sessions/{chat_id}/messages 读取某个会话的消息历史
DELETE /v1/chat/sessions/{chat_id} 删除某个会话

基础约定

Base URL

https://paw.domai.fun

所有公开接口都在 /v1 前缀下。请求体和响应体均为 UTF-8 编码的 JSON,唯一例外是流式对话,它返回 text/event-stream。

认证

每个请求都要带上租户 API Key:

Authorization: Bearer sk-xxxxxxxxxxxxxxxx
请求头 必填 说明
Authorization 是 Bearer <API Key>,Key 以 sk- 开头
Content-Type 是 请求体为 JSON 时填 application/json
X-Agent-Slug 对话接口必填 目标智能体的 slug;也可以改用请求体的 agent_slug 字段

关于 API Key,有几件事值得提前知道:

  • Key 在控制台创建:登录 DomPaw 控制台,进入「API Keys」页面新建,创建成功后只显示一次完整内容,请立即保存到你自己的密钥管理系统。
  • 一个 Key 归属一个租户。请求会依据 Key 自动路由到该租户的独享实例,不需要额外传租户标识。
  • Key 泄露后请在控制台立即吊销;吊销后的 Key 会立刻返回 401。
  • 认证发生在限流之前:Key 无效时你会拿到 401,而不是 429。

认证失败时的响应:

{
  "error": {
    "message": "Invalid or revoked API key",
    "type": "authentication_error",
    "param": null,
    "code": "invalid_api_key"
  }
}

与 OpenAI API 的差异

DomPaw 复用了 OpenAI 的请求/响应结构,因此官方 SDK 改个 base_url 就能用。但语义上有几处关键差别,忽略它们是最常见的踩坑来源:

项目 OpenAI DomPaw
调用目标 模型 智能体,由 X-Agent-Slug 或 agent_slug 指定
model 字段 决定使用哪个模型 被忽略,实际模型由智能体配置决定;为兼容 SDK 仍需填写
对话上下文 客户端每次传完整 messages 服务端按 session_id 保存上下文,只有最后一条 user 消息会执行
响应 usage 返回 token 统计 目前恒为 null,用量请在控制台账单中查看
finish_reason stop / length / 等 恒为 stop
响应 id 本次补全的唯一 ID 等于本次的 session_id,不能用作去重键
流式首块 delta 含 role delta 不含 role,只含 content
流式收尾 先给 finish_reason 再 [DONE] 直接 data: [DONE],没有收尾块
额外字段 无 响应含 session_id、agent_id;余额偏低时含 credits_warning

核心概念

智能体

智能体是 DomPaw 的执行单元。你在控制台为它配置名称、人设、技能、模型和安全围栏,之后所有 API 调用都指向它。

agent_slug 是智能体的稳定标识(例如 front-desk)。解析顺序是:先按 slug 精确匹配,匹配不到时再按 slug 或名称在你的智能体列表里查找。推荐始终使用 slug——名称改动不会影响调用方。

两种失败情形:

  • 400 agent_not_found:slug 和名称都没匹配上,检查拼写。
  • 400 agent_not_in_qwenpaw:元数据存在,但智能体还没同步到你的运行时实例。在控制台重新保存一次该智能体即可触发同步。

会话与上下文

对话状态保存在服务端,session_id 就是那把钥匙。这带来两个直接后果:

第一,不需要重复传历史。 messages 数组里只有最后一条 role: "user" 的消息会被送入智能体;之前的轮次由服务端依据 session_id 自动接续。传一整段历史不会报错,但也不会生效。

第二,默认的会话键是固定的。 不传 session_id 时,服务端按 default-<租户ID>-<调用方ID>-<智能体slug> 派生。也就是说,同一个 API Key 调用同一个智能体,永远在续接同一段对话。想开一段全新对话,必须显式传一个新的 session_id:

SESSION=$(uuidgen)

curl -sS https://paw.domai.fun/v1/chat/completions \
  -H "Authorization: Bearer $DOMPAW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Agent-Slug: front-desk" \
  -d "{\"session_id\": \"$SESSION\", \"model\": \"dompaw\", \"messages\": [{\"role\": \"user\", \"content\": \"我叫林一,住在 1208 房\"}]}"

curl -sS https://paw.domai.fun/v1/chat/completions \
  -H "Authorization: Bearer $DOMPAW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Agent-Slug: front-desk" \
  -d "{\"session_id\": \"$SESSION\", \"model\": \"dompaw\", \"messages\": [{\"role\": \"user\", \"content\": \"我住哪个房间?\"}]}"

session_id 会做安全字符归一化(非字母数字、-、_、. 的字符会被替换为 _),因此建议直接使用 UUID 或你自己的业务单号。响应里的 session_id 是归一化后的实际值。

独享实例与可用性

每个租户拥有一套独立的智能体运行时。它的状态直接决定 API 是否可用,因此这些错误码值得单独认识:

实例状态 HTTP code 你该做什么
开通中 503 instance_provisioning 首次开通约需 2-5 分钟,按 Retry-After 重试
异常恢复中 503 instance_unhealthy 平台正在自愈,按 Retry-After 退避重试
已停止 403 instance_stopped 到控制台启动实例
已缩容 403 instance_scaled-down 到控制台扩容
已释放 403 instance_released 到控制台重新开通
试用到期 403 instance_trial-expired 升级套餐

503 响应会带 Retry-After 头(单位秒),照着等即可,不要盲目快速重试。

计费与余额

费用按两个维度计算:token 用量和实例运行时。请求前先做余额检查:

  • 余额已为负数:请求被拒绝,返回 400 insufficient_credits,错误信息里带当前余额。
  • 余额低于 100:请求正常执行,但非流式响应会附带一个提示对象:
{
  "credits_warning": {
    "balance": 42,
    "message": "Low credits: 42 remaining. Please top up soon."
  }
}

流式响应不包含该字段。响应的 usage 目前恒为 null,逐条用量明细请在控制台的「用量与账单」中查看。

对话补全

POST /v1/chat/completions

请求体字段

字段 类型 必填 默认值 说明
messages array 是 — 消息数组,至少包含一条 role: "user" 的消息
agent_slug string 否 — 目标智能体;与 X-Agent-Slug 头二选一,必须提供其一
model string 否 null 被忽略,为兼容 SDK 而保留
stream boolean 否 false true 时以 SSE 流式返回
session_id string 否 自动派生 会话键,决定上下文接续到哪一段对话
filter_thinking boolean 否 智能体配置 是否过滤智能体的思考过程输出
filter_tool_messages boolean 否 智能体配置 是否过滤工具调用产生的中间消息

messages 数组元素

字段 类型 说明
role string user / assistant / system;只有最后一条 user 消息会执行
content string 或 array 文本内容;也可以是下面描述的多模态部件数组

多模态内容

最后一条 user 消息的 content 可以是数组,数组会原样交给智能体处理:

部件 字段 说明
text {"type": "text", "text": "..."} 文本片段,多个片段会被拼接
image_url {"type": "image_url", "image_url": {"url": "...", "detail": "auto"}} 图片,url 支持公网地址或 data: base64

若数组中没有 text 部件,请求会以 [multimodal message] 作为文本兜底。

最小示例

curl -sS https://paw.domai.fun/v1/chat/completions \
  -H "Authorization: Bearer $DOMPAW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Agent-Slug: front-desk" \
  -d '{
    "model": "dompaw",
    "messages": [
      {"role": "user", "content": "帮我查一下今天的到店预约"}
    ]
  }'

多模态示例

curl -sS https://paw.domai.fun/v1/chat/completions \
  -H "Authorization: Bearer $DOMPAW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Agent-Slug: front-desk" \
  -d '{
    "model": "dompaw",
    "messages": [
      {
        "role": "user",
        "content": [
          {"type": "text", "text": "这张照片里的房间号是多少?"},
          {"type": "image_url", "image_url": {"url": "https://example.com/room.jpg", "detail": "auto"}}
        ]
      }
    ]
  }'

非流式响应

{
  "id": "1f0c1e7a-6d3b-4a19-9c62-2b7a4d0f3a11",
  "object": "chat.completion",
  "created": 1767225600,
  "model": "qwen-plus",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "今天共有 3 位客人预约到店,最早一位是 14:00 的林一……"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": null,
  "session_id": "1f0c1e7a-6d3b-4a19-9c62-2b7a4d0f3a11",
  "agent_id": "front-desk"
}
字段 类型 说明
id string 本次会话的 session_id(缺失时回退为 chatcmpl-<时间戳>)
object string 固定 chat.completion
created integer Unix 秒级时间戳
model string 智能体当前配置的模型;未配置时为 default
choices array 目前固定返回 1 个元素
choices[].message.content string 智能体的回复文本
choices[].finish_reason string 恒为 stop
usage object 或 null 目前恒为 null
session_id string 归一化后的会话键,下一轮请沿用它
agent_id string 实际执行的智能体标识
credits_warning object 仅当余额低于 100 时出现,含 balance 与 message

流式响应

把 stream 设为 true,服务端返回 SSE。响应头:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
X-Accel-Buffering: no

注意 HTTP 200 在连接你的实例之前就已发出,因此连接后的失败不会体现在状态码上,而是以流内错误事件的形式给出(见下文)。

内容块

每个数据块形如 data: <JSON>,块之间用一个空行分隔:

data: {"id":"msg_01","object":"chat.completion.chunk","created":1767225600,"model":"default","choices":[{"index":0,"delta":{"content":"今天"},"finish_reason":null}]}

data: {"id":"msg_01","object":"chat.completion.chunk","created":1767225601,"model":"default","choices":[{"index":0,"delta":{"content":"共有 3 位"},"finish_reason":null}]}

data: [DONE]
字段 说明
id 运行时消息 ID,同一条回复的多个块保持一致
object 固定 chat.completion.chunk
created Unix 秒级时间戳
model 流式块中恒为 default(与非流式响应不同)
choices[0].delta.content 本次增量文本;拼接所有块即为完整回复
choices[0].finish_reason 恒为 null,流以 data: [DONE] 结束

三个与 OpenAI 不同、客户端需要适配的点:

  • 首块的 delta 不含 role 字段,不要依赖 delta.role 判断消息开始。
  • 没有 finish_reason: "stop" 的收尾块,data: [DONE] 就是终点。
  • 流式块不含 usage,也不含 credits_warning。

工具审批事件

智能体触发需要人工确认的高风险工具时,你会收到一个特殊块:delta.content 为 null,改为携带 approval 对象,顶层多一个 session_id:

{
  "id": "appr_8f2c1d",
  "object": "chat.completion.chunk",
  "created": 1767225620,
  "model": "default",
  "session_id": "1f0c1e7a-6d3b-4a19-9c62-2b7a4d0f3a11",
  "choices": [
    {
      "index": 0,
      "delta": {
        "content": null,
        "approval": {
          "request_id": "appr_8f2c1d",
          "session_id": "1f0c1e7a-6d3b-4a19-9c62-2b7a4d0f3a11",
          "agent_id": "front-desk",
          "tool_name": "delete_record",
          "tool_args": { "id": "bk-2201" },
          "severity": "HIGH",
          "findings_count": 1,
          "findings_summary": "批量删除操作,需人工确认"
        }
      },
      "finish_reason": null
    }
  ]
}

审批需要在控制台完成;解析流时请把 content 为 null 的块当作非文本事件处理,不要直接拼接。

流内错误

上游连接失败或返回错误时,流会写入一个非 OpenAI 格式的事件后关闭:

data: {"type":"error","message":"Connection error"}

因此健壮的客户端需要:检查每块是否含 type === "error";在没有收到 data: [DONE] 就断流时按失败处理。

超时:单次请求的上游处理上限为 300 秒。长任务建议拆分为多轮对话。

流式示例(curl)

curl -N https://paw.domai.fun/v1/chat/completions \
  -H "Authorization: Bearer $DOMPAW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Agent-Slug: front-desk" \
  -d '{
    "model": "dompaw",
    "stream": true,
    "messages": [{"role": "user", "content": "用三句话介绍 DomPaw"}]
  }'

流式示例(Node.js,无依赖)

const res = await fetch('https://paw.domai.fun/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.DOMPAW_API_KEY}`,
    'Content-Type': 'application/json',
    'X-Agent-Slug': 'front-desk',
  },
  body: JSON.stringify({
    model: 'dompaw',
    stream: true,
    messages: [{ role: 'user', content: '用三句话介绍 DomPaw' }],
  }),
});

if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let reply = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  const lines = buffer.split('\n');
  buffer = lines.pop();

  for (const line of lines) {
    if (!line.startsWith('data: ')) continue;
    const payload = line.slice(6).trim();
    if (payload === '[DONE]') continue;

    const evt = JSON.parse(payload);
    if (evt.type === 'error') throw new Error(evt.message);

    const text = evt.choices?.[0]?.delta?.content;
    if (text) {
      reply += text;
      process.stdout.write(text);
    }
  }
}

模型

GET /v1/models

返回你的智能体当前正在使用的模型(去重后),格式与 OpenAI 一致。它反映的是你已经配置了什么,而不是平台可选的模型目录;要切换模型请在控制台修改智能体配置。

curl -sS https://paw.domai.fun/v1/models \
  -H "Authorization: Bearer $DOMPAW_API_KEY"
{
  "object": "list",
  "data": [
    {
      "id": "qwen-plus",
      "object": "model",
      "created": 1767225600,
      "owned_by": "dashscope"
    }
  ]
}
字段 说明
id 模型 ID,与智能体配置中的 active_model.model 一致
created 本次请求的时间戳,不是模型发布时间
owned_by 模型提供方标识,未标注时为 qwenpaw

会话管理

会话数据来自你的独享实例,因此这些接口只能看到本租户的会话。

GET /v1/chat/sessions

按更新时间倒序列出会话。

查询参数 类型 默认 说明
limit integer 50 返回条数上限
agent_slug string 无 仅返回指定智能体的会话
curl -sS "https://paw.domai.fun/v1/chat/sessions?limit=10&agent_slug=front-desk" \
  -H "Authorization: Bearer $DOMPAW_API_KEY"

响应是裸数组(不是 OpenAI 风格的 {object, data} 包装):

[
  {
    "chat_id": "c-9f21",
    "session_id": "1f0c1e7a-6d3b-4a19-9c62-2b7a4d0f3a11",
    "name": "今日到店预约",
    "agent_name": "前台小 Dom",
    "agent_slug": "front-desk",
    "tenant_agent_id": "3f2a…",
    "status": "idle",
    "created_at": "2026-01-01T02:11:00Z",
    "updated_at": "2026-01-01T02:19:30Z"
  }
]
字段 说明
chat_id 会话在运行时中的 ID,读取历史与删除都用它
session_id 发起对话时使用的会话键
name 会话标题,未命名时为 未命名对话
agent_name 智能体显示名
agent_slug 智能体 slug
tenant_agent_id 智能体在本租户内的记录 ID
status 会话状态,通常为 idle
created_at 创建时间(ISO 8601)
updated_at 最后更新时间,列表按它倒序

某个智能体的实例查询失败时会被跳过,不影响其他智能体的结果。

GET /v1/chat/sessions/{chat_id}/messages

读取指定会话的完整消息历史。路径参数用上一步拿到的 chat_id。

curl -sS https://paw.domai.fun/v1/chat/sessions/c-9f21/messages \
  -H "Authorization: Bearer $DOMPAW_API_KEY"
{
  "chat_id": "c-9f21",
  "session_id": "c-9f21",
  "agent_name": "前台小 Dom",
  "agent_slug": "front-desk",
  "status": "idle",
  "messages": [
    { "role": "user", "content": "帮我查一下今天的到店预约", "timestamp": "2026-01-01T02:11:02Z" },
    {
      "role": "assistant",
      "content": "今天共有 3 位客人预约到店……",
      "timestamp": "2026-01-01T02:11:19Z"
    }
  ]
}

多模态消息在这里会被拍平为纯文本(只保留 text 部件,用换行拼接)。会话不存在时返回 404:

{ "error": { "message": "Chat not found", "code": "not_found" } }

DELETE /v1/chat/sessions/{chat_id}

删除会话。该操作会移除运行时中的会话记录,不可恢复。

curl -sS -X DELETE https://paw.domai.fun/v1/chat/sessions/c-9f21 \
  -H "Authorization: Bearer $DOMPAW_API_KEY"
{ "deleted": true, "chat_id": "c-9f21" }

会话不存在时返回与上面相同结构的 404。

错误处理

错误响应结构

除限流和会话 404 外,所有错误都是 OpenAI 风格的信封:

{
  "error": {
    "message": "agent_slug is required (via X-Agent-Slug header or body)",
    "type": "invalid_request_error",
    "param": null,
    "code": "missing_agent"
  }
}
字段 说明
message 人类可读的原因,判断分支请优先用 code,message 可能调整
type invalid_request_error / authentication_error / server_error
param 出错的请求字段,多数情况为 null
code 稳定的机器可读错误码

两处需要兼容的例外:429 的错误体只有 message 和 code;会话接口的 404 只有 message 和 code(没有 type / param)。

错误码总表

HTTP code 含义 可否重试
400 missing_messages 缺少 messages 或它不是数组 修正后重试
400 missing_agent 未提供 X-Agent-Slug 或 agent_slug 修正后重试
400 agent_not_found 智能体不存在 修正后重试
400 agent_not_in_qwenpaw 智能体未同步到运行时实例 控制台重新保存后
400 insufficient_credits 余额为负 充值后
400 hook_blocked 被租户自定义策略拦截 否
401 missing_auth 缺少 Authorization 头,或租户已停用 否
401 invalid_api_key Key 无效或已吊销 否
403 agent_access_denied 当前子用户无权访问该智能体 否
403 instance_stopped 等 实例被停止/缩容/释放/试用到期 控制台处理后
404 not_found 会话不存在 否
429 rate_limit_exceeded 超过通用限流 按 Retry-After
429 chat_rate_limit_exceeded 超过对话限流 按 Retry-After
500 internal_error 实例处理失败或超时 是,指数退避
502 upstream_error 上游返回错误 是
503 instance_provisioning 实例开通中(约 2-5 分钟) 按 Retry-After
503 instance_unhealthy 实例异常,自愈中 按 Retry-After
503 upstream_unreachable 无法连通你的实例 是
503 directus_unavailable 平台元数据服务不可用 是
504 upstream_timeout 上游超时 是

限流

范围 限额 超限 code
所有 /v1 接口 100 次/分钟/租户 rate_limit_exceeded
对话接口 /v1/chat 20 次/分钟/租户 chat_rate_limit_exceeded

对话接口同时受两条限制,因此实际对话吞吐上限是每分钟 20 次。计数按租户维度合并——同一个租户下的所有 Key 与子用户共享配额。

每个响应都带标准限流头:

RateLimit-Limit: 20
RateLimit-Remaining: 17
RateLimit-Reset: 42
Retry-After: 42

RateLimit-Reset 与 Retry-After 都是「距离配额恢复的秒数」。被限流时返回 429:

{
  "error": {
    "message": "Chat rate limit exceeded, please try again later",
    "code": "chat_rate_limit_exceeded"
  }
}

需要更高配额,请在控制台联系我们。

重试建议

  • 可以重试:429、500、502、503、504。使用指数退避(例如 1s、2s、4s,最多 5 次),429 与 503 优先遵循 Retry-After。
  • 不要重试:400、401、403、404。这些是请求本身或账号状态的问题,重试只会重复失败。
  • 对话请求不是幂等的:每次成功执行都会产生新的一轮推理和计费。重试前请确认上一次确实失败(例如没有收到 data: [DONE])。
  • 流式请求建议设置客户端读超时(略大于 300 秒),并在断流时按失败处理。

SDK 接入

DomPaw 兼容 OpenAI Chat Completions 协议,官方 SDK 只需改 base_url 与 api_key,并通过 extra_headers 传入智能体标识。model 会被忽略,但 SDK 要求必填,随便给个占位值即可。

Python

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://paw.domai.fun/v1",
    api_key=os.environ["DOMPAW_API_KEY"],
)

headers = {"X-Agent-Slug": "front-desk"}

resp = client.chat.completions.create(
    model="dompaw",
    messages=[{"role": "user", "content": "帮我查一下今天的到店预约"}],
    extra_headers=headers,
    extra_body={"session_id": "order-desk-2026-01-01"},
)
print(resp.choices[0].message.content)

stream = client.chat.completions.create(
    model="dompaw",
    messages=[{"role": "user", "content": "用三句话介绍 DomPaw"}],
    extra_headers=headers,
    stream=True,
)
for chunk in stream:
    piece = chunk.choices[0].delta.content
    if piece:
        print(piece, end="", flush=True)

Node.js

import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://paw.domai.fun/v1',
  apiKey: process.env.DOMPAW_API_KEY,
});

const resp = await client.chat.completions.create(
  {
    model: 'dompaw',
    messages: [{ role: 'user', content: '帮我查一下今天的到店预约' }],
    session_id: 'order-desk-2026-01-01',
  },
  { headers: { 'X-Agent-Slug': 'front-desk' } }
);

console.log(resp.choices[0].message.content);

SDK 会把 DomPaw 额外返回的字段(session_id、agent_id、credits_warning)当作未知字段处理,取用时可能需要访问原始响应。若你需要这些字段,请直接调用 HTTP 接口。

排错指南

症状 可能原因 处理办法
401,但 Key 确认无误 Key 已吊销,或租户被停用 在控制台核对 Key 状态与租户状态
400 missing_agent 用 SDK 时忘了传 X-Agent-Slug 通过 extra_headers(Python)或第二个参数(Node)传入
400 agent_not_found slug 拼写错误,或用了改名后的显示名 从控制台复制智能体 slug
400 agent_not_in_qwenpaw 智能体尚未同步到运行时 在控制台重新保存该智能体
智能体不记得上一轮说过的话 每次请求都换了 session_id,或换了 Key 固定 session_id,或先查 /v1/chat/sessions 确认
智能体总是接着很久以前的话题 没传 session_id,命中了默认会话 显式传入新的 session_id 开启新对话
503 instance_provisioning 首次开通实例 等 2-5 分钟,按 Retry-After 重试
500 internal_error 实例处理失败或超过 300 秒 简化任务、拆分轮次后重试;持续出现请查看实例状态
流式没有任何输出就断开 流内 type: "error" 事件,或中间层缓冲了 SSE 打印原始 SSE;确认代理层关闭缓冲(已返回 X-Accel-Buffering: no)
流式回复缺少开头或结尾 客户端按 OpenAI 习惯等待 finish_reason 以 data: [DONE] 作为结束信号
usage 是 null 预期行为 到控制台「用量与账单」查看明细
响应里出现 credits_warning 余额低于 100 及时充值,余额为负后请求会被拒绝
429 频繁出现 超过每分钟 20 次对话限额 客户端限速排队,或申请提升配额

相关文档

DomPaw API · OpenAI 兼容接口 · 机器可读规范见 /docs/openapi.yaml