Agent 工程化新底座:用 CLI 契约层打通 HTTP 接口与业务能力
变化分层Agent 基建应该压在哪一层如果把 AI 应用的技术栈按变化速度拆开会发现它不是一条直线而是一组稳定性差异很大的层。图越往底层越适合承载长期业务契约。模型和 Agent 框架适合快速试错不适合作为业务基础设施的根。原因很简单它们变化太快团队越是把能力绑定在这些层上后续迁移成本越高。更稳的切入点是业务能力契约。它向下连接 HTTP / RPC向上暴露给不同 Agent自己负责把接口能力、权限边界、调用说明和返回结构固定下来。这样一来业务系统不需要跟着每个 Agent 框架重做一遍接入。CLI Contract Layer把能力边界变成模型看得懂的命令工具协议当然有价值但它并不总是最低成本的入口。对于大量业务接口来说CLI 有几个天然优势。第一命令行是模型熟悉的表达方式。预训练语料里有大量 shell、CI/CD、运维脚本和命令帮助信息模型知道如何读-h、如何看Usage、如何组合子命令。第二CLI 支持渐进式披露。模型不需要一开始拿到全部工具 schema它可以先看一级命令再看二级命令最后只读取目标命令的参数。第三命令可以被真实执行、干跑、记录和审计工程闭环比较直接。这并不意味着 CLI 要取代所有工具协议。更合理的定位是把 CLI 作为业务能力的稳定契约层让MCP、Workflow、Agent Runtime等上层系统按需消费它。图CLI 位于 Agent 编排与后端接口之间承接稳定调用契约。这里的关键不是“写一堆命令”而是把命令变成契约它必须知道自己面向谁、能做什么、不能做什么、参数怎么裁剪、结果怎么返回、失败时如何解释。Command Metadata用配置把接口编译成命令手写 CLI 命令很快会失控。业务接口多、字段多、变更频繁如果每接一个接口都写一份命令代码最后维护成本会压过收益。更可维护的方案是让接口配置成为事实来源。研发只描述接口的路由、入参、出参、分页规则和说明文案构建器根据配置生成命令定义再由 CLI 框架动态注册命令树。图接口配置成为命令生成、注册、打包和调用的事实来源。一个脱敏后的 API 层配置可以长这样method: POST path: /api/domain/order/list service: order.service pagination: enabled: true page_param: page.page_no size_param: page.page_size default_page: 1 default_page_size: 20 max_page: 20 max_page_size: 50 merge_path: data.items total_path: data.total input: mode: include params: - name: resource-id request_name: resource_id type: string required: true description: 资源 ID只能传当前上下文允许访问的资源。 example: --params #x27;{\resource-id\:\123\}#x27; output: mode: include params: - data.total - data.items[*].id - data.items[*].status - data.items[*].updated_at docs: summary: 查询资源列表 description: 按资源 ID 查询当前身份可见的列表数据。 notes: - 只允许传入 resource-id其他字段由服务端上下文注入。 - 分页参数由网关统一处理调用方不要手写下游分页结构。这类配置看起来普通但它解决的是一个非常实际的问题命令不再是散落在代码里的手工逻辑而是一份可生成、可测试、可审计的能力说明。Runtime Gateway模型只表达意图安全留在服务端让 Agent 直接拼接口请求是很危险的设计。模型可能传错身份、误填资源 ID、把不该暴露的字段带出去也可能在上下文混乱时调用到错误环境。更稳的做法是让 CLI 只负责表达意图把真正的鉴权、权限判断、环境识别、字段裁剪和转发都放在服务端网关。图安全边界收敛在服务端网关而不是交给模型自行判断。这个设计的分工很清楚Agent 负责理解用户要做什么CLI 负责把意图变成标准命令网关负责判断这件事能不能做。身份和权限不交给模型决定。通用安全闸口通常包括这几类闸口服务端要判断什么为什么不能交给模型执行环境识别命令来自哪个宿主、是否允许调用模型可能无法区分测试环境、生产环境和本地模拟环境登录态校验当前身份是否真实有效登录态不能进入模型上下文更不能由模型拼接垂直权限校验当前身份是否有该功能权限模型不知道组织权限树也不应猜测水平权限校验当前身份是否能访问目标资源资源 ID 必须来自服务端上下文或可信入口命令白名单只允许已注册命令执行防止模型构造未开放能力参数裁剪只接收配置允许的字段防止模型把多余字段传给下游输出裁剪只返回 Agent 需要的字段降低敏感字段暴露风险有了这层网关上层可以换 Agent也可以换编排框架但安全边界不跟着漂移。API 与 Actions铺量和意图不要混在一起CLI 命令通常需要分两层做。API 层追求覆盖速度。一个配置对应一个 HTTP / RPC 接口适合把现有系统能力快速变成命令。它的命名可能比较贴近接口优点是接入快、成本低。Actions 层追求语义准确。一个命令对应一个业务动作背后可以串多个接口、补参数、做判断、加确认点。它的命名应该更接近用户意图也更适合被 Skill 编排。维度API 层命令Actions 层命令目标快速覆盖已有接口把多步能力封成业务动作命令粒度一个接口一个命令一个动作一个命令命名方式接近 method / path / resource接近领域动作如case diagnose、resource submit编排能力通常只做转发和字段裁剪可串联 HTTP / RPC可做参数加工和状态判断适用阶段初期铺量、能力盘点、内部调试面向 Agent 和 Skill 的稳定交付风险控制依赖通用网关叠加动作级权限、确认点和灰度开关Actions 层配置可以更像“写给模型的操作说明”enable: true cmds: case diagnose strategy: case_diagnose permission: path: /domain/case/diagnose pagination: default_page: 1 default_page_size: 10 max_page: 20 max_page_size: 50 params: - name: case-id type: string required: true description: 问题单 ID。只有用户明确给出问题单时才传。 example: --case-idCASE123 - name: with-detail type: bool required: false description: 是否返回明细。用户要求解释原因或排查证据时才传 true。 docs: summary: 诊断问题单 description: 查询问题单状态、关联资源和可解释的诊断结果。只用于诊断不执行修改。 notes: - 没有 case-id 时不要猜测应先向用户确认。 - 本命令不会提交、撤销或修改任何资源。 - 需要执行修改时应切换到带确认流程的 action。这里最有价值的不是 YAML 本身而是这些描述把模型的取舍写清楚了什么时候该传参数什么时候不能执行什么时候要换命令。Progressive Disclosure让模型按需读取命令面工具太多时把所有 schema 一次性塞进上下文会让模型注意力变差。CLI 的list和-h可以把这个问题拆开。模型先看一级命令确定领域再看二级命令确定动作最后只读取目标命令的参数说明和示例。上下文更小误选工具的概率也会下降。# 查看可用领域 domain-cli list # 查看某个领域下的动作 domain-cli case list # 查看目标命令的参数、示例和边界 domain-cli case diagnose -h # 先 dry-run看即将发出的标准请求 domain-cli case diagnose --case-idCASE123 --with-detailtrue --dry-run为了让模型真的用得稳通用 flag 也要统一设计Flag作用设计要点--params传查询参数只接受 JSON object避免任意字符串透传--data传请求体与--params分开降低 GET / POST 混用--page-all自动翻页服务端控制最大页数--page-size单页条数有默认值和上限--page-limit最大翻页数防止模型无限拉取--dry-run预览请求修改类动作必须先经过确认流程--env指定环境默认生产环境时要更谨慎测试包可注入默认隔离环境-h/--help查看帮助帮助文案要写清楚“什么时候不要用”好的帮助信息不是参数字典而是决策指南。它要告诉模型这个命令解决什么问题不能解决什么问题哪些参数只有在用户明确表达时才能传。Skill Envelope把命令链收成会办事的能力CLI 解决“能力能不能被调用”Skill 解决“任务能不能被办完”。真实用户很少会说“请调用某个接口查询某个字段”。他们会说“帮我排查为什么失败”“看看这个资源能不能提交”“把符合条件的项整理出来”。这些请求背后往往不是一条命令而是一段流程。Skill 的职责就是把命令链、判断逻辑、确认点和输出格式封起来图Skill 负责把命令链、判断逻辑、确认点和输出格式收成任务能力。一个成熟的 Skill 至少要写清楚这些内容模块要写清楚什么适用场景用户用什么说法会触发这个能力可用命令允许调用哪些 CLI禁止调用哪些 CLI参数取舍哪些参数必须来自用户哪些来自上下文哪些不能由模型编风险动作哪些步骤需要二次确认确认文案怎么写输出格式返回表格、诊断结论、下一步建议还是执行结果失败处理权限不足、资源不存在、下游失败时怎么解释这样封装后业务团队交付给 Agent 的就不是一堆接口而是一组可复用、可治理、可升级的能力。测试门禁先验证会拦再谈上线很多团队测试 CLI 时只看“能不能调通”。这不够。对Agent 可调用能力来说更重要的是确认它会在该拦的地方拦住。上线前至少要跑四类检查检查项要验证什么框架校验命令注册、参数类型、帮助信息、输出裁剪是否符合配置登录态失败缺失或过期身份是否被拒绝垂直越权没有功能权限的身份是否无法调用水平越权A 资源上下文是否无法访问 B 资源dry-run写操作是否能先生成可读的执行预览审计日志每次调用是否能追到命令、参数摘要、身份、环境和结果本地模拟也有价值但它只能解决“能否快速调试”的问题。真正的安全边界要在接近真实的环境里验证因为身份、权限和资源隔离往往只有在完整链路里才会暴露问题。构建和发布也建议拆出环境# 本地构建并生成命令树 make build TARGETinternal # 打测试包默认连到隔离环境 make package TARGETinternal CHANNELtest DEFAULT_ENVsandbox # 只在测试验证完成后发布正式包 make publish TARGETinternal CHANNELrelease