# HAOEE 自编排 AI 接入与平台能力指南

> 面向 Codex、Cursor、Claude Desktop、Trae、Cherry Studio 等本地或云端智能体。
> 目标：把 5 个 `haoee-*` MCP server 配进智能体后，智能体能理解 Haoee 平台的 Agent、知识库、MCP、Skill、文件、模型与浅层记忆机制，并按 §一 编排引导的 flow 直调 @Tool。

> ⚠️ **上传阻断性前置（ISSUE-004）**：`oss.file.uploadFile` @Tool 已下架，所有上传（Skill 包/知识库文档/Excel/CSV/图片）须走 REST multipart POST `/v1/api/oss/file/uploadFile` + **raw apiKey**（非连接级 apiKey header，需向用户索要真实值）。catalog/flow 的 `invokeMode=MULTIPART_UPLOAD` 已标注"禁止 invokeMcpApi 传文件"，**勿用 `invokeMcpApi("oss.file.uploadFile")` 调**（@Tool 下架必失败）——必须用 `getMcpApiDetail` 拿 `agentScriptPostUrl` 后 curl/Python multipart POST。

<!-- AUTO-GEN:START (gen-mcp-guide.py 生成，勿手改) -->

## 〇、MCP 配置（必读必做，自动生成，勿手改）

> 智能体启动时自检：逐个核对 5 个 haoee-* server 是否都已在配置中；缺哪个由你直接编辑对应客户端配置文件补齐（保留已配项），apiKey 未提供时先向用户索要真实值再写入（勿用 `<你的apiKey>` 占位符落盘），补齐后重启会话再操作 MCP。

**codex `~/.codex/config.toml`**（5 server）：
```toml
[mcp_servers."haoee-agent"]
url = "https://mcp.haoee.com/mcp/service-mcp-platform-agent/mcp"
[mcp_servers."haoee-agent".http_headers]
apiKey = "<你的apiKey>"

[mcp_servers."haoee-knowledge"]
url = "https://mcp.haoee.com/mcp/service-mcp-platform-knowledge/mcp"
[mcp_servers."haoee-knowledge".http_headers]
apiKey = "<你的apiKey>"

[mcp_servers."haoee-mcp"]
url = "https://mcp.haoee.com/mcp/service-mcp-platform-mcp/mcp"
[mcp_servers."haoee-mcp".http_headers]
apiKey = "<你的apiKey>"

[mcp_servers."haoee-skill"]
url = "https://mcp.haoee.com/mcp/service-mcp-platform-skill/mcp"
[mcp_servers."haoee-skill".http_headers]
apiKey = "<你的apiKey>"

[mcp_servers."haoee-oss"]
url = "https://mcp.haoee.com/mcp/service-mcp-platform-oss/mcp"
[mcp_servers."haoee-oss".http_headers]
apiKey = "<你的apiKey>"

```

**Cursor `~/.cursor/mcp.json` / Claude Desktop `claude_desktop_config.json`**（同结构，5 server + apiKey header）：
```json
{
  "mcpServers": {
    "haoee-agent": {
      "url": "https://mcp.haoee.com/mcp/service-mcp-platform-agent/mcp",
      "headers": { "apiKey": "<你的apiKey>" }
    },
    "haoee-knowledge": {
      "url": "https://mcp.haoee.com/mcp/service-mcp-platform-knowledge/mcp",
      "headers": { "apiKey": "<你的apiKey>" }
    },
    "haoee-mcp": {
      "url": "https://mcp.haoee.com/mcp/service-mcp-platform-mcp/mcp",
      "headers": { "apiKey": "<你的apiKey>" }
    },
    "haoee-skill": {
      "url": "https://mcp.haoee.com/mcp/service-mcp-platform-skill/mcp",
      "headers": { "apiKey": "<你的apiKey>" }
    },
    "haoee-oss": {
      "url": "https://mcp.haoee.com/mcp/service-mcp-platform-oss/mcp",
      "headers": { "apiKey": "<你的apiKey>" }
    }
  }
}
```

**自检流程**：
1. 识别你是哪个客户端（codex→`~/.codex/config.toml`；cursor→`~/.cursor/mcp.json`；claude desktop→`claude_desktop_config.json`）。
2. 逐个核对 5 个 haoee-* server 是否已在配置中（haoee-agent / haoee-knowledge / haoee-mcp / haoee-skill / haoee-oss）。
3. 全部已配且 apiKey 非占位 → 跳过本节，直接按 §一 flow 操作。
4. 有缺失 → 由你直接编辑该配置文件，补齐缺失的 server 段，保留已配项不动。
5. apiKey 未提供时，先向用户索要真实值再写入，勿用 `<你的apiKey>` 占位符落盘。
6. 写完提示用户重启客户端，重启后确认 5 个 server 工具全可见再继续。未配齐前禁止拼接业务 REST URL。


---

## 一、编排引导（自动生成，勿手改）

### 1.1 跨模块 flow 全步骤（按任务选 flow，照步骤调 @Tool）

#### agent.create — 创建并发布智能体（仅骨架，不含知识库/Skill）

1. agent.bot.findCategoryList（必填: 无）→ data[].categoryId
2. model.base.list（必填: 无）→ data[].modelId → data[].modelName
3. agent.bot.create（必填: botName, categoryIds, botMode）→ data.id
4. agent.bot.updateGraph（必填: id, nodes, edges）→ 无
5. agent.bot.publish（必填: botId, botName, categoryIds, isHide）→ 无
6. agent.bot.getPublishedDetail（必填: botId）→ data.releaseKey

#### agent.chatTest — 已有智能体对话/调试（发布对话测试）

1. agent.bot.pageListBySelf（必填: 无）→ data.records[].id
2. agent.bot.getPublishedDetail（必填: botId）→ data.releaseKey
3. agent.chat.publish（必填: releaseKey, ulid, query）→ 无

#### agent.createFull — 创建完整智能体（骨架+知识库+可选Skill/MCP）

1. phase1: agent.create（创建智能体骨架（Prompt/开场白/模型/发布），必经）— 详见本节该 flowId 条目
2. phase2: knowledge.createUploadAndBindAgent（创建知识库、导入内容并绑定（按 knowledgeType 分流），必经）— 详见本节该 flowId 条目
3. phase3: skill.importAndBindAgent（导入 Skill 并绑定（工单/转人工等），可选；跳过条件：用户未提供 skill 包 URL/文件，或设计中的 create_ticket 等仅为规划、暂无可导入包）— 详见本节该 flowId 条目
4. phase4: mcp.createAndBindAgent（创建 MCP 服务并绑定，可选；跳过条件：用户未要求外部 MCP/插件，或无可用的 MCP 配置 JSON）— 详见本节该 flowId 条目

#### skill.importAndBindAgent — 导入 Skill 并绑定到智能体

1. REST:oss.file.uploadFile（必填: file）→ data.id
2. skill.skills.importPackage（必填: ossFileId）→ data.skillId → data.code → data.name
3. agent.bot.getDraftDetail（必填: botId）→ data.nodes → data.edges → data.feature
4. agent.bot.updateGraph（必填: id, nodes[].data.skills, edges）→ 无
5. agent.bot.publish（必填: botId, botName, categoryIds, isHide）→ 无
6. agent.bot.getDraftDetail（必填: botId）→ 无
7. skill.skills.detail（必填: skillId）→ 无

#### mcp.createAndBindAgent — 创建 MCP 并绑定到智能体

1. mcp.server.validate（必填: json）→ 无
2. mcp.server.create（必填: name, descr, json）→ data.mcpId
3. agent.bot.getDraftDetail（必填: botId）→ data.nodes → data.edges → data.feature
4. agent.bot.updateGraph（必填: id, nodes[].data.mcps）→ 无
5. agent.bot.publish（必填: botId, botName, categoryIds, isHide）→ 无

#### knowledge.createUploadAndBindAgent — 创建知识库（TEXT）、导入文档并绑定智能体（上传≠导入）

