# 后端 Agent HTTP/SSE API 参考（完全自定义方案）

> 面向**不用 Flutter SDK、自建 UI** 的集成方（Web / React Native / 原生 / 后端代理）。直连
> ai-boss 后端的 HTTP + SSE 接口，自己渲染对话与业务卡片。
>
> 用 Flutter？直接用《[Agent SDK 对接说明](../../packages/agent-client-flutter/INTEGRATION.md)》更快（它封装了这里的全部）。

---

## 0. 概览

```
你的 UI ──HTTP/SSE──▶ ai-boss 后端 ──连接器──▶ NearBoss / 业务网关
        (本文档)                    (上游 BFF，见下)
```

- **本文档** = 你和 **ai-boss 后端** 的契约（`/api/v1/agent/*`）。
- **上游 BFF**（`docs/外部 api/2026-06-10-aigent-AI对接说明.md`，minipos-aigent）是 ai-boss 后端**再往下**调的 NearBoss 接口——你**不**直接对接它。两者别混。

支持两个专家：营销专家 `promo-designer`、数据专家 `data-analyst`。一个 SSE 入口承载对话 + 流式文本 + 结构化卡片；营销活动的确认 / 生命周期走独立的同步端点。

---

## 1. 通用约定

### 1.1 Base URL
所有路径以 ai-boss 后端根地址为前缀，统一 `/api/v1`。例：`https://api.example.com/api/v1/agent/chat`。

### 1.2 认证头（每个请求都带）

| 头 | 必填 | 说明 |
|---|---|---|
| `X-Merchant-Id` | **是** | 商家 ID（租户隔离）。缺 → 401。 |
| `X-Store-Id` | 否 | 门店 ID（scope；部分工具需要） |
| `X-User-Id` | 否 | 用户 ID（审计） |
| `X-Role` | 否 | `boss` / `store_manager` / `staff` / `ops` / `admin`（默认 `boss`） |
| `X-Plan` | 否 | 套餐（默认 `basic`） |
| `Accept-Language` | 否 | `zh` / `en` / `ar`（影响返回文案语言） |
| `X-Trace-Id` | 否 | 链路追踪 ID；不传则后端自动生成并回显 |
| `Content-Type` | 是 | `application/json` |

> 自定义鉴权（JWT 等）按你的网关约定附加 `Authorization`，但租户隔离仍需 `X-Merchant-Id`。

### 1.3 SSE 帧格式

对话端点返回 `Content-Type: text/event-stream`，每个事件一帧：

```
event: <事件名>
data: <一行 JSON>

```

（事件名 + JSON 数据 + 空行分隔。）以 `final`（或 `plan_final`）事件标记流结束，随后连接关闭。

### 1.4 trace_id
每个请求生成唯一 `trace_id`，回显在响应头 `X-Trace-Id`、SSE `final` 事件、后端日志——排障时提供它即可全链路定位。

### 1.5 统一错误格式（非 SSE 端点）

```json
HTTP <status>
{
  "error_code": "AUTH_INVALID",
  "message": "人类可读说明",
  "kind": "auth|authz|quota|validation|biz|system",
  "trace_id": "…"
}
```

---

## 2. 对话主端点（SSE）

```
POST /api/v1/agent/chat
Content-Type: application/json
Accept: text/event-stream
```

### 2.1 请求体

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `message` | string | **是** | 用户问题 / 指令 |
| `agent_id` | string | 否 | 指定专家（`promo-designer` / `data-analyst`…）。省略 → 路由器按意图自动分流 |
| `conversation_id` | string | 否 | 续聊会话 ID；省略则自动新建并在首帧回传 |
| `entry` | string | 否 | 场景入口 ID（优先级高于 agent_id） |
| `context` | object | 否 | 上下文；营销编辑续聊时带 `{"draft": {…前轮活动草稿}}` 防覆盖 |
| `choose_expert` | bool | 否 | 「换个专家」：返回候选专家列表让用户选，不自动重路由 |
| `exclude_agents` | string[] | 否 | 重路由时避开的专家 |
| `plan_run_id` | string | 否 | 多步规划续跑 ID |
| `resume` | object | 否 | 审批 / 补答续跑参数 |

### 2.2 响应
`HTTP 200` + SSE 流。首帧总是 `conversation`（携 `conversation_id`），随后按对话进展推送 token / 卡片 / 控制事件，以 `final` 收尾。

---

## 3. SSE 事件类型与 payload

