给 Agent 搞个干活“工具人”:一个 HTTP 小服务带来的改变

给 Agent 搞个干活“工具人”:一个 HTTP 小服务带来的改变

这篇不是讲“如何零代码搭一个 AI 工作流平台”,也不是讲“某个大模型能力有多牛”。
它讲的是一件更朴素的事:
我为什么宁愿多写一个看起来“很普通”的 HTTP 小服务,也不想再在编排工具里堆一堆复杂节点。

如果你也在做 Agent / 工作流,已经开始被流程图、脚本逻辑和大模型输出共同折磨,这篇可能会对你有点参考价值。


一、先把话说白:这个 Agent 工具服务具体在干什么?

我这个服务的定位特别简单粗暴:
给 Agent 和工作流,提供一组“能干具体活”的 HTTP 工具接口。

它大概是在做这几类事情(抽象说法,不贴项目细节):

  • 一类:复杂一步活“打包”为一次 HTTP 调用

    • 上游发来一个结构化请求(JSON),里面写清楚:你想干什么,需要多少条数据,需要什么字段。
    • 服务内部会:
      • 按要求调大模型做内容生成或理解;
      • 用普通 Java 代码去重、补字段、格式整理;
      • 把结果导出成文件(比如表格),存到临时存储里;
      • 生成一个下载 token + 一份 Markdown 预览,方便人或其他系统查看。
    • 对调用方来说:
      • 只看到“一次请求 → 一个结构化响应”,不需要关心中间调了几次大模型、做了多少后处理。
  • 另一类:配套的小工具能力

    • 自检大模型配置:
      • 发一个简单问句,确认 baseUrl / model / apiKey 这些都没问题,把延迟和部分响应内容打出来。
    • 做一些和 Agent 工作流强相关的基础活:
      • 日志记录和脱敏;
      • 临时文件存储;
      • 统一的错误结构和返回码。

用一句话讲,这个服务就在做两件事:

  1. 帮你跟大模型、文件、复杂逻辑打交道;
  2. 把最后的结果通过一个 HTTP 接口,干净地交给上游。

二、为什么这些逻辑不适合全堆在编排工具 / Agent 脚本里?

一开始我也以为:“有了编排平台,很多东西拖一拖节点就完了,何必再写服务?”

实践下来,几个很具体的问题把我劝退了。

1. 某些步骤内部太复杂,用节点堆出来“理论可行、实际难维护”

想象一个很现实的需求:

  • 需要批量生成一批结构化内容(不一定是题库,也可能是某种配置、表格数据等);
  • 要求数量要对;
  • 内容不能重复;
  • 字段要统一;
  • 结果要导出成 Excel / CSV 给运营同学用。

如果你完全在编排工具里做,大概会变成这样:

  • 一堆“大模型调用”节点,控制生成数量;
  • 中间插着各种“json 解析”、“分支判断”、“重试”节点;
  • 后面再加一堆“数组处理”、“格式转换”、“上传文件”的节点。

能跑起来,但一眼看过去就知道:
一旦需求稍微变形,这条流程图谁都不敢动。

2. 需要精细的资源与异常控制,编排层很难做细

比如:

  • 大模型调用是 IO 密集 + CPU 也会吃一点,想控制并发度和线程池大小;
  • 某些步骤你希望有明确的“总超时时间”和“每批超时时间”;
  • 解析 JSON 失败时,希望能给出可读的错误信息,而不是一个“未知异常”。

这些事情,用一门普通语言(Java/Go/Python 都行)写在服务里,逻辑、日志、测试都有办法做得比较细。
但如果你全靠编排工具,能拼出来,
但很难做到“这一块就交给一个人负责、写单元测试、看日志、做性能调优”

3. 逻辑复制粘贴问题:同一套“大模型 + 解析 + 清洗”,到处都是