1. knowledge.base.save（必填: name, description, knowledgeType）→ data.id
2. REST:oss.file.uploadFile（必填: file, folderPath）→ data.id
3. knowledge.doc.batchCreate（必填: knowledgeId, fileIdList）→ 无
4. knowledge.doc.preview（必填: knowledgeBaseId, fileIdList）→ 无
5. knowledge.doc.confirm（必填: knowledgeBaseId）→ data.batchNo → data.jobId
6. knowledge.processingJob.getByBatch（必填: batchNo）→ data.status → data.overallPercent
7. agent.bot.getDraftDetail（必填: botId）→ data.nodes → data.edges → data.feature
8. agent.bot.updateGraph（必填: id, nodes[].data.knowledges）→ 无
9. agent.bot.publish（必填: botId, botName, categoryIds, isHide）→ 无

#### knowledge.tableImportAndBindAgent — 创建知识库（TABLE）、表格 wizard 导入并绑定智能体

1. knowledge.base.save（必填: name, description, knowledgeType）→ data.id
2. REST:oss.file.uploadFile（必填: file, folderPath）→ data.id
3. knowledge.structuredImport.session.tableFile（必填: knowledgeId, ossFileId）→ data.sessionId
4. knowledge.structuredImport.session.listSheets（必填: sessionId）→ data[].sheetName
5. knowledge.structuredImport.table.detectSchema（必填: sessionId, sheetName）→ data.columns
6. knowledge.structuredImport.table.saveSchema（必填: sessionId, sheetName, columns）→ 无
7. knowledge.structuredImport.table.preview（必填: sessionId, sheetName）→ 无
8. knowledge.structuredImport.table.confirm（必填: sessionId, sheetName）→ data.batchNo → data.tasks[].taskId
9. knowledge.structuredImport.task.get（必填: taskId）→ data.status → data.percent
10. agent.bot.getDraftDetail（必填: botId）→ data.nodes → data.edges → data.feature
11. agent.bot.updateGraph（必填: id, nodes[].data.knowledges）→ 无
12. agent.bot.publish（必填: botId, botName, categoryIds, isHide）→ 无

#### knowledge.imageImportAndBindAgent — 创建知识库（IMAGE）、图片 wizard 导入并绑定智能体

1. knowledge.base.save（必填: name, description, knowledgeType）→ data.id
2. REST:oss.file.uploadFile（必填: file, folderPath）→ data.id
3. knowledge.structuredImport.session.imageFiles（必填: knowledgeId, fileIdList）→ data.sessionId
4. knowledge.structuredImport.image.saveAnnotation（必填: sessionId, annotationMode）→ 无
5. knowledge.structuredImport.image.start（必填: sessionId）→ data.taskId → data.batchNo
6. knowledge.structuredImport.task.get（必填: taskId）→ data.status → data.percent
7. knowledge.structuredCatalog.image.page（必填: knowledgeId, annotationStatus）→ data.rows[].imageId
8. knowledge.structuredImport.image.annotateItem（必填: imageId, description）→ 无
9. agent.bot.getDraftDetail（必填: botId）→ data.nodes → data.edges → data.feature
10. agent.bot.updateGraph（必填: id, nodes[].data.knowledges）→ 无
11. agent.bot.publish（必填: botId, botName, categoryIds, isHide）→ 无

---

### 1.2 接口描述（自动生成：apiRef + 用途 + 必填参数）

**智能体 (agent)**

| apiRef | 用途 | 必填参数 |
|---|---|---|
| `agent.bot.create` | 创建智能体 | botName, categoryIds, botMode |
| `agent.bot.update` | 修改智能体基础信息 | botId |
| `agent.bot.updateGraph` | 保存智能体整图（编排） | id, bodyJson（见 §1.3 骨架） |
| `agent.bot.getDraftDetail` | 获取智能体草稿详情 | botId |
| `agent.bot.getPublishedDetail` | 获取智能体发布详情 | botId |
| `agent.bot.publish` | 发布智能体 | botId, botName, categoryIds, isHide |
| `agent.bot.pageListBySelf` | 个人智能体列表分页 | - |
| `agent.bot.pageListByPub` | 市场智能体列表分页 | - |
| `agent.bot.findCategoryList` | 查询标签列表 | - |
| `agent.bot.delete` | 删除智能体 | botId |
| `agent.bot.deleteIds` | 批量删除智能体 | botIds |
| `agent.bot.copy` | 复制智能体 | sourceBotId |
| `agent.bot.unpublish` | 下架智能体 | botId |
| `agent.chat.publish` | 智能体对话（SSE，MCP 唯一开放入口） | releaseKey, ulid, query |
| `agent.chat.stop` | 停止对话流 | ulid |
| `agent.chat.getIntroduction` | 查询开场白 | botId |
| `agent.count.runStatistics` | 运行统计 | botId |
| `agent.count.dataStatistics` | 数据统计 | botId |
| `model.base.list` | 获取可用模型列表 | - |
| `agent.bot.findBotsByKnowledgeBaseId` | 按知识库查智能体 | knowledgeBaseId |
| `agent.bot.findBotsByMcpId` | 按 MCP 查智能体 | mcpId |
| `agent.bot.findBotsBySkillId` | 按技能查智能体 | skillId |
| `agent.chat.clearConversationHistory` | 清空会话历史 | botId |
| `agent.chat.conversationHistoryByCursor` | 查会话历史（游标分页） | botId |
| `agent.chat.deleteMessage` | 删除会话消息 | chatPairIds |
| `agent.chat.debug` | 调试对话（SSE 流式） | botId, ulid, query |

**知识库（路径根=/{knowledgeId}；knowledgeType 决定链路：TEXT 走 doc/batchCreate→preview→confirm→轮询 Job；TABLE/IMAGE 走 structured-import wizard） (knowledge)**

