# 豆包智能服务 Simulator Eval 指南

`dbx simulator eval` 用于在本机验证智能服务模拟器后端数据链路。它不验证前端视觉效果，而是确认 **query / history / skill / MCP / manifest / tool result / trace / card_delta** 是否已经跑通。`dbx dev` 不强制以前置 eval 为门禁；只有在需要判断问题是否出在后端出卡链路时，才优先跑 eval。

## 默认评测

在项目目录下直接运行：

```bash
dbx simulator eval --query "<当前轮用户问题>" --verbose
```

默认约定：

- 项目目录是当前目录；不在项目目录执行时才传 `--project-path <project_dir>`。
- Manifest 默认是 `<project>/manifest.yaml`。
- 运行态 Skill 默认是 `<project>/skill/SKILL.md`。
- AppID（manifest 字段名为 `app_key`）、`name` 和 MCP 地址从后端 `/yaml/verify` 返回的 parsed manifest 中读取。
- MCP 地址来自 `manifest.mcp_server.end_point`，可以是 Streamable HTTP `/mcp` 或 SSE `/sse`。

只有默认路径有问题时才加参数：

```bash
dbx simulator eval \
  --project-path <project_dir> \
  --query "<当前轮用户问题>" \
  --history ./history.json \
  --skill ./skill/SKILL.md \
  --manifest ./manifest.yaml \
  --max-step 10 \
  --verbose
```

需要切换 MCP 时，修改 `manifest.mcp_server.end_point`；eval 不再支持用命令行参数覆盖 MCP 地址。

## Eval 链路

eval 是由本机 CLI 发起的一次完整链路测试：

1. 读取项目根目录的 `manifest.yaml` 和 `skill/SKILL.md`。
2. 调后端 `/yaml/verify`，获取 parsed manifest。
3. **由 CLI 当前运行环境连接 MCP server**，执行 `initialize`、`notifications/initialized`、`tools/list`。
4. CLI 把 query、history、skill、manifest、MCP tools 列表发给 Simulator gateway。
5. 模型如果下发 tool call，CLI 会在本机继续调用 MCP `tools/call`，再把 tool result 上报给 gateway。
6. 最后根据 trace、tool report、`card_delta` 和内置 demo expectation 输出 PASS/FAIL。

`manifest.mcp_server.end_point` 不强求是公网 URL。只要 CLI 所在机器能访问即可，例如本机开发时可以是 `http://localhost:8000/mcp` 或 `http://127.0.0.1:<port>/mcp`。反过来，如果 CLI 在远端机器或 CI 里运行，`localhost` 指的是那台机器。

当前 `dbx simulator eval` 能力边界：

- 输入方式：`--demo <name>`、`--case <yaml_or_json>`，或用 flags 临时传 `--app-id`、`--app-name`、`--query`、`--history`、`--skill`、`--manifest`、`--mcp-url`。
- MCP：`--mcp-url` 支持标准 Streamable HTTP `/mcp` 和 SSE `/sse`；CLI 会执行 `initialize`、`notifications/initialized`、`tools/list`，并在模型下发工具调用后执行 `tools/call`。
- Simulator 网关：默认使用 CLI build config 的 `apiBaseUrl`；需要切环境时传 `--gateway-origin <origin>`。
- 调试控制：`--max-step` 调整 Agent 最大循环步数；`--verbose` 展示更多 trace 和工具结果摘要；`--force` 可在 preflight 有 error 时仍继续请求 Simulator。
- 路径解析：`--project-path` 是 dbx 项目目录；相对路径参数按该目录解析。

## Agent 必须遵守

当用户提到这些任务时，优先走 eval，而不是先改 Widget UI：

- 卡片不出、卡片空白、没有 `widget_id`、没有 `card_delta`。
- MCP / Manifest / Skill / query / history 联调。
- 想判断问题在后端数据链路还是前端渲染。

标准动作顺序：

1. 如果当前线程没有明确的 demo PASS 证据，先跑内置 demo：`dbx mcp demo-server --demo milk-tea`，再跑 `dbx simulator eval --demo milk-tea --verbose`。
2. demo PASS 后，确认业务项目根目录已有 `manifest.yaml` 和 `skill/SKILL.md`。
3. 跑业务 eval：`dbx simulator eval --query "<当前轮用户问题>" --verbose`。
4. 业务 eval PASS 后，再进入 `dbx dev` / Web 模拟器 / Widget UI 调试看真实渲染；如果用户只是要求打开 Web 模拟器，直接执行 `dbx dev --mcp-endpoint <mcp_endpoint>`。