如果你把所有逻辑写在 Agent 脚本里,会发生什么?

  • 每个 Agent 都有一段类似的“模型调用 + 解析 + 清洗 + 导出”逻辑;
  • 需要改格式 / 换模型 / 调整规则时,你得去每个地方改一遍;
  • 很快就没有人弄得清“线上到底在跑哪一版逻辑”。

这其实是一个很典型的“应该收敛成服务能力,却被复制成脚本逻辑”的问题。

所以最后的落地选择是:

把那些“步骤内部很复杂,但对外可以收敛成一个动作”的事情,抽进一个 HTTP 工具服务里。
工作流只需要把它当“黑盒步骤”去调用。


三、设计这个服务时,我只盯着三个特别务实的目标

我没有想着做一个所谓“Agent 能力平台”,只盯着三件很务实的事:

  1. 单一职责,别搞成另一个大一统平台
  2. 可复用,任何会发 HTTP 的东西都能用
  3. 好运维,能按正常微服务那一套去部署监控

1. 单一职责:谁该干啥,就只干那点事

在服务内部,我基本按下面这种划分来做:

  • 控制器(Controller):

    • 只做 HTTP 协议相关的事情:参数接收、校验、返回 200/400/500 这种状态;
    • 打一日志:谁调了这个接口,带了什么参数;
    • 捕获异常、包装成统一的错误响应结构。
  • 业务服务(Service):

    • 聚焦某一类“工具能力”,比如“批量生成结构化内容 + 去重 + 导出文件”;
    • 内部可以包含多次模型调用、各种小算法,但对外就是“一次 HTTP 请求”。
  • 基础设施(Infra):

    • LLM 调用适配:
      • 屏蔽不同厂商 API 差异;
      • 统一控制超时时间、重试策略;
      • 统一打日志。
    • 临时文件存储:
      • 管理文件路径、过期时间、下载 token;
    • 日志配置、OpenAPI 文档、Docker 打包等。

这样做的好处是:

  • 新需求进来时,大概率只改某个 Service;
  • 控制器层不用被业务细节污染;
  • 和大模型的交互逻辑集中在一处,可调可观测。

2. 可复用:只要能发 HTTP,就能用

相比起“某个 SDK”或者“某个本地插件”,HTTP + JSON 是目前最通用的一种能力暴露方式

  • 后端服务直接用自己熟悉的 HTTP 客户端库就能调用;
  • 前端 / BFF 可以通过网关转发;
  • 传统工作流平台,可以直接加一个 HTTP 节点调用;
  • 命令行、脚本、CI/CD 也都能用 curl 之类调试。

这意味着:

只要我把这套能力打包成 HTTP 接口,就不怎么依赖上游用的是哪一套 Agent 框架或编排平台。

3. 好运维:当成一个普通微服务来管

这一点很现实:

  • 我们已经有一套成熟的 Spring Boot + Docker 的体系;
  • 监控平台、日志平台、网关、灰度发布都围绕 HTTP 微服务建好的。

把工具能力做成 HTTP 服务,最大的好处之一就是:

  • 它能直接接入这套现有体系:
    • 用容器参数来控制 JVM 内存、GC 策略;
    • 使用统一的日志格式、日志滚动策略;
    • 用健康检查接口接入监控;
    • 用网关来做鉴权、限流、熔断。

如果我上来就做一个 MCP 服务,反而会脱离当前这套稳定的基础设施,这其实是我暂时没有这么做的关键原因之一。


四、和大模型合作这件事,我在服务里都做了什么

说点细节,会更清晰一些。

1. 把 Prompt 写成“协议说明”,不是随便聊两句

比如我要让模型生成一批结构化结果,我不会只说:

“请帮我生成 XX 条数据,用 JSON 返回。”

我会在系统提示和用户提示里,把约束写得很“工程化”,类似于:

  • 必须返回严格的 JSON 数组;
  • 数组长度必须等于 N,不能多也不能少;
  • 每个元素必须包含哪些字段(字段名要严格一致);
  • 不允许有额外解释文字、不要加 Markdown 代码块;
  • 某个字段应该包含什么语义上的信息。