| apiRef | 用途 | 必填参数 |
|---|---|---|
| `knowledge.base.save` | 创建知识库 | name, description, knowledgeType |
| `knowledge.base.pageQuery` | 分页查询知识库 | - |
| `knowledge.base.list` | 查询知识库列表 | - |
| `knowledge.base.update` | 更新知识库 | id, name |
| `knowledge.base.batchDelete` | 批量删除知识库 | idList |
| `knowledge.doc.batchCreate` | 【导入第1步·登记】批量创建知识库文档（消除「未导入」） | bodyJson（见 §1.3 骨架） |
| `knowledge.doc.batchDelete` | 批量删除知识库文档 | bodyJson（见 §1.3 骨架） |
| `knowledge.doc.listFolder` | 浏览知识库目录（文件/子文件夹列表） | where_id |
| `knowledge.doc.preview` | 【导入第2步·分片预览】知识库文档预览（分段） | bodyJson（见 §1.3 骨架） |
| `knowledge.doc.confirm` | 【导入第3步·触发流水线】确认并触发解析流水线 | knowledgeBaseId |
| `knowledge.doc.reprocess` | 单文档重处理（换文件或改配置后重跑流水线） | knowledgeId, docId |
| `knowledge.doc.recall` | RAG 知识库召回 | bodyJson（见 §1.3 骨架） |
| `knowledge.chunk.save` | 新增知识库分片 | knowledgeId, docId, chunkIndex, content |
| `knowledge.chunk.update` | 更新知识库分片 | id, knowledgeId, docId, chunkIndex, content |
| `knowledge.chunk.delete` | 删除知识库分片 | id |
| `knowledge.chunk.get` | 查询知识库分片详情 | id |
| `knowledge.chunk.pageQuery` | 分页查询知识库分片 | - |
| `knowledge.processingJob.getByBatch` | 按批次号查处理任务（confirm/structured-import 后轮询） | batchNo |
| `knowledge.processingJob.listActive` | 查询知识库下活跃处理任务 | knowledgeId |
| `knowledge.processingJob.getById` | 按 jobId 查处理任务 | jobId |
| `knowledge.structuredImport.session.tableFile` | 【表格 wizard·建会话】按 OSS 文件建表格导入会话 | knowledgeId, ossFileId |
| `knowledge.structuredImport.session.tableApi` | 【表格 wizard·建会话】按 API URL 建表格导入会话 | knowledgeId, apiUrl |
| `knowledge.structuredImport.session.imageFiles` | 【图片 wizard·建会话】按 OSS 图片列表建图片导入会话 | knowledgeId, fileIdList |
| `knowledge.structuredImport.session.imageFilesRemove` | 【图片 wizard·会话内】移除会话中部分图片 | sessionId, ossFileIds |
| `knowledge.structuredImport.session.listSheets` | 【表格 wizard·决策点1】列出会话内所有 Sheet | sessionId |
| `knowledge.structuredImport.session.get` | 查会话详情（含图片文件列表） | sessionId |
| `knowledge.structuredImport.table.detectSchema` | 【表格 wizard·决策点2】探测单 Sheet 表结构 | sessionId, sheetName |
| `knowledge.structuredImport.table.detectAllSchemas` | 【表格 wizard·决策点2 批量】探测全部 Sheet 表结构 | sessionId |
| `knowledge.structuredImport.table.saveSchema` | 【表格 wizard·决策点3】保存列定义（codex 调整列类型与索引） | bodyJson（见 §1.3 骨架） |
| `knowledge.structuredImport.table.getSchema` | 查询单 Sheet 已保存的列定义 | sessionId, sheetName |
| `knowledge.structuredImport.table.preview` | 【表格 wizard·决策点4·可选】预览 Sheet 数据行 | sessionId, sheetName |
| `knowledge.structuredImport.table.confirm` | 【表格 wizard·触发入库】确认 Sheet 导入（返回 batchNo+tasks） | sessionId, sheetName |
| `knowledge.structuredImport.image.saveAnnotation` | 【图片 wizard·决策点】设置标注方式 | sessionId, annotationMode |
| `knowledge.structuredImport.image.getAnnotation` | 查询会话标注方式配置 | sessionId |
| `knowledge.structuredImport.image.annotateItem` | 【图片 wizard·MANUAL】单张图片补描述 | imageId, description |
| `knowledge.structuredImport.image.start` | 【图片 wizard·触发入库】启动图片标注与入库（返回 taskId+batchNo） | sessionId |
| `knowledge.structuredImport.task.get` | 查结构化导入任务进度（轮询） | taskId |
| `knowledge.structuredImport.task.cancel` | 取消结构化导入任务 | taskId |
| `knowledge.structuredCatalog.image.page` | 【IMAGE·图片分页】查询已入库图片列表（取 imageId） | knowledgeId |
| `knowledge.structuredCatalog.image.removeItem` | 【IMAGE·删单张】删除单张已入库图片（image item+ES+向量） | imageId |
| `knowledge.structuredCatalog.image.batchRemove` | 【IMAGE·批量删】批量删除已入库图片（image item+ES+向量） | knowledgeId, imageIds |
| `knowledge.structuredCatalog.image.annotate` | 【IMAGE·详情页修改】编辑图片描述/文件名 | imageId |
| `knowledge.structuredCatalog.image.autoDescribe` | 【IMAGE·AI 生成描述】Vision 自动生成描述（不落库） | imageId |
| `knowledge.structuredCatalog.album.list` | 【IMAGE·album 列表】查询已发布图片批次 | knowledgeId |
| `knowledge.structuredCatalog.album.detail` | 【IMAGE·album 详情】查图片批次详情（含图片项列表） | albumId |
| `knowledge.structuredCatalog.table.list` | 【TABLE·表列表】查询已发布表格 Catalog | knowledgeId |
| `knowledge.structuredCatalog.table.detail` | 【TABLE·表详情】查表 Catalog 详情（含列定义） | tableId |
| `knowledge.structuredCatalog.table.overview` | 【TABLE·overview】表 overview（单库单索引元数据） | knowledgeId |
| `knowledge.structuredCatalog.table.rows.page` | 【TABLE·行查询】分页查询表格行 | knowledgeId |
| `knowledge.structuredCatalog.table.rows.add` | 【TABLE·新增行】手动新增表格行 | knowledgeId, cells |
| `knowledge.structuredCatalog.table.rows.update` | 【TABLE·编辑行】编辑表格行 | knowledgeId, rowId, cells |
| `knowledge.structuredCatalog.table.rows.remove` | 【TABLE·删单行】删除表格行 | knowledgeId, rowId |
| `knowledge.structuredCatalog.table.rows.batchRemove` | 【TABLE·批量删行】批量删除表格行 | knowledgeId, rowIds |

**MCP 服务管理 (mcp)**

| apiRef | 用途 | 必填参数 |
|---|---|---|
| `mcp.server.create` | 新增MCP服务 | bodyJson（见 §1.3 骨架） |
| `mcp.server.detail` | 获取MCP详情 | mcpId |
| `mcp.server.page` | 分页查询MCP列表 | - |
| `mcp.server.update` | 更新MCP服务（含启停 status） | bodyJson（见 §1.3 骨架） |
| `mcp.server.delete` | 删除MCP服务 | mcpId |
| `mcp.server.validate` | 校验MCP配置JSON | json |
| `mcp.server.batchDelete` | 批量删除MCP服务 | mcpIds |

**技能（Skill） (skill)**

| apiRef | 用途 | 必填参数 |
|---|---|---|
| `skill.skills.importPackage` | 导入 Skill 包（一步入库并发布） | ossFileId |
| `skill.skills.create` | 创建 INLINE 技能 | bodyJson（见 §1.3 骨架） |
| `skill.skills.update` | 更新 Skill 元信息 | skillId, name |
| `skill.skills.toggleStatus` | 启用/禁用 Skill | skillId, status |
| `skill.skills.delete` | 删除 Skill | skillId |
| `skill.skills.batchDelete` | 批量删除 Skill | skillIds |
| `skill.skills.page` | 分页查询 Skill 列表 | - |
| `skill.skills.detail` | 获取 Skill 详情 | skillId |
| `skill.skills.detailByCode` | 按 code 获取 Skill 详情 | code |

**文件存储（OSS）（知识库内文件均以 /{knowledgeId} 为路径根，与上传 folderPath 一致） (oss)**

| apiRef | 用途 | 必填参数 |
|---|---|---|
| `oss.file.getPresignedDownloadUrl` | 按 fileId 获取预签名下载 URL（上传后轮询） | fileId |
| `oss.file.ensureFolderForUser` | 幂等确保用户目录存在（ForUser） | folderName |
| `oss.file.downloadFile` | 下载单个文件（知识库文档 / Skill 导出） | fileId |
| `oss.file.createFolder` | 创建文件夹（知识库子目录等） | folderName |
| `oss.file.moveResources` | 移动文件或文件夹（知识库内跨子目录） | targetFolderPath |

---

### 1.3 复杂接口 bodyJson parameters 骨架（9 个，构造 bodyJson 用）