不要跳过 demo。demo 的作用是确认 CLI、网关、标准 MCP 客户端、trace 和 card_delta 基准链路正常；没有这个基准时，业务 eval 失败很容易被误判成模型或前端问题。

## 你要先做什么

如果你完全不知道 eval 怎么用，先跑内置 demo，建立一条正确链路的参照。

开第一个终端启动标准 MCP demo server：

```bash
dbx mcp demo-server --demo milk-tea
```

它会自动选择空闲端口，并输出类似：

```text
streamable_http: http://127.0.0.1:53886/mcp
sse: http://127.0.0.1:53886/sse
```

CLI 同时会把实际 URL 写入当前项目 `.dbx/mcp-demo-server/milk-tea.json`。所以第二个终端可以直接运行：

```bash
dbx simulator eval --demo milk-tea --verbose
```

不用手动复制端口。

当前内置 demo：

- `milk-tea`：奶茶搜索 + 下单 + 出卡，推荐作为首个 demo。
- `taxi`：打车搜索 + 下单 + 出卡。

成功时应看到：

```text
Verdict: PASS
Agent Flow
1. MilkTea_search reported
2. MilkTea_order reported

Cards
1. widget_id=tpl.milk_tea_order ...
```

## 看报告

优先按这个顺序看，不要一上来翻全部 trace：

1. `Preflight`
   先修静态配置问题，比如 manifest AppID 是否和请求 AppID 一致、MCP 是否暴露 tools、manifest 是否声明 tool、entity 和 card binding、MCP tool schema 是否兼容。
2. `Agent Flow`
   看模型有没有按 skill 调工具，工具入参是否符合业务语义，tool report 是否成功。开 `--verbose` 时重点看每个工具的 `args` 和 `result`。
3. `Cards`
   看后端是否产出卡片数据，重点看 `widget_id`、`entity_id`、`template`。
4. `Diagnostics`
   按 `fix:` 提示改配置或数据。这里会尽量把问题归因到 manifest、MCP server、MCP schema、tool result、agent 或 card_delta。
5. `Trace`
   开 `--verbose` 时看后端阶段，例如 `query.accepted`、`tool.call.requested`、`tool.call.finished`、`card.delta.emitted`、`reply.finished`。

`Verdict: PASS` 不是只看接口没报错，而是同时满足：

- Preflight 没有 error。
- SSE 流、trace、tool report 没有错误。
- 内置 demo 会额外检查预期工具链和卡片。

## 迁移到业务项目

demo 跑通后，在业务项目根目录直接跑 eval：

```bash
dbx simulator eval --query "我想点一杯适合下午提神的少糖奶茶" --verbose
```

迁移顺序建议：

1. 先替换 MCP server，确认 `tools/list` 和 `tools/call` 正常。
2. 再替换 manifest，确认 AppID、tools、entities、`tool_card_binding` 对齐。
3. 再替换 skill，确认模型能按流程调工具。
4. 最后替换 query 和 history，验证真实用户场景。

`query` 永远表示当前轮用户输入；`history_messages` 只放历史轮次，不要把当前 query 重复放进 history。

## MCP 要求

`dbx simulator eval` 只支持标准 MCP 协议。地址只需要 CLI 当前运行环境可访问，不要求公网 URL。

CLI 会完成：

1. `initialize`
2. `notifications/initialized`
3. `tools/list`
4. 模型下发工具调用后执行 `tools/call`

MCP 工具可以直接返回标准智能服务结果：

```json
{
  "content": [{ "type": "text", "text": "已创建订单" }],
  "structuredContent": {
    "entities": [
      { "entity_type": "product", "product_id": "p1", "name": "订单" }
    ]
  },
  "isError": false
}
```

CLI 会把这个结果转换成 simulator tool report 所需结构。

常见 MCP server 兼容注意：

- MCP server 必须在 `initialize` 和 `tools/list` 阶段不抛异常；如果 CLI 报 `mcp_server_internal_error`，先看 MCP server 终端日志。
- `tools/list` 返回的 input schema 要能被后端消费。Zod 的 `z.number().positive()` 可能生成数值型 `exclusiveMinimum: 0`，当前后端可能不兼容；可优先使用 `z.coerce.number()`，业务范围校验放到工具内部。
- 模型有时会把数字参数以字符串形式传入，例如 `"28"`。如果工具语义允许，MCP server 可以用 `z.coerce.number()` 或手动转换提升容错。

## History 文件

`history.json` 示例：

```json
[
  { "role": "USER", "content": "我想点一杯少糖奶茶" },
  { "role": "ASSISTANT", "content": "可以，我先帮你查一下合适的饮品。" }
]
```

