Skip to content

docs(api): 补齐执行工厂公开面 API 文档(90 个端点) - #583

Open
sh00tg0a1 wants to merge 8 commits into
mainfrom
docs/522-execution-factory-api
Open

docs(api): 补齐执行工厂公开面 API 文档(90 个端点)#583
sh00tg0a1 wants to merge 8 commits into
mainfrom
docs/522-execution-factory-api

Conversation

@sh00tg0a1

Copy link
Copy Markdown
Contributor

Description

文档中心一直没有 execution-factory。本批次先收函数面——写一段 Python、在沙箱里跑起来、看输出,以及围绕它的依赖查询、代码模板与 AI 生成,共 6 个端点。

端点 作用
POST /function/execute 在沙箱中执行代码
GET /function/dependencies 沙箱里已装了哪些第三方库
GET /function/dependency-versions/{package_name} 查某个包的可用版本
GET /template/{template_type} 取函数骨架
POST /ai_generate/function/{type} 由描述生成代码 / 由代码反推参数定义
GET /ai_generate/prompt/{type} 查看生成用的提示词模板

写函数的例子

模块 README 里有一条从取模板 → 查依赖 → 执行 → 加第三方库 → AI 生成的完整 curl 走查,GET /template/python 的响应示例直接用服务内置的真实模板,function/execute 的示例是测试服的真实返回。

几处「不看文档就会踩」的地方

  • 入口函数名固定是 handler,签名 handler(event: Dict[str, Any]) -> Any。写成 main 之类不会报「找不到入口」,只是行为不符合预期。
  • 代码抛异常时接口仍返回 200——判断成败看 exit_codestderr,不是 HTTP 状态码。
  • timeout 单位是秒,而内部面 POST /internal-v1/function/exec/{version} 用的是毫秒,容易照搬出错。
  • 响应比想象中宽:除 stdout / stderr / result / metrics 外还有 exit_code / error_message / execution_time_ms / artifacts / session_idmetricspeak_memory_mb 与 IO 计数依赖沙箱运行时能力,取不到就是 null(实测如此)。

错误信封

本服务与 context-loader 一样没有用 kweaver-go-lib,各自带了一份同源的 infra/errors.HTTPError,字段是 code / description / solution / link / details,与 rest.BaseError 不同_shared/errors.yaml 原注释把这两个服务列进「统一走 BaseError」的名单属误述,已改为如实说明,并新增两者共用的 ErrorCompact

⚠️#578 有一处小冲突:两个 PR 都改了 _shared/errors.yaml#578 加的是 ErrorAgentRetrieval,本 PR 加的是通用的 ErrorCompact)。哪个先合,另一个我来 rebase 收敛成一个 ErrorCompact

Links