| 事件 | 何时 | data 关键字段 | UI 该做什么 |
|---|---|---|---|
| `conversation` | 首帧 | `{conversation_id}` | 存下，用于续聊 |
| `step` | 阶段 / 工具调用 | `{phase, agent, agent_name, tool?, ok?}` | 可选：展示「正在查询…」 |
| `token` | LLM 逐字 | `{text}` | 追加到当前助手气泡 |
| `action_card` | 结构化输出 | 见 §3.1 | 渲染业务卡片 |
| `final` | 流尾（必发） | `{answer, cost_usd?, trace_id}` | 收尾；流结束 |
| `clarify` | 路由低置信 / 缺信息 | `{message, candidates[], kind}` | 渲染候选 chip 让用户选 |
| `routed` / `reroute` | 自动 / 决策转接 | `{agent, reason}` / `{from_agent, reason}` | 可选：提示「已转接到 X」 |
| `approval_required` | 需人工审批 | `{step_id, message, options[]}` | 弹确认；用户选择后带 `resume` 重发 |
| `ask_user` | 需补充信息 | `{step_id, message, input_type?}` | 收集输入后带 `resume` 重发 |
| `plan_created` / `plan_progress` / `plan_final` | 多步规划 | 计划 DAG / 进度 / 汇总 | 可选：渲染步骤树 |
| `memory` / `memory_used` / `memory_write` | 记忆读写 | 已读 / 已用 / 已存的偏好项 | 可选：透明展示「已结合历史偏好」 |
| `error` | 出错 | `{error_code, message, trace_id}` | 渲染错误气泡，不击穿 |

> 还有一组 `react_*` 事件属实验中的 ReAct 路径，当前默认不开启，集成时可忽略。

### 3.1 `action_card` 结构

```json
{
  "id": "card_1",
  "title": "活动建议",
  "body": "建议做一场招牌秒杀…",
  "category": "marketing",
  "indicators": [ {"key": "net_income", "value": 12345.6} ],
  "chart": {
    "type": "line",
    "title": "近7天净收入趋势",
    "unit": "AED",
    "data": [ {"x": "06-09", "y": 1820.5}, … ]
  },
  "form": { "kind": "nb_campaign", "...": "可编辑草稿字段" },
  "buttons": [ {"id":"confirm","label":"确认发布","action":"...","params":{}} ]
}
```

- **数据卡**（数据专家）：`indicators` + 可选 `chart`。数字全部来自接口，**零推算**（后端不自算）。指标域：净收入 / 成功交易 / 退款。
- **营销卡**（营销专家）：`form.kind` 决定卡型——`nb_type_picker`（选活动类型）/ `nb_campaign`（可编辑草稿）/ `nb_campaign_list`（活动列表）/ `nb_campaign_detail`（详情 + 生命周期按钮）。`buttons` / `availableActions` 驱动操作。

### 3.2 完整示例流

```
event: conversation
data: {"conversation_id":"conv_abc"}

event: step
data: {"phase":"start","agent":"promo-designer","agent_name":"营销专家"}

event: token
data: {"text":"根据你的数据，"}

event: action_card
data: {"id":"k1","title":"活动建议","form":{"kind":"nb_campaign"},"buttons":[…]}

event: final
data: {"answer":"根据你的数据，建议做一场招牌秒杀…","cost_usd":0.012,"trace_id":"t_xyz"}
```

---

## 4. 营销确认 / 管理端点（同步，非 SSE）

营销活动遵循 **preview→commit** 两段式：对话里产出草稿卡（preview），用户确认后调以下端点落地。

### 4.1 确认发布 / 存草稿

```
POST /api/v1/agent/nearboss/commit
```

两种 body：

```jsonc
// A. token 直提（未编辑，卡片按钮已带 preview_token）
{ "surface": "campaign", "preview_token": "…", "action": "PUBLISH", "confirmed": true }

// B. 编辑后提交（改了草稿字段）→ 后端 re-preview→commit
{ "surface": "campaign", "action": "PUBLISH",   // PUBLISH | DRAFT
  "params": { "activityType": "FLASH_SALE", "activityName": "周末秒杀", "...": "…" } }
```

`surface` 取值：`campaign`（活动）/ `member_tag`（会员标签）/ `member_delete`（删会员）/ `report_export`（报表导出）。

**成功 200**：
```json
{ "id": "act_1", "status": "PUBLISHED", "activityName": "周末秒杀" }
```

**业务错误**（4xx，`error_code` + `message`）：参见 §6。

### 4.2 活动生命周期动作

```
POST /api/v1/agent/nearboss/campaign/action
```
```json
{ "op": "PAUSE", "id": "act_1", "confirmed": false }
```
- `op`：`PAUSE` / `RESUME` / `TERMINATE`。`TERMINATE` 不可逆，首次返回 `AIGENT_CONFIRM_REQUIRED`，带 `confirmed: true` 重发。
- **成功 200**：整形后的详情卡（`status` / `statusLabel` / `availableActions` / `metrics`）——可直接刷新你的详情卡 UI。

> 哪些动作可用由返回的 `availableActions` 决定（按状态 DRAFT / PENDING / ACTIVE / PAUSED / ENDED），别在 UI 写死。

---

## 5. 历史会话端点