- `agent.bot.updateGraph` (保存智能体整图（编排）): { id, feature, feature.logicPrompt(≤8000), feature.prologue(≤1000), feature.prologueList, feature.inputType(enum:['0', '1']), feature.planEnable(enum:['0', '1']), nodes, nodes[].id, nodes[].position, nodes[].position.x, nodes[].position.y, nodes[].data.agentName(≤50), nodes[].data.modelId, nodes[].data.modelName, nodes[].data.prompt(≤3000), nodes[].data.description(≤8000), nodes[].data.contextRound, nodes[].data.maxTokens, nodes[].data.temperature, nodes[].data.enableThink(enum:['0', '1']), nodes[].data.evalEnable(enum:['0', '1']), nodes[].data.evalMaxTimes, nodes[].data.evalPrompt(≤1000), nodes[].data.isAdvice(enum:['0', '1']), nodes[].data.openPrompt(enum:['0', '1']), nodes[].data.advicePrompt(≤1000), nodes[].data.knowledges, nodes[].data.skills, nodes[].data.mcps, edges }
- `knowledge.doc.batchCreate` (【导入第1步·登记】批量创建知识库文档（消除「未导入」）): { knowledgeId, fileIdList, syncMode(enum:['FULL_SYNC', 'INCREMENTAL_ADD']), processConfig, processConfig.parseMode(enum:['FAST', 'PRECISE']), processConfig.extractImage, processConfig.extractTable, processConfig.scanOcr, processConfig.splitterType(enum:['AUTO', 'PARAGRAPH', 'LENGTH', 'SEPARATOR', 'HIERARCHICAL']), processConfig.splitterSeparator(≤100), processConfig.chunkSize, processConfig.chunkOverlap, processConfig.maxParagraphDepth, processConfig.indexSize, processConfig.headingLevel }
- `knowledge.doc.batchDelete` (批量删除知识库文档): { knowledgeId, fileIdList, fileIdList[].fileId, fileIdList[].isDir }
- `knowledge.doc.preview` (【导入第2步·分片预览】知识库文档预览（分段）): { knowledgeBaseId, fileIdList, parseMode(enum:['FAST', 'PRECISE']), extractImage, extractTable, scanOcr, splitterType(enum:['AUTO', 'PARAGRAPH', 'LENGTH', 'SEPARATOR', 'HIERARCHICAL']), splitterSeparator(≤100), chunkSize, chunkOverlap, maxParagraphDepth, indexSize, headingLevel, score, topK, allowRerank }
- `knowledge.doc.recall` (RAG 知识库召回): { query, knowledgeId, topK, allowRerank, debug, tokenBudget, conversationHistory, conversationHistory[].role(enum:['user', 'assistant']), conversationHistory[].content }
- `knowledge.structuredImport.table.saveSchema` (【表格 wizard·决策点3】保存列定义（codex 调整列类型与索引）): { sessionId, sheetName, sheetDisplayName, headerRow, dataStartRow, tableDisplayName, columns, columns[].columnName, columns[].fieldType(enum:['STRING', 'NUMBER', 'INTEGER', 'IMAGE']), columns[].semanticType(enum:['DATE', 'DURATION', 'MONEY', 'NUMBER', 'ENUM', 'TEXT', 'ID']), columns[].alias, columns[].indexed, columns[].ordinal }
- `mcp.server.create` (新增MCP服务): { name(≤30), descr(≤200), json(≤4000), category(enum:['plugin', 'mcp']), endpoint, headers, type(enum:['SSE', 'STDIO', 'STREAMABLE_HTTP']), status(enum:['0', '1']), validateStatus(enum:['0', '1']), mcpId, version }
- `mcp.server.update` (更新MCP服务（含启停 status）): { mcpId, name(≤50), descr(≤200), json(≤4000), status(enum:['0', '1']), validateStatus(enum:['0', '1']), type(enum:['SSE', 'STDIO', 'STREAMABLE_HTTP']), category(enum:['plugin', 'mcp']), endpoint, headers, version }
- `skill.skills.create` (创建 INLINE 技能): { name(≤50), description(≤1000), iconUrl(≤512), visibility(enum:['PRIVATE', 'WORKSPACE', 'PUBLIC']), language, image, script(≤65536), entryCommand, inputSchema, defaultTimeoutSeconds, defaultCpu, defaultMemory, autoPublish }

---

### 1.4 全局规则

- 鉴权：apiKey 走连接级 `apiKey` header（网关注入 user_id）；客户端不传 apiKey 参数（wrapper `McpApiKeyInjectingToolCallback` 从 header 注入到 ToolContext，@Tool 方法 apiKey 参从注入取，tools/list schema 不含 apiKey）。
- 文件上传：`oss.file.uploadFile` @Tool 已下架，走 REST multipart POST `/v1/api/oss/file/uploadFile`（?apiKey=&folderPath=）获 fileId（详见 §12）。文件大小按类型限制：音频/视频≤200MB，文档/表格/图片/其他≤20MB。
- SSE（agent.chat.publish / agent.chat.debug）：响应聚合为 apiRef/eventCount/progress/content/finalResult，流式等完成。
- 轮询（knowledge.processingJob.getByBatch / knowledge.structuredImport.task.get）：轮询到 COMPLETED/SUCCESS 再进下一步。
- 跨模块调用：tools/list 返的 @Tool name 为下划线形式（apiRef 点号→下划线，如 `agent.bot.create` → `agent_bot_create`）；客户端以 tools/list 实际返回的 name 为准调用，照 §1.1 flow 步骤跨模块调。

<!-- AUTO-GEN:END -->

---

## 1. 快速结论

`haoee-agent / haoee-knowledge / haoee-mcp / haoee-skill / haoee-oss` 是 Haoee 平台开放能力按业务模块拆分的 5 个 MCP server。每个 server 配连接级 `apiKey` header 后，本地智能体可以：

- 创建、修改、发布、复制、下架、删除 Haoee 平台 Agent。
- 读取可用模型列表，并为 Agent 节点选择合适模型。
- 调用已发布 Agent 做流式对话。
- 创建 TEXT/TABLE/IMAGE 知识库，完成文档、表格、图片导入，并绑定到 Agent。
- 创建平台 MCP 服务，并绑定到 Agent 节点。
- 上传 Skill 包，导入 Skill，绑定到 Agent 节点。
- 管理文件、文件夹、预签名下载链接。

当前功能模块：

| 模块 | MCP server | @Tool 范围 |
|---|---|---|
| 智能体 | haoee-agent | agent.bot/chat/count + model |
| 知识库 | haoee-knowledge | base/doc/structuredImport/structuredCatalog |
| MCP 服务 | haoee-mcp | mcp.server.* |
| Skill | haoee-skill | skill.skills.* |
| 文件存储 | haoee-oss | oss.file.* |

最重要的使用原则：

1. 先看 §〇 MCP 配置必读，配好 5 个 `haoee-*` server（`apiKey` 走连接级 header）。
2. 多步业务按 §1.1 flow 全步骤直调 @Tool（@Tool name = apiRef，如 `agent.bot.create`）。
3. 普通接口直调对应 @Tool；流式对话用 `agent.chat.publish`（SSE）。
4. 本地文件上传走 REST multipart POST `/v1/api/oss/file/uploadFile`（`oss.file.uploadFile` @Tool 已下架，详见 §12），不能放进 bodyJson。
5. `apiKey` 代表调用身份（连接级 header），不要另查数字用户 ID。
6. GET、query、path 参数也写进对应 @Tool 的 bodyJson / @ToolParam，不要自己拼业务接口路径。

---

## 2. MCP 配置

5 server 的详细配置（codex `~/.codex/config.toml`、Cursor `~/.cursor/mcp.json`、Claude Desktop `claude_desktop_config.json`）与自检流程见上方 §〇（自动生成，随 catalog 变更同步，勿手改）。本节只补充通用建议：

- `apiKey` 建议放环境变量 `HAOEE_API_KEY`，不要提交到仓库：

```bash
export HAOEE_API_KEY="你的 apiKey"
```

然后在客户端配置里引用该变量。

- URL 形式为 `https://mcp.haoee.com/mcp/service-mcp-platform-{agent|knowledge|mcp|skill|oss}/mcp`。
- 按场景可只连子集：智能客服连 agent + knowledge + oss；知识库管理连 knowledge + oss；全功能连 5 个。
- 未配好 `haoee-*` server 前，禁止拼接业务 REST URL 直调平台接口。

---

## 4. 推荐业务链路

8 条跨模块 flow 的完整步骤见 §1.1（自动生成，按任务选 flow 照步骤调 @Tool）。本节只保留每条 flow 的**决策点**与 **bodyJson 示例**，步骤罗列不再重复。

| flow id | 场景 | 关键决策点 |
|---|---|---|
| agent.create | 创建并发布 Agent 骨架 | botMode 固定 2；节点 id 用 36 位 UUID；模型来自 model.base.list |
| agent.chatTest | 已有 Agent 对话/调试 | releaseKey 来自 agent.bot.getPublishedDetail；chatKey=终端用户 key（非 releaseKey） |
| agent.createFull | 创建完整 Agent（复合） | 按 phase 顺序；phase2 知识库按 knowledgeType 分流 TEXT/TABLE/IMAGE |
| skill.importAndBindAgent | 导入 Skill 并绑定 | 绑定用 skillId/code/name，勿用 ossFileId |
| mcp.createAndBindAgent | 创建 MCP 并绑定 | json 须为 mcpServers 单服务对象；descr 禁止换行 |
| knowledge.createUploadAndBindAgent | TEXT 知识库+文档导入+绑定 | 上传≠导入；须 batchCreate→preview→confirm→轮询 Job SUCCESS 再绑定 |
| knowledge.tableImportAndBindAgent | TABLE 知识库+表格 wizard+绑定 | 决策点：选 Sheet / 定表头 / 定列类型与索引列 |
| knowledge.imageImportAndBindAgent | IMAGE 知识库+图片 wizard+绑定 | MANUAL 须 start 入库后取 imageId 逐张补 description |