这样做的目的不是“提高成功率”这么空泛,而是:

让服务端有足够的信息做机械的校验,比如数量、字段名、某些必填字段。

2. 默认认为模型输出“不干净”,所以要有清洗流程

现实里,模型非常喜欢这样输出:

1
2
3
4
5
6
好的,下面是为你生成的内容:


[
...
]

你让它只给 JSON,它依然会在前后加评价、加说明。

在服务里,我会做这几步:

  1. 从原始文本中“抠出” JSON 数组:
    • 找到 [] 的合理区间;
    • 去掉 Markdown 代码块包裹;
    • 去掉 <think> 之类非 JSON 内容(如果模型会加)。
  2. 用 JSON 解析器解析成对象列表;
  3. 解析失败时,记录原始文本 + 错误堆栈,返回一个结构化的错误响应,而不是直接 500。

这件事如果散落在每个 Agent 里,就会到处都是“差不多的正则 + try-catch”;
集中在工具服务里,就变成一个可以单独演进、统一维护的“清洗模块”。

3. 把业务规则放在代码里,而不是只写在 Prompt 里祈祷

例如,有几个常见的情况:

  • 数量不对

    • 说好要 50 条,它给了 47 条;
    • 做法:
      • 服务端算一下数量,如果不足,就再触发一次补生成;
      • 最终截断或补齐到需要的数量。
  • 字段不统一

    • 有的元素多一个字段,有的少一个字段,字段顺序不一致;
    • 做法:
      • 用第一条记录的字段作为“列模板”;
      • 扫一遍所有记录,把额外字段加到末尾;
      • 对缺少字段的记录,用空值补齐。
  • 内容重复

    • 看起来不同,其实表达的是类似的内容;
    • 最基础的一步,先用一个简单的“签名”(比如基于某个主字段的字符串)做去重,
      更复杂的相似度检测可以后面再加。

这些逻辑,如果只写在 Prompt 里:“请不要重复、请确保数量正确”,
你会一遍遍发现现实和你想象的不一样
把它收在服务里以后,就变成了可见、可测、可调的“业务规则”。


五、为什么我现在选的是 HTTP 接口,而不是直接做 MCP 服务?

这部分我尽量讲具体一点,不绕术语。

1. 谁来用这个服务?只靠 MCP 生态不够

目前实际情况大致是这样:

  • 一部分 Agent / 工具确实跑在支持 MCP 的环境里(比如某些编辑器、框架);
  • 但还有很多调用方非常“传统”:
    • 后端 REST 服务;
    • BFF / 网关;
    • 现有的工作流平台(它只认识 HTTP 节点);
    • 定时任务、脚本、CI 任务等。

如果我一开始就只暴露 MCP 接口,这些调用方几乎都用不上。

而 HTTP + JSON 的好处是:

  • 任何环境,只要能发 HTTP 请求,就能用;
  • 调试也简单:curl / Postman / 浏览器都行。

所以我的实际选择是:

把能力的“底层实现”和“统一接口形态”先落在 HTTP 上,
MCP 以后可以作为“这套 HTTP 能力的其中一个适配壳”。

也就是说,HTTP 是“内核接口”,MCP 反而是可以在外面再加的一层皮,这样不会把自己绑死在某个生态里。

2. 部署与运维:HTTP 服务可以无缝接入现有体系

现在的基础设施大多围绕“HTTP 微服务”建好的:

  • Spring Boot 项目 → 打成 Jar 或 Docker 镜像;
  • 发布到 K8s 或虚机上;
  • 网关 / API 网关 做统一鉴权、路由、限流;
  • Prometheus / Grafana / ELK 做监控和日志。