| 方法 | 路径 | 用途 |
|---|---|---|
| `GET` | `/api/v1/conversations?limit=&offset=` | 会话列表（按最近活跃） |
| `GET` | `/api/v1/conversations/{id}/messages?limit=&before_seq=` | 会话消息（游标翻页，含历史 `action_card`） |
| `POST` | `/api/v1/conversations/{id}/archive` | 归档会话 |

---

## 6. 错误码表

| error_code | HTTP | kind | 含义 / 处理 |
|---|---|---|---|
| `AUTH_INVALID` | 401 | auth | 缺 `X-Merchant-Id` |
| `AUTHZ_DENIED` | 403 | authz | 角色无权限 |
| `QUOTA_EXCEEDED` | 429 | quota | 配额用尽（可能降级运行） |
| `VALIDATION` | 400 | validation | 入参 / 字段校验失败 |
| `TOOL_FAILED` | 502 | biz | 下游工具 / API 调用失败 |
| `LLM_UNAVAILABLE` | 503 | system | LLM 不可用（SSE 内以 `error` 事件透出） |
| `BIZ_UNAVAILABLE` | 503 | biz | 业务系统不可用 |
| `SYS_INTERNAL` | 500 | system | 内部错误 |
| `AIGENT_CONFIRM_REQUIRED` | 4xx | validation | 危险操作需二次确认（带 `confirmed:true` 重发） |
| `AIGENT_PREVIEW_EXPIRED` / `TOKEN_EXPIRED` | 409 | validation | 预览失效，重走一次 preview |
| `AIGENT_INVALID_ACTIVITY_TYPE` | 400 | validation | 活动类型非法 |
| `MKT_*` | 400 | biz | 上游活动校验（如「全单折扣须 10–99」），`message` 可直接转述商家 |

---

## 7. 专家与活动类型常量

**agentId**：`promo-designer`（营销专家）· `data-analyst`（数据专家）。省略 `agent_id` 走统一入口自动分流。

**4 类营销活动**（枚举名与直觉相反，以此为准）：

| 枚举 | 含义 | 关键参数 |
|---|---|---|
| `FLASH_SALE` | 秒杀 | 选品 + 秒杀价 |
| `DIRECT_AMOUNT_OFF` | 订单直减 | `directAmount` 1–999 + 门槛 `minOrderAmount` |
| `ORDER_DISCOUNT` | 全单折扣 | `discountValue` 10–99（**实付** X%，9 折=90） |
| `FIRST_ORDER_DISCOUNT` | 首单优惠 | `discountType` + `discountValue` + 门槛 |

---

## 8. cURL 速查

**对话（SSE）**：
```bash
curl -N -X POST https://api.example.com/api/v1/agent/chat \
  -H 'X-Merchant-Id: M123' -H 'X-Store-Id: B1' -H 'X-Role: boss' \
  -H 'Accept-Language: zh' -H 'Content-Type: application/json' \
  -d '{"message":"做个全单9折活动","agent_id":"promo-designer"}'
```

**确认发布**：
```bash
curl -X POST https://api.example.com/api/v1/agent/nearboss/commit \
  -H 'X-Merchant-Id: M123' -H 'Content-Type: application/json' \
  -d '{"surface":"campaign","action":"PUBLISH","params":{"activityType":"ORDER_DISCOUNT","discountValue":90}}'
```

**终止活动（二次确认）**：
```bash
curl -X POST https://api.example.com/api/v1/agent/nearboss/campaign/action \
  -H 'X-Merchant-Id: M123' -H 'Content-Type: application/json' \
  -d '{"op":"TERMINATE","id":"act_1","confirmed":true}'
```

---

## 9. 自建 UI 最小实现提示

浏览器端用 `fetch` + ReadableStream 读 SSE（`EventSource` 不支持 POST + 自定义头，故用 fetch 手动解帧）：

```js
const resp = await fetch(`${BASE}/api/v1/agent/chat`, {
  method: 'POST',
  headers: { 'X-Merchant-Id': 'M123', 'X-Role': 'boss', 'Content-Type': 'application/json' },
  body: JSON.stringify({ message: '本月净收入多少', agent_id: 'data-analyst' }),
});
const reader = resp.body.getReader();
const dec = new TextDecoder();
let buf = '';
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += dec.decode(value, { stream: true });
  const frames = buf.split('\n\n'); buf = frames.pop();      // 留半帧
  for (const f of frames) {
    const ev = f.match(/^event: (.+)$/m)?.[1];
    const data = JSON.parse(f.match(/^data: (.+)$/m)?.[1] || '{}');
    if (ev === 'token') appendText(data.text);
    else if (ev === 'action_card') renderCard(data);
    else if (ev === 'final') finish(data.answer);
  }
}
```

确认 / 动作端点是普通 `POST`，按 §4 发 JSON 即可。

---

文档基于后端真实代码整理（2026-06-15）。SDK 集成见《[Agent SDK 对接说明](../../packages/agent-client-flutter/INTEGRATION.md)》。问题反馈联系 ai-boss 团队。