### agent.create — 创建并发布 Agent 骨架

适合：只要一个可对话 Agent，不需要知识库、Skill、MCP。

决策点：`botMode` 固定传 2；编排图节点 id 用 36 位 UUID（勿用 start/agent1 短名）；`agentNode.data.modelId/modelName` 必须来自 `model.base.list`，勿臆造。图须含 1 个 startNode + ≥1 个 agentNode + startNode→agentNode 边。

### agent.chatTest — 已有 Agent 试聊 / 调试

适合：用户说"问答、试试、对话、调试、验证效果"。

`agent.chat.publish`（SSE）body 必填 `releaseKey + ulid + query`，可选 `conversationId/chatPairId/appId/externalUserId`，header `chat-key` 可选（未传代理兜底为调用方 apiKey）。普通文本对话 body 示例：

```json
{
  "releaseKey": "<agent.bot.getPublishedDetail 返回的 releaseKey>",
  "appId": "<可选：业务应用ID，用于长期记忆隔离>",
  "externalUserId": "<可选：外部渠道用户标识>",
  "ulid": "<随机唯一ID>",
  "conversationId": "",
  "chatPairId": "<可选：问答对ID，续聊或更新该轮回答时传>",
  "query": [
    {
      "type": "text",
      "text": "你好，请介绍一下你能做什么"
    }
  ]
}
```

`query` 项支持的消息类型：

| `type` | 必填字段 | 说明 |
|---|---|---|
| `text` | `text` | 普通文本。 |
| `file` | `fileId`, `fileName`, `fileUrl` | 文件消息，先上传再传入。 |
| `image` | `fileId`, `fileName`, `fileUrl` | 图片消息。 |
| `audio` | `fileId`, `fileName`, `fileUrl`, `size` | 音频消息，按文件型字段传。 |

`releaseKey` 只放在 body；`chatKey`（chat-key header）是终端用户身份，不要把 `releaseKey` 填到 `chatKey`。续聊必须把上一轮 SSE 返回的 `conversationId` 放回 body。

### agent.createFull — 创建完整 Agent（复合）

适合：智能客服、企业助手、知识问答、表格查询、图片问答、带工具动作的 Agent。

复合链路，按 §1.1 的 4 phase 顺序执行：phase1 `agent.create` 骨架 → phase2 知识库（按 `knowledgeType` 分流：TEXT 走 `knowledge.createUploadAndBindAgent`，TABLE 走 `knowledge.tableImportAndBindAgent`，IMAGE 走 `knowledge.imageImportAndBindAgent`）→ phase3 可选 `skill.importAndBindAgent` → phase4 可选 `mcp.createAndBindAgent`。不能只创建 Agent 壳就结束；没有用户文档时，可先整理一份基础 FAQ markdown 走 TEXT 链路完整导入。

### skill.importAndBindAgent — 导入 Skill 并绑定

`agent.bot.updateGraph` 在目标 `agentNode.data.skills` 追加：

```json
{
  "skillsId": "<skill.skills.importPackage 返回 data.skillId>",
  "skillsCode": "<data.code>",
  "skillsName": "<data.name>",
  "orderNum": 1
}
```

不要把上传文件 ID 直接写进 Agent 节点；绑定必须使用 `skillId/code/name`。

### mcp.createAndBindAgent — 创建 MCP 服务并绑定

`agent.bot.updateGraph` 在目标 `agentNode.data.mcps` 追加：

```json
{
  "mcpId": "<mcp.server.create 返回的 mcpId>",
  "mcpName": "<MCP服务名称>"
}
```

### knowledge.createUploadAndBindAgent — TEXT 知识库、导入文档并绑定

`agent.bot.updateGraph` 在目标 `agentNode.data.knowledges` 追加：

```json
{
  "knowledgeBaseId": "<knowledge.base.save 返回 data.id>",
  "knowledgeName": "<知识库名称>"
}
```

上传不是导入。只上传文件或只登记文档，都会导致知识库不可召回或召回为空。须 `knowledge.doc.batchCreate`（登记）→ `knowledge.doc.preview`（分片预览）→ `knowledge.doc.confirm`（触发流水线，返回 batchNo/jobId）→ `knowledge.processingJob.getByBatch` 轮询到 `SUCCESS` 再绑定。

### knowledge.tableImportAndBindAgent — TABLE 知识库、表格 wizard 导入并绑定

适合：Excel、CSV、结构化台账、商品表、订单表、人员表。

本地智能体需承担决策：选哪个 `sheetName`、表头在哪一行（`headerRow`/`dataStartRow`）、哪些文本主列设 `indexed=true`、数值/金额列只作字段保留、修正 `fieldType/semanticType`。`columnName` 须为表头原文不可改。

### knowledge.imageImportAndBindAgent — IMAGE 知识库、图片 wizard 导入并绑定

适合：商品图、设备图、空间图、图文素材问答。

图片知识库的核心检索字段是描述。`INTELLIGENT` 模式由平台按 prompt 自动生成描述；`MANUAL` 模式须 `knowledge.structuredImport.image.start` 入库后，用 `knowledge.structuredCatalog.image.page` 按 `annotationStatus=PENDING` 取未标注图片的 `imageId`（ULID），再逐张 `knowledge.structuredImport.image.annotateItem` 补 `description`，否则向量未生成、召回为空。

---

## 5. Agent 机制浅析

Haoee 平台 Agent 由三部分组成：

1. 基础信息：名称、描述、分类、图标、发布状态。
2. 编排图：`feature + nodes + edges`。
3. 节点能力：模型、Prompt、知识库、Skill、MCP。

### 5.1 编排图

最小图：

```text
startNode -> agentNode
```

关键规则：

- 节点 ID 必须是 36 位 UUID。
- 至少有一个 `startNode` 和一个 `agentNode`。
- 边使用 `sourceId -> targetId`。
- `agentNode` 必须带合法 `modelId + modelName`，从 `model.base.list` 获取。
- 绑定知识库、Skill、MCP 时，先 `agent.bot.getDraftDetail` 读取草稿，再局部修改节点，避免覆盖已有绑定。

### 5.1 编排字段速记

`feature` 常用字段：

| 字段 | 含义 |
|---|---|
| `logicPrompt` | 人设与回复逻辑。 |
| `prologue` | 开场白。 |
| `prologueList` | 开场白预设问题。 |
| `inputType` | 0 打字，1 语音。 |
| `planEnable` | 强管理模式，全局规划循环。 |

`agentNode.data` 常用字段：

| 字段 | 含义 |
|---|---|
| `modelId` / `modelName` | 节点模型。 |
| `prompt` | 节点提示词。 |
| `description` | 节点场景说明。 |
| `contextRound` | 最大调用轮数。 |
| `maxTokens` | 最大回复长度。 |
| `temperature` | 随机性。 |
| `enableThink` | 深度思考开关。 |
| `evalEnable` / `evalMaxTimes` / `evalPrompt` | 节点评估。 |
| `knowledges` | 知识库绑定。 |
| `skills` | Skill 绑定。 |
| `mcps` | MCP 绑定。 |
| `isAdvice` / `openPrompt` / `advicePrompt` | 建议问题相关配置。 |

### 5.2 强管理模式与节点评估

文档给用户讲概念即可，不要暴露内部 JSON 字段名：

- 强管理模式：让 Agent 在执行过程中更严格地回到规划和管理流程，适合复杂任务。
- 节点评估模式：让某个 Agent 节点输出后经过评估器检查，适合高准确率场景。
- 评估最大次数：评估失败后最多重试几轮。

### 5.3 对话事件

流式对话事件常见类型：

| event | 含义 |
|---|---|
| `ping` | 心跳。 |
| `message` | 流式消息。 |
| `done` | 本轮完成。 |
| `error` | 错误事件。 |

`message` 中常见 `kind`：

| kind | 含义 |
|---|---|
| `node_start` | 节点开始。 |
| `content` | 正文片段。 |
| `node_end` | 节点结束。 |
| `plan` | 规划叙事。 |
| `suggest` | 追问建议。 |

建议前端或本地智能体单独处理 `suggest`，不要只从 `done` 里取建议。

---

## 6. Agent 浅层记忆机制