把工具能力做成 HTTP 服务的结果是:

  • 运维侧几乎不需要学习新东西,按“普通微服务”来管就行;
  • 可以很容易插入:
    • 请求级别的限流;
    • 鉴权策略;
    • 灰度策略(按一些 Header 或路径分流)。

而 MCP 服务的运行模型更多是:

  • 被某个宿主应用拉起来;
  • 宿主负责和 MCP 通信、转发请求;
  • 运维、监控、日志更多依赖宿主对 MCP 的支持。

在我现有的团队环境里,HTTP 服务的接入成本要小得多,这就是为什么我现在先选了 HTTP。

3. 安全模型和边界控制更清晰

HTTP 服务这条路上的安全模型,相对成熟:

  • 可以挂在内网,只通过网关或特定服务访问;
  • 可以验 Token / JWT / mTLS 等;
  • 可以做 IP 白名单、Rate Limit、审计日志。

而 MCP 更多设计在“本地工具对接 Agent”这条路上,
如果我要把它暴露到跨团队、跨服务的调用场景里,
安全边界如何划、谁能调、从哪儿调、怎么审计,要重新设计一套东西。

对我当前的需求来说:

一个明确的 HTTP 边界 + 网关 + 内网访问控制,就已经能满足“只给可信方调用”的要求了。

MCP 的协议更偏向“宿主 ↔ 工具”的能力协商,对使用它的 Agent 来说很友好,
但对不支持 MCP 的系统来说,要用就得先引一整套 MCP 客户端逻辑,这对很多传统系统是不现实的。

所以我的顺序是:

  1. 先用最简单的 HTTP + JSON,把核心能力抽象稳定;
  2. 等 MCP 在团队内部的使用场景足够多,再考虑在外面包一层 MCP 适配。

六、这个 HTTP 工具服务在实际里的用法

说几个已经在用、而且挺顺手的模式。

模式一:工作流里当成“黑盒步骤”

  • 上游节点:准备好参数(通常是一个 JSON 对象),填到 HTTP 调用节点里;
  • 调这个服务的某个接口,等待返回结构化结果;
  • 后续节点只处理这些结果,不关心里面调了几次大模型。

效果是:

流程图变短了,
很多原本拆成 7、8 个节点的东西,收敛成一个 HTTP 节点。

模式二:Agent 把“难啃的骨头”一次甩给服务

  • Agent 负责决定“什么时候需要用这个能力”;
  • 一旦确定要用,就把上下文和配置打包成请求,丢给工具服务;
  • 工具服务负责和大模型、文件系统、规则逻辑打交道,Agent 只看最终结果和状态。

效果是:

Agent 的 Prompt 不用写得特别长,
它只需要知道:“这个接口会帮我完成某类标准化的工作”。

模式三:纯后端 / 运营任务也直接用

有些场景甚至不走 Agent:

  • 后台管理页直接调用这个服务生成一批结构化结果;
  • 定时任务每天跑一次,用来产出某种报表或模板。

这时候,这个工具服务就成了一个通用的“结构化生成 + 转换”后端能力
顺带也减轻了 Agent 那边的负担。


七、写在最后:给 Agent 找到自己的“工具人”

回头看,这个小小的 HTTP 工具服务并没有什么“炫技”的地方,更多是一些很朴素的取舍:

  • 复杂逻辑不再硬塞进流程图和 Agent 脚本,而是收敛到一个专门干活的服务里;
  • 和大模型打交道的坑(格式不稳、数量不准、内容重复)集中在一处解决,而不是到处复制 paste;
  • 把大家已经熟悉的 HTTP 微服务体系继续用下去,而不是为了“跟风”换一整套栈。

如果你现在也在做 Agent / 工作流,已经感觉到编排工具和脚本越来越难 hold 住复杂步骤,
也许可以考虑:先别急着再造一个平台,先像这样抽一个“小工具人服务”出来,把最难啃的那几块活接过去。
等这块基础垫稳了,后面不论是接 MCP、换模型、上新的编排框架,都会轻松很多。

0%