常规业务测试优先只使用 `USER` 和 `ASSISTANT`；`TOOL` 只在你明确需要复现工具历史时使用。

## 常见问题

### MCP 连不上

现象：

```text
MCP server unreachable
```

处理：

- 确认 MCP server 进程还在运行。
- 确认 `manifest.mcp_server.end_point` 是 CLI 所在机器可访问的 `/mcp` 或 `/sse` 地址。
- 确认服务支持标准 MCP 的 `initialize`、`notifications/initialized`、`tools/list`、`tools/call`。
- 如果是内置 demo，先运行 `dbx mcp demo-server --demo milk-tea`，再运行 `dbx simulator eval --demo milk-tea --verbose`。

### MCP server 内部异常

现象：

```text
mcp_server_internal_error
```

处理：

- CLI 已经连上 MCP server，不是网络问题。
- 查看 MCP server 所在终端的 stack trace。
- 重点检查 `initialize` 期间创建 server、注册 tool、生成 schema 的代码是否抛异常。

### MCP tool schema 不兼容

现象：

```text
mcp_tool_schema_incompatible
```

处理：

- 查看诊断里的 `tool_name` 和 schema path。
- 常见原因是 Zod 生成了后端暂不兼容的 JSON Schema，例如 `exclusiveMinimum: 0`。
- 优先把 `z.number().positive()` 改为 `z.coerce.number()`，再在工具函数内部做业务校验。

### MCP 工具返回错误

现象：

```text
mcp_tool_result_error
```

处理：

- 工具已经被模型调用，问题在 MCP `tools/call` 的返回。
- 看 `Agent Flow` 中该工具的 `args` 和 `result`。
- 常见原因是参数类型不匹配，例如工具期望 number，模型传了字符串数字 `"28"`；可在 MCP server 做参数兼容转换。

### manifest 和请求 AppID 不一致

现象：

```text
manifest_app_id_mismatch
```

处理：

- 保持 eval 请求使用的 AppID 与 manifest 的 `app_key` 值一致；默认不要覆盖 AppID。
- 不要用别的 app 的 manifest 混测。

### manifest.tools 缺工具

现象：

```text
manifest_tool_missing_in_mcp
```

处理：

- 会出卡的工具必须同时存在于 MCP `tools/list` 和 `manifest.tools`。
- `manifest.tools.<tool>.output.entity_types` 必须包含 MCP result 里的 `entity_type`。

### 没有 tool_card_binding

现象：

```text
card_binding_missing
```

处理：

在对应 entity 下配置：

```yaml
entities:
  product:
    tool_card_binding:
      Your_order: your.widget_id
```

`your.widget_id` 必须是前端工程实际注册的 widget id。

### MCP result 协议不合法

现象：

```text
miniapp_mcp_result_invalid
```

处理：

- MCP result 必须是合法 JSON。
- 出卡结果必须包含 `content` 和 `structuredContent.entities`。
- `structuredContent.entities[*].entity_type` 必须在 `manifest.entities` 中声明。
- entity 必须包含 manifest 中标记 `is_entity_id: true` 的字段。
- 当前协议中 app 归属由请求和 manifest 绑定，不要依赖 `_meta.app_key`。

### 工具成功但没有卡

现象：

```text
card_not_emitted
```

处理顺序：

1. 在 `Agent Flow` 中确认出卡工具真的被调用并 report 成功。
2. 检查 `manifest.tools.<tool>.output.entity_types`。
3. 检查 `entities.<entity_type>.tool_card_binding.<tool>`。
4. 检查 MCP result 的 `structuredContent.entities` 是否有正确 `entity_type` 和主键。
5. 看 trace 是否有 `card.delta.emitted`。

如果 CLI 已经 PASS，但 Web 不展示卡，问题大概率在前端 widget 注册、模板实现或渲染层。

### 工具调用重复直到 max step

现象：

```text
simulator skill text protocol exceeded max step
```

处理：

- 先临时调大 `--max-step`，确认是否只是步数不足。
- 如果仍重复同一工具，检查 skill 是否要求“查到后必须再查”。
- 给 skill 补上“工具成功后应回答或出卡”的收口规则。
- 检查 MCP result 是否缺少模型收口所需字段，例如订单状态、实体 ID、候选 sku_id / plan_id。

## 业务 eval 原则

- 一次 eval 只验证一个核心意图，不要混太多业务分支。
- 先用 demo 对比，再改业务配置；不要在没有基准的情况下盲调 query 或 skill。
- eval PASS 后再进入 Web 渲染排查。