Haoee Agent 记忆可以按“会话内”和“跨会话”两层理解。给本地智能体的浅层认识，只需要抓住三件事：**什么时候记、记什么、怎么隔离**。

### 6.1 短期记忆

短期记忆服务于同一会话内的连续多轮对话。对话越长，系统越不会死背全部原文，而是把前文压成结构化摘要，重点保留：

- 当前用户目标
- 已确认的决定
- 关键事实、数字、文件名、方案名
- 尚未完成的待办
- 用户偏好与约束

可以把它理解为“会话内的滚动笔记”。这样 Agent 在长对话里仍能抓住主线，不容易丢失前面确认过的信息。

### 6.2 长期记忆

长期记忆服务于跨会话场景。它不是把每句话都记下来，而是从对话里提取值得长期保存的内容，例如：

- 用户偏好
- 用户画像
- 稳定事实
- 已确认方案
- 以后可能继续用到的操作习惯

写入时机也不是每轮都写，而是更接近“对话结束后批量整理”。召回时则是在新会话开始前，把和当前问题相关的记忆提前注入上下文。对本地智能体来说，只要记住：**先召回，再回答，再按对话结果补充记录** 就够了。

### 6.3 记忆的过滤与隔离

并不是所有内容都会进入长期记忆。系统会优先过滤掉：

- 明显的闲聊
- 临时情绪表达
- 未确认的推断
- 低置信度内容
- 明显敏感的信息

记忆隔离主要按这些维度理解：

| 维度 | 作用 |
|---|---|
| 用户 | 不同用户的记忆互不干扰。 |
| 应用 | 同一用户在不同应用下的记忆可隔离。 |
| 智能体 | 不同 Agent 的记忆不混用。 |
| 会话 | 方便回溯来源，但不作为跨会话主索引。 |

所以，在产品设计上不要把它理解成“一个全局脑子”，而是“按用户、应用、Agent 切开的记忆仓库”。

### 6.4 对话记忆相关参数

本地智能体调用 Agent 对话时，记忆机制主要受这些参数影响。可以把它们理解为四层定位：**哪个 Agent、哪个应用、哪个用户、哪段会话**。

| 参数 | 位置 | 记忆含义 | 使用规则 |
|---|---|---|---|
| `releaseKey` | body | 定位已发布 Agent。 | 从 `agent.bot.getPublishedDetail` 获取，只放在 body，不要填到 `chatKey`。 |
| `appId` | body | 定位业务应用端，用于区分不同入口下的长期记忆。 | 可选；同一应用入口保持固定，不同应用使用不同值。 |
| `externalUserId` | body | 定位外部渠道终端用户，用于长期记忆隔离。 | 可选；应使用稳定的外部用户标识。 |
| `chatKey` | `chat-key` header | 定位终端用户，是用户维度记忆隔离的关键。 | 可选；未传代理兜底为调用方 apiKey。一个真实用户或模拟用户对应一个稳定值，不要多人共用。 |
| `conversationId` | body | 定位当前会话，是会话内短期记忆和历史连续性的关键。 | 首聊可空；服务端通过 SSE 返回后，续聊必须带回。 |
| `ulid` | body | 定位本次请求，用于流式请求和停止控制。 | 每轮提问重新生成，不能当会话 ID 使用。 |
| `chatPairId` | body / SSE 返回 | 定位某一轮问答对。 | 可选；续聊或更新该轮回答时复用，普通新提问可空。 |

参数之间的关系：

```text
releaseKey -> 哪个 Agent
appId      -> 哪个业务应用入口
externalUserId -> 哪个外部渠道用户
chatKey    -> 哪个终端用户
conversationId -> 哪段连续会话
chatPairId -> 哪一轮问答
ulid       -> 哪一次请求
```

首轮对话时，最小可用记忆参数是：

```json
{
  "releaseKey": "<agent.bot.getPublishedDetail 返回的 releaseKey>",
  "appId": "<可选：业务应用ID，用于长期记忆隔离>",
  "externalUserId": "<可选：外部渠道用户标识>",
  "ulid": "<本次请求唯一ID>",
  "conversationId": "",
  "chatPairId": "<可选：问答对ID，续聊或更新该轮回答时传>",
  "query": [
    {
      "type": "text",
      "text": "你好"
    }
  ]
}
```

续聊时，必须把上一轮 SSE 返回的 `conversationId` 放回 body。否则即使 `chatKey` 没变，也会被当成新会话处理，短期上下文会断开。长期记忆是否召回，仍取决于平台侧的 Agent 记忆配置、用户身份、应用隔离和当前问题相关性。

### 6.5 对本地智能体的使用建议

| 场景 | 建议 |
|---|---|
| 普通问答助手 | 默认保留记忆能力。 |
| 客服、学习助手、销售助手 | 建议保留长期记忆，便于延续偏好和历史决策。 |
| 高隐私场景 | 提供清除记忆或不记录偏好的入口。 |
| 一次性工具型 Agent | 可以弱化长期记忆，更多依赖当前会话上下文。 |

### 6.6 管理入口

如果要做管理后台或本地智能体的能力面板，可以理解为四类操作：

- 查看全部记忆
- 删除单条记忆
- 重置某个用户 + Agent 维度的记忆
- 关闭记忆召回或记忆记录

这些能力适合做成产品开关，而不是让用户直接理解底层实现。

---

## 7. 知识库机制

知识库不再只等同于“文档分片”。当前能力分为三类：

| 类型 | 适合内容 | 推荐链路 | 核心能力 |
|---|---|---|---|
| `TEXT` | Markdown、PDF、Word、网页内容、FAQ 文档 | knowledge.createUploadAndBindAgent | 文档解析、分片、向量化、RAG 召回。 |
| `TABLE` | Excel、CSV、结构化明细表 | knowledge.tableImportAndBindAgent | 表格导入 wizard、列类型与索引配置、表格行查询。 |
| `IMAGE` | 商品图、设备图、场景图、素材图 | knowledge.imageImportAndBindAgent | 图片标注、图片描述检索、图片批次管理。 |

### 7.1 通用路径规则

- 知识库创建成功后，平台自动创建根目录 `/{knowledgeId}`。
- 上传知识库文件时 `folderPath=/{knowledgeId}`。
- 子目录写作 `/{knowledgeId}/子目录名`。
- 浏览目录时 `knowledge.doc.listFolder` 的 `where_id` 填同样路径。
- Skill 包上传用 `/default`，不要和知识库路径混用。

### 7.2 TEXT 导入规则

TEXT 类型必须走完整链路：

```text
knowledge.base.save 创建 TEXT 知识库
  -> REST 上传 oss.file.uploadFile 获 fileId（上传文档）
  -> knowledge.doc.batchCreate 登记文档
  -> knowledge.doc.preview 分片预览
  -> knowledge.doc.confirm 确认并触发解析
  -> knowledge.processingJob.getByBatch 轮询任务成功
  -> agent.bot.getDraftDetail / agent.bot.updateGraph 绑定 Agent
  -> agent.bot.publish 发布
```

`knowledge.doc.batchCreate` 是导入登记，`knowledge.doc.preview` 产生可预览分片，`knowledge.doc.confirm` 才会触发解析流水线。只上传文件不会完成导入。

`knowledge.doc.batchCreate` 默认可用 `syncMode=FULL_SYNC`；如需细调处理方式，可传 `processConfig`，缺省字段补默认（parseMode→PRECISE，extractImage/extractTable/scanOcr→false，splitterType→默认）；不传则按 TEXT 模板回落（FAST/false/false/false）。
`knowledge.doc.preview` 的预览阶段还能补充 `score / topK / allowRerank` 等召回参数，便于先看分片效果再 confirm。

常用分片参数：

| 参数 | 含义 | 建议 |
|---|---|---|
| `chunkSize` | 单个分片最大字符数 | 默认可用；长文档可用 800-1200。 |
| `chunkOverlap` | 分片重叠字符数 | 默认 50；连续上下文强时可提高。 |
| `score` / `recallScore` | 相似度阈值 | 默认 0.5；太低噪声多，太高召回少。 |
| `topK` / `recallTopK` | 召回条数 | 一般 3-10。 |
| `splitterType` | 分片策略 | 常用 `PARAGRAPH`。 |
| `allowRerank` | 是否重排 | 默认开启。 |