Item Value
Issue Refs #522(该 issue 覆盖 4 个模块,本 PR 只做 execution-factory 的函数面)
Design Doc 不适用(type: docs
Branch docs/522-execution-factory-api

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactoring

Testing

静态make api-docs-lint 通过(新增文件 0 warning);make api-docs-html 渲染正常,首页出现「执行工厂」分区。

实机核对(测试服 14.103.77.23,admin token 打外部面):

核对项 结果
GET /template/python 返回的正是服务内置模板,入口函数确为 handler
GET /function/dependencies dependencies + session_id,与文档一致
GET /function/dependency-versions/requests package_name + versions,一致
GET /ai_generate/prompt/python_function_generator 5 个字段全对
POST /function/execute 实跑一次拿到真实响应,据此补全了 exit_code / error_message / execution_time_ms / artifacts / session_idmetrics 结构

Pre-Merge Checklist

  • 设计文档:不适用(type: docs
  • CHANGELOG.md:未更新——纯文档,无服务行为变化
  • API 文档已更新(本 PR 即是)
  • make api-docs-lint 通过
  • 无破坏性变更(不改任何服务代码)

覆盖边界(据实写进模块 README)

本批次只有函数面 6 个端点。同一服务公开面还有约 80 个端点未文档化,其响应结构未经验证

端点数
算子 operator 15
工具箱 toolbox 22
MCP 15
Skill 25
沙箱观测 sandbox 4
导入导出 impex 2

内部面 internal-v1 与能力面 /api/capabilities-lab/v1 同样不收录。服务目录下 adp/execution-factory/operator-integration/docs/apis/ 的历史草稿可作参照,但与实现有漂移(context-loader 的同类草稿实测就有三处写错),不能直接当真相源。

要不要继续把剩下 80 个补完、以及是否拆成多个 PR,等你定。

🤖 Generated with Claude Code

@sh00tg0a1

Copy link
Copy Markdown
Contributor Author

@claude 审一下

@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 6m 14s —— View job


评审进行中

  • 拉取 diff 与仓库规范(CLAUDE.md / CONTRIBUTING)
  • 核对 function.yaml 与 execution-factory 源码实现
  • 核对 sandbox.yaml / impex.yaml 与源码实现
  • 核对 _shared/errors.yaml 改动与既有引用方
  • 核对 Makefile / docs/api/README.md 改动
  • 检查 CI 状态与既往评论
  • 提交 PR review
    · 分支 docs/522-execution-factory-api

@sh00tg0a1 sh00tg0a1 changed the title docs(api): 补齐执行工厂函数面 API 文档 docs(api): 补齐执行工厂公开面 API 文档(90 个端点) Aug 1, 2026
@sh00tg0a1

Copy link
Copy Markdown
Contributor Author

执行工厂公开面已收齐:90 / 90

PR 开的时候只有函数面 6 条,现在 /api/agent-operator-integration/v1 全部收录完毕,7 份 YAML:

文件 端点 主题
function 6 沙箱执行 / 依赖 / 模板 / AI 生成
sandbox 4 只读观测(限超管)
impex 2 .adp 导出导入
operator 15 算子注册 / 版本 / 调试 / 市场
mcp 16 接入外部 MCP + 对外提供 MCP
toolbox 22 工具箱与工具、代理调用、能力包
skill 25 技能包、发布历史、执行、索引构建

数量与从代码 RegisterPublic 抽出的路由表逐条对上。

收录判据

只收 Ingress 暴露的 /v1——浏览器(Studio)能到达的面。内部面 internal-v1 刻意不挂 Ingress:不校验令牌、身份取自调用方自填的 X-Account-ID,缺头时降级为硬编码管理员,暴露出去等于 40 条写接口无凭据可调(chart values 注释与 #326)。不写进任何对外文档。 能力面 /api/capabilities-lab/v1 暂未收。

验证程度分两级,不含糊

  • 路由与收录范围:90 条全部从代码逐条核过。
  • 字段级:只有实机打过的 17 条算验证过(函数 5 / 沙箱 3 / 算子 3 / MCP 2 / 工具箱 2 / Skill 2)。其余 73 条按 Go 类型写成,未经实机验证,README 已如实标注。等 docs(api): 补齐 context-loader 外部面 API 文档 #578 合入后可用 x-contract-probe 把只读 GET 纳入巡检。

实机撞出来的坑(都写进文档了)

  • /sandbox/poolsession_resources首字母大写CPU/Memory/Disk/Timeout),因为那结构只有 yaml 标签没 json 标签;同一响应里 resource_limit 却是小写。已开 fix(execution-factory): /sandbox/pool 的 session_resources 键名是 Go 字段名(CPU/Memory/Disk/Timeout),漏了 json 标签 #585
  • 时间戳全是纳秒time.Now().UnixNano()),按毫秒解析会落到 1970 年。我第一版在 operator.yaml 里写成毫秒,已改。
  • /tool-box/market/toolstool_name 必填,不传直接 400——它不是「列出全部市场工具」。
  • 箱内工具列表返回 tools,市场工具列表返回 data,同模块两种键名。
  • 注册技能 / 更新技能包不收 JSON,只认 form / multipart。
  • Skill 的消费态(/content)与管理态(/management/content)读的是不同版本,拿错会看到不一致的内容。
  • MCP 的 mode 在新增(sse/stream)与更新(多两个 stdio_*)时可选范围不同,stdio 模式没法直接新建。
  • 多处「报错也返回 200」:函数执行看 exit_code、MCP 工具调用看 is_error、工具代理调用看 status_code、技能执行看 exit_codemocked=true 表示压根没真跑。

自我修正

  • 端点总数从 89 改为 90——抽路由的正则漏了 Any("/app/:mcp_id/mcp")
  • _shared/errors.yaml 里错误码服务名前缀写成了小写,实际是 AgentOperatorIntegration(首字母大写),已按实测报文改正。

@claude

claude Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 8m 0s —— View job


评审进行中

  • 拉 diff 与仓库规范
  • 从源码抽 RegisterPublic 路由表,与 7 份 YAML 逐条比对 — 路由表对不上:源码 91 条,文档 90 条
  • _shared/errors.yaml 改动 — 纯新增 ErrorCompact,字段与 Go struct 逐个对上
  • 核 Makefile / docs/api/README.md 改动
  • 抽查未实机验证端点的字段(Go struct tag ↔ schema)
  • 核作者自述的几处「坑」是否与源码一致
  • 查 CI 状态并提交 PR review
    · 分支 docs/522-execution-factory-api

| [toolbox.yaml](toolbox.yaml) | 工具箱 | 工具箱 CRUD 与状态 / 箱内工具增删改查与启停 / 调试与代理调用 / 算子转工具 / OpenAPI 能力包 / 市场 4 条,共 22 条 |
| [skill.yaml](skill.yaml) | Skill | 注册 / 列表 / 详情 / 元数据与包更新 / 发布与历史 / 市场 2 条 / 消费态与管理态读取各 3 条 / 执行 / 索引构建 5 条,共 25 条 |

**公开面 90 条已全部收录。**

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

阻塞:公开面是 91 条,不是 90 条——漏了 POST /function/infer-schema

rest_public_handler.go:73 就在已收录的 /function/execute(第 70 行)和
/function/dependency-versions/:package_name(第 75 行)中间:

// 从函数代码推导参数定义(@tool 函数的签名即参数定义)
engine.POST("/function/infer-schema", r.UnifiedProxyHandler.FunctionInferSchema)

它不是边角料。common/proxy.go:342-355 定义了完整的请求/响应:

type FunctionInferSchemaReq struct {
	Code string `json:"code" validate:"required"`
}
type FunctionInferSchemaResp struct {
	Supported   bool                       `json:"supported"`
	Name        string                     `json:"name,omitempty"`
	Description string                     `json:"description,omitempty"`
	Inputs      []*interfaces.ParameterDef `json:"inputs,omitempty"`
	Outputs     []*interfaces.ParameterDef `json:"outputs,omitempty"`
}

这条是确定性地从 @tool 装饰器推参数定义,代码里没用 @tool 时返回
supported: false 让调用方回退手填。而本文档第 88-91 行只教了大模型版的
ai_generate/function/metadata_param_generator——照文档接的人会用大模型去做一件
本来能精确算出来的事。这正是「不看文档就会走弯路」的典型,恰恰该收。

要改的地方:

  • 本行「公开面 90 条已全部收录」
  • 第 10 行 function.yaml 索引行
  • 第 121-122 行「90 个端点已全部收录:函数 6 + …」
  • 第 130 行「全部从代码的 RegisterPublic 逐条核过,90 条不多不少」
  • function.yaml 补该端点

其余六个面我逐条核过,数量与路径完全对得上(算子 15 / MCP 16 / 工具箱 22 /
Skill 25 / 沙箱 4 / 导入导出 2),只有函数面差这一条。

Fix this →

- **内部接口**:`/api/agent-operator-integration/internal-v1` 是内部面,另有 `POST /function/exec/{version}`(按已注册的函数版本执行,`timeout` 单位毫秒)等端点,**本文档不收录**。
- **能力面**:`/api/capabilities-lab/v1` 是合并进本服务的另一套路由(原 capabilities-lab 独立服务),也挂在 Ingress 上,路径与语义都与 `v1` 不同,**本文档暂不收录**。
- **时间戳一律是纳秒**:所有 `*_time` 字段由 `time.Now().UnixNano()` 生成,形如 `1784880971306127803`;按毫秒解析会得到 1970 年附近的日期。全服务统一,算子 / MCP / 工具箱 / Skill 都是。
- **契约巡检**:只读 GET 上标了 `x-contract-probe`,需巡检工具支持该扩展(见 #578)才会生效;在那之前这些标注是惰性的。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#578 已经合进 main 了,这句话的前提没了——而且合入后结论是反的。

看 main 上的 docs/api/tools/api_contract_diff.pyx-contract-probe 只对 POST 生效

def is_probeable(o):
    if o["method"] == "GET":
        return True                      # GET 无条件探测,跟标注无关
    if o["override"] and args.include_query_post:
        return True
    spec = o.get("probe")
    return bool(args.include_probe_post and isinstance(spec, dict)
                and spec.get("readonly") is True)

run_probes() 的候选集也写死了 o["method"] == "POST"。工具头部注释说得很直白:
--include-probe-post 文档里用 x-contract-probe 显式标注 readonly:true
POST」——这个扩展就是为 context-loader 那种「查询即 POST」的服务准备的。

所以本 PR 这 13 处标注全部落在 GET 上(sandbox 3 / operator 3 / skill 3 / mcp 2 /
toolbox 2),一处都不会被读到;而这些 GET 本来就已经在探测范围内,不需要标注。
两句话都要改:

顺带:本行说「只读 GET 上标了 x-contract-probe」也名不副实——function.yaml 4 个
GET、impex.yaml 1 个 GET 一处没标,其余文件也只标了一部分。

建议二选一:把这 13 处删掉、README 改成「只读 GET 已被 make api-contract-diff
默认覆盖,无需额外标注」;或者保留标注但明确写清它当前对 GET 无作用、是为将来
工具扩展预留的。别留着一句读者会当真的承诺。

Fix this →

Comment on lines +139 to +145
**验证程度分两级**,模块内不同文件不一样:**路由与收录范围**全部从代码的
`RegisterPublic` 核过;**字段级**只有实机打过的算数——函数 5 条、沙箱 3 条、
算子的分类与两个列表。其余按 Go 类型与服务目录草稿写成,标注为未实机验证。

这些接口的**响应结构未经本批次验证**,改动时请人工核对。服务目录下
`adp/execution-factory/operator-integration/docs/apis/` 里有一份历史草稿可作参照,
但它与实现存在漂移(context-loader 的同类草稿实测就有三处写错),不要直接当作真相源。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这段是第一批(只收函数面 6 条)时的残留,跟上面第 128-134 行重复且互相矛盾,删掉。

  • 第 139-141 行又说了一遍「验证程度分两级」,但列的是旧口径「函数 5 条、沙箱 3 条、
    算子的分类与两个列表」,漏了 MCP / 工具箱 / Skill,与第 131-132 行的「合计 17 条」对不上。
  • 第 143 行「这些接口的响应结构未经本批次验证」——「这些接口」原本指的是第一批
    里那张「未文档化端点」表,那张表这轮已经删了,现在这个指代悬空,读者会以为指的是
    全部 90 条。

第 143-145 行里关于历史草稿有漂移、不能当真相源的提醒本身有价值,建议并到第 128-134
行那一节末尾,保留一句即可。

Suggested change
**验证程度分两级**,模块内不同文件不一样:**路由与收录范围**全部从代码的
`RegisterPublic` 核过;**字段级**只有实机打过的算数——函数 5 条、沙箱 3 条、
算子的分类与两个列表。其余按 Go 类型与服务目录草稿写成,标注为未实机验证。
这些接口的**响应结构未经本批次验证**,改动时请人工核对。服务目录下
`adp/execution-factory/operator-integration/docs/apis/` 里有一份历史草稿可作参照,
但它与实现存在漂移(context-loader 的同类草稿实测就有三处写错),不要直接当作真相源。
仍不收录的两个面:内部面 `internal-v1`(刻意不挂 Ingress,见上节)与能力面
`/api/capabilities-lab/v1`
服务目录下 `adp/execution-factory/operator-integration/docs/apis/` 里有一份历史草稿
可作参照,但它与实现存在漂移(context-loader 的同类草稿实测就有三处写错),
不要直接当作真相源。

@sh00tg0a1 sh00tg0a1 closed this Aug 1, 2026
@sh00tg0a1 sh00tg0a1 reopened this Aug 1, 2026
@sh00tg0a1
sh00tg0a1 force-pushed the docs/522-execution-factory-api branch from 34661bc to 96d1f61 Compare August 1, 2026 04:38
sh00tg0a1 and others added 8 commits August 1, 2026 12:53
文档中心一直没有 execution-factory。本批次先收函数面——写一段 Python、在沙箱里
跑起来、看输出,以及围绕它的依赖查询、代码模板与 AI 生成,共 6 个端点:

  POST /function/execute
  GET  /function/dependencies
  GET  /function/dependency-versions/{package_name}
  GET  /template/{template_type}
  POST /ai_generate/function/{type}
  GET  /ai_generate/prompt/{type}

以代码与实机返回为准,写清了几处「不看文档就会踩」的地方:

- **入口函数名固定是 `handler`**,签名 handler(event: Dict[str, Any]) -> Any。
  这是平台的硬约定,写成 main 之类不会报「找不到入口」,只是行为不符合预期。
  模块 README 里给了从取模板、查依赖、执行到 AI 生成的完整 curl 走查,
  `GET /template/python` 的响应示例直接用服务内置的真实模板。
- **代码抛异常时接口仍返回 200**,判断成败要看 exit_code 与 stderr,不是 HTTP
  状态码。
- **timeout 单位是秒**,而内部面 POST /internal-v1/function/exec/{version} 用的是
  毫秒,两者不一致,容易照搬出错。
- **响应比想象中宽**:除 stdout/stderr/result/metrics 外还有 exit_code /
  error_message / execution_time_ms / artifacts / session_id;metrics 里
  peak_memory_mb 与 IO 计数依赖沙箱运行时能力,取不到就是 null(实测如此)。

错误信封:本服务与 context-loader 一样没有用 kweaver-go-lib,各自带了一份同源的
infra/errors.HTTPError,字段是 code/description/solution/link/details,与
rest.BaseError 不同。_shared/errors.yaml 原注释把这两个服务列进「统一走 BaseError」
的名单属误述,已改为如实说明,并新增两者共用的 ErrorCompact。

覆盖边界写进模块 README,不含糊:本批次只有函数面 6 个端点,同一服务公开面还有
约 80 个端点未文档化(算子 15 / 工具箱 22 / MCP 15 / Skill 25 / 沙箱观测 4 /
导入导出 2),其响应结构未经验证;内部面 internal-v1 与能力面
/api/capabilities-lab/v1 同样不收录。服务目录下的历史草稿可作参照但与实现有漂移
(context-loader 的同类草稿实测就有三处写错),不能直接当真相源。

验证(测试服 14.103.77.23,admin token 打外部面实机核对):
template/python(返回的正是内置模板,入口确为 handler)、function/dependencies、
function/dependency-versions/requests、ai_generate/prompt/python_function_generator
字段全对;function/execute 实跑一次拿到真实响应,据此补全了上述五个字段与
metrics 结构。make api-docs-lint 通过(0 warning),make api-docs-html 渲染正常,
首页出现「执行工厂」分区。

Refs #522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
接着函数面往下补,本次加 sandbox(4)与 impex(2),公开面覆盖到 12 / 89。

收录范围的判据写进模块 README:只收 Ingress 暴露的 /api/agent-operator-integration/v1
——那是浏览器(Studio)能到达的面。内部面 internal-v1 刻意不挂 Ingress:它不校验
令牌,身份取自调用方自填的 X-Account-ID 头,该头缺失时调用者被降级为硬编码管理员,
一旦暴露其下约 40 条写接口即可从集群外无凭据调用(chart values 注释与 #326)。
因此不写进任何对外文档。

按代码与实机返回,记下几处会踩的地方:

- 沙箱四条只读接口**仅超管可见**。它们原本只在内部面,为了 Studio 的沙箱运行时页
  才开到公开面,公开面拿到经校验的身份后再叠一道超管判定收口,非超管 403。
- **/sandbox/pool 的 session_resources 键是首字母大写的**(CPU/Memory/Disk/Timeout):
  该结构直接来自服务端配置对象,只带 yaml 标签没有 json 标签,序列化用的是 Go 字段名;
  而会话对象里的 resource_limit 是小写(cpu/memory/disk/max_processes)。两套命名,
  解析时极易混。实测确认。
- 会话里的 user_id / user_name 是执行请求自带的追踪标记,不是经校验的身份,
  不能拿来做审计结论。
- 会话详情的 requested_dependencies 与 installed_dependencies 对不上即装包出了问题。
- impex 导入不是发 JSON:Content-Type 必须 multipart/form-data(否则 415),
  文件字段名固定 data(不是 file),且必须带 x-business-domain 头。
- impex 导出响应是 application/json + Content-Disposition 附件头,.adp 就是 JSON。

只读 GET 上标了 x-contract-probe,待巡检工具支持该扩展(#578)后生效,
在那之前是惰性标注;README 已注明。

验证(测试服 14.103.77.23,admin token 打外部面):/sandbox/health、/sandbox/pool、
/sandbox/sessions 三条实机核对,字段全对,session_resources 的大写键即由此发现。
make api-docs-lint 通过(三份新文件 0 warning)。

Refs #522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
实测发现的 session_resources 键名问题已开成 issue #585(漏 json 标签,
修复是破坏性改动)。文档里补一句指向,避免读者把现状当成设计。

Refs #522, #585

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
公开面覆盖 12 → 27(共 89)。算子是把一段能力固化成可复用、可版本化、可发布的
产物——function.yaml 那边是「跑一段临时代码」,这里是「带元数据、执行控制与生命
周期地存下来」。

从代码里核出的、不看文档就会踩的点:

- 注册按 operator_metadata_type 分叉必填字段:function 走 function_input
  (含 code 与入参出参定义),openapi 走 data(OpenAPI 原文字符串)。
- 生命周期 unpublish → published → offline,已发布后再编辑进 editing,
  每次编辑产生新 version;市场只收 published / offline,传另两个状态 400。
- **page_size 传负数等价于不分页全量返回**(服务端翻成 all=true),正常范围 1-100。
- **DELETE /operator/delete 带请求体,且请求体是数组**——不少 HTTP 客户端默认
  不给 DELETE 发 body。
- POST /operator/status 也是数组,一次改多个算子的状态。
- 编辑走 POST /operator/info 而非 PUT,算子由请求体里的 operator_id 指定。
- /operator/names 对不存在的 ID 静默略过、不占位,调用方要自己比对;
  与 /tool-box/names、/skills/names 同契约。
- 调试打的是具体 version 而非「当前版本」,version 必填。
- 分页信封是本服务通用的 {total, page, page_size, total_pages, has_next,
  has_prev, data},分页参数是 page / page_size。

实机核对(测试服 14.103.77.23):/operator/category 的枚举直接取自实机返回;
/operator/info/list 与 /operator/market 的分页信封实机确认(该环境无算子,
data 为空数组)。其余字段按 Go 类型写,README 已如实标注验证程度分两级。

Refs #522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
公开面覆盖 27 → 43。同时修一处我自己写错的地方。

**修正:时间戳是纳秒不是毫秒。** operator.yaml 上一版把 create_time / update_time /
release_time 写成「毫秒时间戳」,错的——全服务统一 time.Now().UnixNano(),
实测 MCP 列表返回 1784880971306127803。按毫秒解析会落到 1970 年附近。已改,并把
这条作为服务级约定写进两份文件的 info.description 与模块 README。

**修正:端点总数 89 → 90。** MCP 的 Any /mcp/app/{mcp_id}/mcp(Streamable HTTP
端点)此前统计漏了——抽路由的正则只匹配 GET/POST/PUT/DELETE/PATCH,没算 Any。

MCP 面本身,从代码与实机核出的点:

- 两个方向别混:/mcp/proxy/... 是平台代你调**外部** MCP;/mcp/app/... 是平台把
  自己的工具**对外**暴露成 MCP Server 给客户端连。creation_type 区分二者
  (custom 登记外部 / tool_imported 由工具箱组装)。
- **mode 在新增与更新时可选范围不同**:POST /mcp/ 只接受 sse、stream,
  PUT /mcp/{mcp_id} 还接受 stdio_uv、stdio_npx(两处 validate 标签不一致)。
  也就是 stdio 类模式没法直接新建,只能建完再改过去。不像有意设计,已写明。
- **新增接口的路径带尾斜杠**:路由注册的是 /mcp/ 而非 /mcp。Gin 会重定向,
  但重定向丢 POST body 的客户端不少,直接写全路径最稳。
- 详情响应分 base_info 与 connection_info:客户端要连的是后者的 sse_url /
  stream_url,不是 base_info.url(那是上游地址)。stream_url 为空即不支持流式。
- 市场批量接口 /mcp/market/batch/{mcp_ids}/{fields} 两个参数都走**路径**、都是
  逗号分隔;fields 有白名单(13 个),不在白名单的字段名被静默忽略;响应是按
  字段投影后的裸 map 数组,故不给固定 schema。
- 工具调试与代理调用**报错也返回 200**,成败看 is_error,不能看 HTTP 状态码。
- MCP 协议结构里的 inputSchema 是驼峰,与本服务其余 snake_case 字段不同。

实机核对(测试服 14.103.77.23):/mcp/list 与 /mcp/market/list 拿到真实数据,
分页信封与条目字段据此写成,示例即实测报文;纳秒时间戳也由此发现。

Refs #522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
公开面覆盖 43 → 65(共 90)。工具箱是一组工具的容器,也是权限与发布的单位——
Agent 挂载的是工具箱不是单个工具。

从代码与实机核出的点:

- **两层状态别混**:工具箱是 unpublish/published/offline(**没有算子那边的
  editing**),箱内每个工具是 enabled/disabled。工具箱已发布不代表箱内工具都启用。
- **列表与详情的 tools 含义不同**:列表条目里的 tools 是**工具名字符串数组**
  (实测:["search_schema","query_object_instance",...]),详情里才是工具对象数组。
- **箱内工具列表的数据字段叫 tools 不是 data**,还多一个 box_id——与本服务其他
  分页接口(含同模块的 /tool-box/market/tools)不一致。
- **/tool-box/market/tools 的 tool_name 是必填**,不传直接 400。这不是「列出全部
  市场工具」而是「按名字找工具」。实测报文已作为 400 示例写进文档。
- 工具箱与工具的更新都走 **POST 不是 PUT**,且都是整体覆盖:更新工具箱要求
  box_name/box_desc/box_category 全给,更新工具要求 name/description/metadata_type
  全给。metadata_type 在工具箱更新时反而可选(建成后类型不变,带了才校验)。
- **建工具是批量操作**:一份 OpenAPI 文档里有几个操作就建几个工具,因此响应是
  success_count/failure_count,**部分失败仍返回 200**。openapi-bundle 同理。
- 代理调用返回的是**被调服务的原始响应**(status_code/headers/body),上游返 500
  时本接口仍是 200,实际状态在 status_code 里。
- global_parameters 是**单个对象不是数组**。
- /tool-box/market/{box_id}/{fields} 的路径段名是单数 box_id,实际收的是逗号分隔
  的 ID 列表(服务端字段就叫 BoxIDs),与 MCP 的 batch 接口同一套路子。

顺带修正 _shared/errors.yaml:错误码里的服务名前缀是 **AgentOperatorIntegration**
(首字母大写),我上一版写成了小写的 agentOperatorIntegration。实测报文为
AgentOperatorIntegration.BadRequest.ValidationRequired,已按实机改正并补进示例。

实机核对(测试服 14.103.77.23):/tool-box/list 拿到 45 条真实数据,分页信封与
条目字段据此写成,示例即实测报文(contextloader 内置工具集);
/tool-box/market/tools 的必填校验也是实机撞出来的。

Refs #522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
公开面覆盖 65 → 90,/api/agent-operator-integration/v1 全部收录完毕。
文档里的端点数与从代码 RegisterPublic 抽出的路由表逐条对上:函数 6 + 沙箱 4 +
导入导出 2 + 算子 15 + MCP 16 + 工具箱 22 + Skill 25 = 90。

Skill 面从代码与实机核出的点:

- **注册与更新技能包不收 JSON**:只接受 multipart/form-data 或
  application/x-www-form-urlencoded,其他直接 400 "unsupported content type"。
  file_type 决定 file 怎么解读(zip 压缩包 / content 直接给内容)。
- **三条读取路径别用错**:/content、/files/read、/download 读的是**已发布版本**
  (Agent 运行时用);/management/* 读的是**当前草稿**含未发布改动(Studio 编辑页
  用);/history 系列是历史版本。拿错会看到不一致的内容,文档里做了对照表。
- **发布接口只接受 published / offline**,改不回 unpublish / editing;且本面用
  PUT 而算子与工具箱的同类接口用 POST。
- history/republish 是把旧版本**回灌成草稿**,history/publish 是**直接发布**旧版本
  跳过草稿——名字接近,语义相反,容易调错。
- /files/read 返回的是文件的 **url 而不是内容**,拿到地址再取。
- 执行技能的响应里 **mocked 为 true 表示并没有真的在沙箱里跑**(降级路径),
  stdout/stderr 不是真实结果;成败同样看 exit_code 而非 HTTP 状态码。
- 索引构建任务状态有 5 个值,但**列表接口的 status 过滤白名单只有 4 个**,
  传 canceled 会 400——尽管任务确实会进入该状态。
- 索引任务里的 create_user 是账号 UUID,而技能条目里的 create_user 是显示名
  (Administrator),同名字段两种形态。

实机核对(测试服 14.103.77.23):GET /skills 与 GET /skills/index/build 拿到真实
数据,分页信封与条目字段据此写成,索引任务示例即实测报文。

模块 README 写清验证程度分两级,不含糊:路由与收录范围 90 条全部逐条核过;
字段级只有实机打过的 17 条算验证过(函数 5 / 沙箱 3 / 算子 3 / MCP 2 / 工具箱 2 /
Skill 2),其余 73 条按 Go 类型写成、未经实机验证,改动时需人工核对,或等 #578
合入后用 x-contract-probe 把只读 GET 纳入巡检。

Refs #522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#578 已合入 main,其 9 份 YAML 按 ErrorAgentRetrieval 引用共享错误 schema。
本分支原本另起了一个 ErrorCompact,rebase 后两者并存。收敛为:ErrorCompact 是
规范定义(覆盖 context-loader 与 execution-factory 两个同源信封),
ErrorAgentRetrieval 保留为它的 allOf 别名——不改已合入文件的引用,也不重复定义
属性。错误码前缀按实测改为 AgentOperatorIntegration(首字母大写)。

另修 Makefile 的 RESNAME 命名空间冲突:context-loader 与 execution-factory 都有
mcp.yaml / skill.yaml,扁平的 RESNAME_<资源> 会互相覆盖,导致 context-loader 的
侧栏名被本分支改掉。print-resname 改为先查 RESNAME_<模块>_<资源>、再回落
RESNAME_<资源>,两个模块的同名文件各自显示正确(实测渲染确认)。

Refs #522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sh00tg0a1
sh00tg0a1 force-pushed the docs/522-execution-factory-api branch from 96d1f61 to 3e1f41c Compare August 1, 2026 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant