3.12 外部接口

外部接口(/api/external/*)是 MoAI 把团队资源开放给外部系统的唯一通道:知识库、知识图谱、应用对话从这里出,MCP / ACP / A2A 三种标准协议端点也在这里。调用方通常是业务后端、MCP 客户端、外部 agent,或嵌入官网的悬浮组件(见 3.7)。

本文讲访问方式:鉴权、范围、端点地图与调用示例。接口全集不逐条罗列——外部接口单独生成 OpenAPI 文档,生成客户端工具即可。

接口文档

入口地址
原始文档(OpenAPI)http://127.0.0.1:5000/openapi/external.json
Scalar 浏览http://127.0.0.1:5000/scalar,左上角下拉切换 AI External API;或直达 /scalar/external
  • 内部接口在 AI API(v1)文档,与外部接口互不混编;
  • 文档仅在 Development 环境暴露,生产环境关闭;
  • 给业务系统生成客户端:把 external.json 喂给 OpenAPI 生成器(Kiota、openapi-generator 等),不要手拼 HTTP 请求。

鉴权

外部接口只认两类凭证,都来自「应用接入 key」(创建与管理见 3.10),按调用语义二选一。

方式一:key 直连(系统身份)

适合无「用户」语义的系统级调用——同步图谱、拉取知识库文档:

curl http://127.0.0.1:5000/api/external/wiki/list \
  -H "Authorization: Bearer moai-ac-xxxxxxxx"

# 与 Bearer 等价的 x-api-key 头,二选一
curl http://127.0.0.1:5000/api/external/wiki/list \
  -H "x-api-key: moai-ac-xxxxxxxx"

方式二:外部用户 token(人的身份)

对话、会话记录等场景需要「这是谁」的语义,先换 token 再调用:

curl -X POST http://127.0.0.1:5000/api/external/token \
  -H "Content-Type: application/json" \
  -d '{
    "accessAppKey": "moai-ac-xxxxxxxx",
    "appId": "9f2b0000-0000-0000-0000-000000000000",
    "externalUserId": "erp-user-1001",
    "nickname": "小明"
  }'

返回 accessToken / refreshToken。三种参数组合对应三种 token:

传参换到能做什么
只传 accessAppKey应用 token资源类调用(读写知识库/图谱等),不能创建会话对话
accessAppKey + appId + externalUserId外部用户 token全部能力:以这个「外部用户」的身份建会话、对话、查自己的历史(nickname 可选)
只传 appId匿名 token仅「免授权」应用,悬浮组件游客场景(见 3.7)

要点:

  • access token 有效期 2 小时(Debug 环境 7 天),refresh token 7 天;POST /api/external/token/refresh(body 传 refreshToken)续期;
  • 刷新按 key 当前勾选的范围重新校验:收窄范围后,旧 token 下一次刷新即降级;
  • 会话与消息归属外部用户(externalUserId),同一 key 可为任意多个外部用户换 token,用户只能看到自己的会话。

能力范围与读写分档

每个范围勾选对应一组端点,范围调整实时生效(范围含义见 3.10):

范围端点深入文档
知识库读 / 写/api/external/wiki/*3.5.4
知识库 MCP/api/external/wiki/{wikiId}/mcp3.5.7
知识图谱读 / 写/api/external/knowledge-graph/*3.6
知识图谱 MCP/api/external/knowledge-graph/{kgId}/mcp3.6
应用对话/api/external/agent/*、/api/external/session/*见下文
应用 ACP/api/external/app/{appId}/acp见下文
应用 A2A/api/external/app/{appId}/a2a见下文
模型网关/api/aigateway/{teamId}/v1/*(不在 /api/external 下)3.11

读写分档口径:GET 与 */list 结尾的请求算读档,其余算写档。知识库、知识图谱各有只读/可写两档,只勾只读时写请求一律 403 insufficient_scope。

团队隔离是硬边界:key 只能触达本团队的知识库、图谱与应用,跨团队资源一律 404(而非 403,不暴露资源存在性)。

端点地图:对话与协议

应用对话

端点说明
POST /api/external/agent/{appId}/session创建会话
GET /api/external/agent/{appId}/session/list会话列表
GET /api/external/session/{sessionId}/messages会话历史消息
POST /api/external/agent/{appId}/chat流式对话(AG-UI 协议,SSE)

对话走 AG-UI 协议:请求体携带 threadId(即会话 id)与本轮用户消息,历史由服务端管理,每轮只发最新一条;响应为 SSE 事件流(文本增量、工具调用等)。前端对话页与悬浮组件用的就是这套协议。

curl -N -X POST http://127.0.0.1:5000/api/external/agent/{appId}/chat \
  -H "Authorization: Bearer <外部用户 accessToken>" \
  -H "Content-Type: application/json" \
  -d '{
    "threadId": "上一步创建的会话 id",
    "messages": [{ "id": "m1", "role": "user", "content": "帮我查一下最近的订单" }]
  }'

ACP / A2A(对接外部 agent)

把团队应用(Agent 应用、流程应用,须已发布)以标准协议暴露给外部 agent 框架,分别由「应用 ACP」「应用 A2A」范围控制,不要求应用对话范围:

协议端点方法面
ACP(Agent Client Protocol,JSON-RPC 2.0)POST /api/external/app/{appId}/acpinitialize / session/new / session/load / session/prompt(SSE)/ session/cancel
A2A(Google Agent2Agent,JSON-RPC 2.0)POST /api/external/app/{appId}/a2amessage/send / message/stream(SSE)/ tasks/get / tasks/cancel;Agent Card 见 GET …/a2a/agent.json

两个协议的会话都归属外部用户;A2A 的 contextId 承载 MoAI 会话 id,缺省自动建会话,携带即续聊。

辅助端点

端点鉴权说明
POST /api/external/token、/token/refresh匿名(凭证在 body)换 token / 续期
GET /api/external/app/list任意外部凭证当前团队可对话的应用列表
GET /api/external/app/{appId}/access-point匿名应用访问点信息(悬浮组件引导)

常见问题

403 insufficient_scope?
key 未勾选对应范围,或只勾了读档却在调写接口。团队管理员在「应用接入」里编辑 key 勾选范围,实时生效。

资源明明存在却 404?
跨团队隔离:key 只认自己团队的资源。确认用的是目标团队管理员签发的 key。

建会话 403?
应用 token 不能对话。用 accessAppKey + appId + externalUserId 换外部用户 token 再操作。

token 刷新 401?
refresh token 已过期(7 天),或来源 key 已删除、范围收窄后不再满足条件。重新用 key 换 token。

Scalar 里找不到外部接口?
打开 /scalar 后左上角文档下拉切换到 AI External API,或直达 /scalar/external;文档仅在 Development 环境暴露。