### 7.3 TABLE 导入规则

TABLE 类型不要走 TEXT 的 `knowledge.doc.batchCreate / preview / confirm` 文档链路。正确方式是 structured-import wizard：

```text
knowledge.base.save 创建 TABLE 知识库
  -> REST 上传 oss.file.uploadFile 获 fileId（Excel/CSV）
  -> knowledge.structuredImport.session.tableFile 建导入会话
  -> knowledge.structuredImport.session.listSheets 选择 Sheet
  -> knowledge.structuredImport.table.detectSchema 探测表结构
  -> knowledge.structuredImport.table.saveSchema 保存列定义
  -> knowledge.structuredImport.table.preview 预览验证
  -> knowledge.structuredImport.table.confirm 确认导入
  -> knowledge.structuredImport.task.get 轮询任务
  -> 绑定并发布 Agent
```

本地智能体需要根据表格内容决定：

- 目标 `sheetName`。
- `headerRow` 与 `dataStartRow`。
- 列的 `fieldType`、`semanticType`。
- 哪些文本主列设置 `indexed=true`。

### 7.4 IMAGE 导入规则

IMAGE 类型也不要走 TEXT 文档链路。正确方式是图片 wizard：

```text
knowledge.base.save 创建 IMAGE 知识库
  -> REST 上传 oss.file.uploadFile 获 fileId（图片）
  -> knowledge.structuredImport.session.imageFiles 建图片会话
  -> knowledge.structuredImport.image.saveAnnotation 设置标注方式
  -> knowledge.structuredImport.image.start 启动入库
  -> knowledge.structuredImport.task.get 轮询任务
  -> knowledge.structuredCatalog.image.page 取 imageId（ULID，MANUAL 时）-> knowledge.structuredImport.image.annotateItem 逐张补描述
  -> 绑定并发布 Agent
```

`INTELLIGENT` 模式由平台按 prompt 自动生成描述；`MANUAL` 模式须 `knowledge.structuredImport.image.start` 入库后逐张补 `description`（imageId 从 `knowledge.structuredCatalog.image.page` 取 ULID），否则向量未生成、召回为空。

删除 IMAGE 知识库前，必须先分页查询库内全部图片，收集图片记录的 `imageId`（ULID，不是 `ossFileId`），再用 `knowledge.structuredCatalog.image.batchRemove` 分批删除已入库图片，最后用 `knowledge.base.batchDelete` 删除知识库。直接删 IMAGE 库可能残留图片向量。TEXT/TABLE 知识库可以直接删除。

### 7.5 RAG 召回接口（knowledge.doc.recall）

`knowledge.doc.recall`（POST `/v1/facade/knowledge/doc/recall`）是外部可直接调用的召回接口，按 `query` 向量召回知识库分片，返回 `List<RagRecallResult>`。

**三类知识库召回行为**：

| 类型 | 召回行为 |
|---|---|
| `TEXT` | 召回文档分片。 |
| `IMAGE` | 基于图片 `description` 向量召回，命中返回图片项信息。 |
| `TABLE` | 不入向量库；`knowledge.doc.recall` 走 fulltext 路从表级 ES 索引返回行数据（`content` 为整行 JSON、`chunkId` 为行 ID）。分页查行可用 `knowledge.structuredCatalog.table.rows.page`。 |

**前置条件**：文档须 `knowledge.doc.confirm` 且处理 Job 为 `SUCCESS`，才有分片可召回。

**请求参数**：

| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| `query` | string | 是 | — | 查询文本 |
| `knowledgeId` | string | 是 | — | 知识库 ID |
| `topK` | integer | 否 | 0 | 返回数量；0=用知识库配置默认（范围 0-100） |
| `allowRerank` | boolean | 否 | true | 是否允许 rerank 重排 |
| `debug` | boolean | 否 | false | 是否返回召回解释（routeHits/fusionScore） |
| `tokenBudget` | integer | 否 | 12000 | Token 预算上限；0 或不传用默认 12000 |
| `conversationHistory` | object[] | 否 | — | 多轮上下文，每项见下两行 |
| `conversationHistory[].role` | string | 否 | — | 角色，枚举 `user`/`assistant` |
| `conversationHistory[].content` | string | 否 | — | 消息内容 |

**返回**：`List<RagRecallResult>`，每项含 `chunkId`、`score`、`content`、`knowledgeId`、`docId`、`docTitle`、`chunkIndex`；`debug=true` 时额外返回召回解释 `routeHits`、`fusionScore`、`vectorScore`、`rerankScore`。

`conversationHistory` 用于多轮上下文融合，把对话历史压缩成自包含查询以提升召回率。`topK` 留空（0）用知识库配置默认值。

---

## 8. Skill 机制

Haoee Skill 是可绑定到 Agent 节点的扩展能力，通常用于确定性脚本、工具调用、文件处理、业务动作。

### 8.1 两种形态

| 形态 | 适用入口 | 说明 |
|---|---|---|
| 包导入 | REST 上传 `oss.file.uploadFile` -> `skill.skills.importPackage` | 给 Codex/Cursor 等智能体使用的标准方式。 |
| INLINE | `skill.skills.create` | 工作台手动入口；本地智能体一般不要走这个。 |

`skill.skills.importPackage` 导入 Skill 后会自动产出 `skillId / code / name / currentVersionId`，编排绑定时必须用这些字段，不要用 `ossFileId`。`iconUrl` 缺省会走默认图标，`visibility` 可在 `PRIVATE / WORKSPACE / PUBLIC` 间覆盖。

### 8.2 Skill 包要求

- 包格式：`.zip` 或 `.skill`。
- 包内应包含 `SKILL.md`。
- zip 上限 10MB。
- 包内最多 200 个文件。
- 单文件解压后不超过 20MB。
- 累计解压不超过 50MB。

### 8.3 Skill 运行参数机制

平台调用 Skill 时，参数主要通过环境变量进入脚本：

```text
SKILL_ARGS_JSON=<完整 JSON 参数>
SKILL_ARG_XXX=<单个参数>
```

Python/Node 脚本建议优先读取 `SKILL_ARGS_JSON`，这样可以保留数字、布尔、数组等原始类型。

---

## 9. MCP 服务机制

这里的 MCP 服务指“挂到 Haoee Agent 节点上的外部工具服务”，不是 §〇 配置的 `haoee-*` MCP server。

### 9.1 配置结构

MCP 服务配置是一个 `mcpServers` JSON 字符串，且应只包含一项服务：

```json
{
  "mcpServers": {
    "my-service": {
      "url": "https://example.com/mcp"
    }
  }
}
```

### 9.2 推荐流程

```text
mcp.server.validate 校验 JSON
  -> mcp.server.create 创建 MCP 服务（name / descr / json；json 必须是单服务对象）
  -> agent.bot.getDraftDetail 读取 Agent 草稿图
  -> agent.bot.updateGraph 绑定到 agentNode.data.mcps
  -> agent.bot.publish 发布 Agent
```

### 9.3 启停规则

启用或禁用走 `mcp.server.update`：

1. 先 `mcp.server.detail` 取详情，拿 `version/json/name/type` 等原配置。
2. 再 `mcp.server.update` 设置 `status=1` 或 `status=0`。
3. 更新时不要只传 `status`，否则可能清空配置。

`mcp.server.create` 和 `mcp.server.update` 共同约束：

- `descr` 不能换行。
- `json` 必须是 `mcpServers` 单项对象。
- `mcp.server.update` 必带 `version`，并建议原样带回 detail 的 `json/descr/type`。

---

## 10. 模型列表与选择建议

调用 `model.base.list` 获取当前可用模型。平台返回字段通常包括：

- `modelId`
- `modelName`
- `providerName`
- `description`
- `allowMaxTokens`
- `enable`

创建或更新 Agent 节点时，`modelId` 和 `modelName` 必须来自 `model.base.list`，不要臆造。下面的模型选择建议只作经验参考，实际可用模型以 `model.base.list` 返回为准。

| 场景 | 建议选择 |
|---|---|
| 默认通用 Agent | 选 `model.base.list` 返回中稳定、成本适中的通用模型。 |
| 低成本高并发客服 | 优先速度快、成本低、上下文够用的模型。 |
| 复杂工程和代码 | 优先推理、工具调用和代码能力强的模型。 |
| 长文档知识助手 | 优先上下文窗口大、长文本理解稳定的模型。 |
| 高质量规划和推理 | 优先高阶推理模型，接受更高延迟和成本。 |
| 多模态或图片任务 | 优先 `model.base.list` 中描述支持多模态的模型。 |

---

## 11. 接口参数速查

简单接口（91 个）的「apiRef + 用途 + 必填参数」见 §1.2（自动生成）；9 个复杂接口的 bodyJson parameters 骨架见 §1.3（自动生成）。本节补充复杂接口的字段注意点（@Tool schema 不展开的字段语义）：

| apiRef | 关键注意点 |
|---|---|
| `agent.bot.updateGraph` | 节点 id 须 36 位 UUID；feature 含 logicPrompt/prologue/prologueList/inputType/planEnable；agentNode.data 需带 modelId/modelName（来自 model.base.list）；绑定 knowledges/skills/mcps 先 getDraftDetail 读整图再局部改。 |
| `knowledge.doc.batchCreate` | TEXT 导入第 1 步；processConfig 不传按 TEXT 模板回落（FAST/false/false/false），传则缺省字段补默认。 |
| `knowledge.doc.batchDelete` | fileIdList[].fileId + isDir 区分文件/目录。 |
| `knowledge.doc.preview` | TEXT 导入第 2 步；可覆盖 processConfig 调参；可补 score/topK/allowRerank 预览召回效果。 |
| `knowledge.doc.recall` | 外部可直接调用的 RAG 召回接口，参数与返回详见 §7.5。 |
| `knowledge.structuredImport.table.saveSchema` | TABLE 决策点 3；indexed 只文本主列；columnName 须表头原文。 |
| `mcp.server.create` | json 须 mcpServers 单服务对象；descr 禁止换行。 |
| `mcp.server.update` | 启停也走此；必带 version + 原样带回 json/descr/type，否则可能清空配置。 |
| `skill.skills.create` | INLINE 技能工作台入口；本地智能体一般走 `skill.skills.importPackage` 包导入而非此。 |

> 复杂接口构造 bodyJson 时，字段骨架以 §1.3 为准；本表只补充字段语义注意点，避免与 §1.3 重复。

---

## 12. 文件上传模板

本地文件上传走 REST multipart POST `/v1/api/oss/file/uploadFile`（`oss.file.uploadFile` @Tool 已下架，仅 REST），不能放进 bodyJson。文件大小按类型限制：音频/视频≤200MB，文档/表格/图片/其他≤20MB。固定 URL 模式（`<apiKey>` 替换调用方 apiKey）：

```text
https://mcp.haoee.com/mcp/service-mcp-platform-oss/mcp/service-oss/v1/api/oss/file/uploadFile?apiKey=<apiKey>
```

> 该 URL 由 §〇 配置的 `haoee-oss` server 承载；`oss.file.uploadFile` @Tool 已下架，统一走 REST curl 上传。下列 curl 为标准上传方式。

### 12.1 知识库文档上传

```bash
curl -sS -X POST \
  "https://mcp.haoee.com/mcp/service-mcp-platform-oss/mcp/service-oss/v1/api/oss/file/uploadFile?apiKey=<apiKey>&folderPath=/<knowledgeId>" \
  -F "file=@/本地绝对路径/文档.md"
```

执行前将 `<apiKey>` 替换为真实值。不要在 `agentScriptPostUrl` 已有 `?apiKey=` 时再次追加 `?apiKey=`。

响应里的 `data.id` 是 `fileId`。TEXT 文档直接用于 `knowledge.doc.batchCreate.fileIdList`；TABLE 用于 `knowledge.structuredImport.session.tableFile.ossFileId`；IMAGE 收集后用于 `knowledge.structuredImport.session.imageFiles.fileIdList`。这些后续接口只需要 `fileId`，无需等待上传 URL；只有业务确实需要可访问 URL 时，才用 `oss.file.getPresignedDownloadUrl` 按 `fileId` 轮询至 `data.url` 非空。

### 12.2 Skill 包上传

```bash
curl -sS -X POST \
  "https://mcp.haoee.com/mcp/service-mcp-platform-oss/mcp/service-oss/v1/api/oss/file/uploadFile?apiKey=<apiKey>&folderPath=/default" \
  -F "file=@/本地绝对路径/skill.zip"
```

响应里的 `data.id` 直接作为 `skill.skills.importPackage.ossFileId`，无需等待上传 URL；只有业务确实需要可访问 URL 时才调用 `oss.file.getPresignedDownloadUrl`。

---

## 13. 常见错误

| 错误 | 正确做法 |
|---|---|
| 自己拼业务接口 URL | 直调对应 @Tool（@Tool name = apiRef）。 |
| GET/query/path 参数拼到 URL | 写进对应 @Tool 的 bodyJson / @ToolParam。 |
| 上传文件放进 bodyJson | 走 REST multipart POST `/v1/api/oss/file/uploadFile`（@Tool 已下架）。 |
| 知识库只上传不导入 | TEXT 走 `knowledge.doc.batchCreate` -> `knowledge.doc.preview` -> `knowledge.doc.confirm` -> `knowledge.processingJob.getByBatch` 轮询 SUCCESS。 |
| TABLE 类型走 TEXT 文档链路 | TABLE 走 structuredImport wizard（`session.tableFile` -> `listSheets` -> `detectSchema` -> `saveSchema` -> `preview` -> `confirm` -> `task.get`）。 |
| IMAGE 类型走 TEXT 文档链路 | IMAGE 走图片 wizard（`session.imageFiles` -> `image.saveAnnotation` -> `image.start` -> `task.get` -> `structuredCatalog.image.page` -> `image.annotateItem` for MANUAL）。 |
| 直接删除 IMAGE 知识库 | 先 `knowledge.structuredCatalog.image.page` 分页收集 `imageId`（ULID，非 ossFileId），用 `knowledge.structuredCatalog.image.batchRemove` 分批删除，再用 `knowledge.base.batchDelete` 删库。 |
| 把 `fileId` 当知识库 ID | 知识库 ID 是 `knowledge.base.save` 返回 `data.id`。 |
| 把上传文件 ID 当 Skill ID | 先 `skill.skills.importPackage` 导入，绑定用 `skillId/code/name`。 |
| 绑定资源时覆盖原节点 | 先 `agent.bot.getDraftDetail` 读整图，在原节点上追加。 |
| 模型名称手写 | 先 `model.base.list` 查询。 |
| MCP 启停只传 `status` | 先 `mcp.server.detail` 取详情，再 `mcp.server.update` 带回原配置 + version。 |
| `releaseKey` 填到 `chatKey` | `releaseKey` 放 body，`chatKey`（chat-key header）填终端用户 key。 |

---

## 14. 给本地智能体的系统提示建议

可以把下面这段放进 Codex/Cursor 的项目说明里：

```text
你已经接入 5 个 haoee-* MCP server（haoee-agent / haoee-knowledge / haoee-mcp / haoee-skill / haoee-oss），apiKey 走连接级 header。涉及 Haoee 平台时，先按 §一.1 flow 全步骤选链路，直调对应 @Tool（@Tool name = apiRef，如 agent.bot.create）；流式对话用 agent.chat.publish（SSE）；本地文件上传用 REST multipart POST /v1/api/oss/file/uploadFile（oss.file.uploadFile @Tool 已下架），不要把文件放进 bodyJson。

创建完整 Agent 时，不能只创建 Agent 壳。应完成模型选择、编排保存、发布；若用户要知识问答或客服，应根据资料类型选择 TEXT/TABLE/IMAGE 知识库链路并完整导入、轮询完成后绑定；有 Skill 包时上传并导入后绑定；有外部 MCP 配置时先校验再创建并绑定。

编排 Agent 节点时，modelId/modelName 必须来自 model.base.list；节点 ID 使用 36 位 UUID；绑定知识库用 knowledgeBaseId/knowledgeName，绑定 Skill 用 skillsId/skillsCode/skillsName，绑定 MCP 用 mcpId/mcpName。任何 GET/query/path 参数都放进对应 @Tool 的 bodyJson / @ToolParam。
```
