¶给 Agent 搞个干活“工具人”:一个 HTTP 小服务带来的改变
这篇不是讲“如何零代码搭一个 AI 工作流平台”,也不是讲“某个大模型能力有多牛”。
它讲的是一件更朴素的事:
我为什么宁愿多写一个看起来“很普通”的 HTTP 小服务,也不想再在编排工具里堆一堆复杂节点。
如果你也在做 Agent / 工作流,已经开始被流程图、脚本逻辑和大模型输出共同折磨,这篇可能会对你有点参考价值。
¶一、先把话说白:这个 Agent 工具服务具体在干什么?
我这个服务的定位特别简单粗暴:
给 Agent 和工作流,提供一组“能干具体活”的 HTTP 工具接口。
它大概是在做这几类事情(抽象说法,不贴项目细节):
-
一类:复杂一步活“打包”为一次 HTTP 调用
- 上游发来一个结构化请求(JSON),里面写清楚:你想干什么,需要多少条数据,需要什么字段。
- 服务内部会:
- 按要求调大模型做内容生成或理解;
- 用普通 Java 代码去重、补字段、格式整理;
- 把结果导出成文件(比如表格),存到临时存储里;
- 生成一个下载 token + 一份 Markdown 预览,方便人或其他系统查看。
- 对调用方来说:
- 只看到“一次请求 → 一个结构化响应”,不需要关心中间调了几次大模型、做了多少后处理。
-
另一类:配套的小工具能力
- 自检大模型配置:
- 发一个简单问句,确认 baseUrl / model / apiKey 这些都没问题,把延迟和部分响应内容打出来。
- 做一些和 Agent 工作流强相关的基础活:
- 日志记录和脱敏;
- 临时文件存储;
- 统一的错误结构和返回码。
- 自检大模型配置:
用一句话讲,这个服务就在做两件事:
- 帮你跟大模型、文件、复杂逻辑打交道;
- 把最后的结果通过一个 HTTP 接口,干净地交给上游。
¶二、为什么这些逻辑不适合全堆在编排工具 / Agent 脚本里?
一开始我也以为:“有了编排平台,很多东西拖一拖节点就完了,何必再写服务?”
实践下来,几个很具体的问题把我劝退了。
¶1. 某些步骤内部太复杂,用节点堆出来“理论可行、实际难维护”
想象一个很现实的需求:
- 需要批量生成一批结构化内容(不一定是题库,也可能是某种配置、表格数据等);
- 要求数量要对;
- 内容不能重复;
- 字段要统一;
- 结果要导出成 Excel / CSV 给运营同学用。
如果你完全在编排工具里做,大概会变成这样:
- 一堆“大模型调用”节点,控制生成数量;
- 中间插着各种“json 解析”、“分支判断”、“重试”节点;
- 后面再加一堆“数组处理”、“格式转换”、“上传文件”的节点。
能跑起来,但一眼看过去就知道:
一旦需求稍微变形,这条流程图谁都不敢动。
¶2. 需要精细的资源与异常控制,编排层很难做细
比如:
- 大模型调用是 IO 密集 + CPU 也会吃一点,想控制并发度和线程池大小;
- 某些步骤你希望有明确的“总超时时间”和“每批超时时间”;
- 解析 JSON 失败时,希望能给出可读的错误信息,而不是一个“未知异常”。
这些事情,用一门普通语言(Java/Go/Python 都行)写在服务里,逻辑、日志、测试都有办法做得比较细。
但如果你全靠编排工具,能拼出来,
但很难做到“这一块就交给一个人负责、写单元测试、看日志、做性能调优”。
¶3. 逻辑复制粘贴问题:同一套“大模型 + 解析 + 清洗”,到处都是
如果你把所有逻辑写在 Agent 脚本里,会发生什么?
- 每个 Agent 都有一段类似的“模型调用 + 解析 + 清洗 + 导出”逻辑;
- 需要改格式 / 换模型 / 调整规则时,你得去每个地方改一遍;
- 很快就没有人弄得清“线上到底在跑哪一版逻辑”。
这其实是一个很典型的“应该收敛成服务能力,却被复制成脚本逻辑”的问题。
所以最后的落地选择是:
把那些“步骤内部很复杂,但对外可以收敛成一个动作”的事情,抽进一个 HTTP 工具服务里。
工作流只需要把它当“黑盒步骤”去调用。
¶三、设计这个服务时,我只盯着三个特别务实的目标
我没有想着做一个所谓“Agent 能力平台”,只盯着三件很务实的事:
- 单一职责,别搞成另一个大一统平台
- 可复用,任何会发 HTTP 的东西都能用
- 好运维,能按正常微服务那一套去部署监控
¶1. 单一职责:谁该干啥,就只干那点事
在服务内部,我基本按下面这种划分来做:
-
控制器(Controller):
- 只做 HTTP 协议相关的事情:参数接收、校验、返回
200/400/500这种状态; - 打一日志:谁调了这个接口,带了什么参数;
- 捕获异常、包装成统一的错误响应结构。
- 只做 HTTP 协议相关的事情:参数接收、校验、返回
-
业务服务(Service):
- 聚焦某一类“工具能力”,比如“批量生成结构化内容 + 去重 + 导出文件”;
- 内部可以包含多次模型调用、各种小算法,但对外就是“一次 HTTP 请求”。
-
基础设施(Infra):
- LLM 调用适配:
- 屏蔽不同厂商 API 差异;
- 统一控制超时时间、重试策略;
- 统一打日志。
- 临时文件存储:
- 管理文件路径、过期时间、下载 token;
- 日志配置、OpenAPI 文档、Docker 打包等。
- LLM 调用适配:
这样做的好处是:
- 新需求进来时,大概率只改某个 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 | 好的,下面是为你生成的内容: |
你让它只给 JSON,它依然会在前后加评价、加说明。
在服务里,我会做这几步:
- 从原始文本中“抠出” JSON 数组:
- 找到
[和]的合理区间; - 去掉 Markdown 代码块包裹;
- 去掉
<think>之类非 JSON 内容(如果模型会加)。
- 找到
- 用 JSON 解析器解析成对象列表;
- 解析失败时,记录原始文本 + 错误堆栈,返回一个结构化的错误响应,而不是直接 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 客户端逻辑,这对很多传统系统是不现实的。
所以我的顺序是:
- 先用最简单的 HTTP + JSON,把核心能力抽象稳定;
- 等 MCP 在团队内部的使用场景足够多,再考虑在外面包一层 MCP 适配。
¶六、这个 HTTP 工具服务在实际里的用法
说几个已经在用、而且挺顺手的模式。
¶模式一:工作流里当成“黑盒步骤”
- 上游节点:准备好参数(通常是一个 JSON 对象),填到 HTTP 调用节点里;
- 调这个服务的某个接口,等待返回结构化结果;
- 后续节点只处理这些结果,不关心里面调了几次大模型。
效果是:
流程图变短了,
很多原本拆成 7、8 个节点的东西,收敛成一个 HTTP 节点。
¶模式二:Agent 把“难啃的骨头”一次甩给服务
- Agent 负责决定“什么时候需要用这个能力”;
- 一旦确定要用,就把上下文和配置打包成请求,丢给工具服务;
- 工具服务负责和大模型、文件系统、规则逻辑打交道,Agent 只看最终结果和状态。
效果是:
Agent 的 Prompt 不用写得特别长,
它只需要知道:“这个接口会帮我完成某类标准化的工作”。
¶模式三:纯后端 / 运营任务也直接用
有些场景甚至不走 Agent:
- 后台管理页直接调用这个服务生成一批结构化结果;
- 定时任务每天跑一次,用来产出某种报表或模板。
这时候,这个工具服务就成了一个通用的“结构化生成 + 转换”后端能力,
顺带也减轻了 Agent 那边的负担。
¶七、写在最后:给 Agent 找到自己的“工具人”
回头看,这个小小的 HTTP 工具服务并没有什么“炫技”的地方,更多是一些很朴素的取舍:
- 复杂逻辑不再硬塞进流程图和 Agent 脚本,而是收敛到一个专门干活的服务里;
- 和大模型打交道的坑(格式不稳、数量不准、内容重复)集中在一处解决,而不是到处复制 paste;
- 把大家已经熟悉的 HTTP 微服务体系继续用下去,而不是为了“跟风”换一整套栈。
如果你现在也在做 Agent / 工作流,已经感觉到编排工具和脚本越来越难 hold 住复杂步骤,
也许可以考虑:先别急着再造一个平台,先像这样抽一个“小工具人服务”出来,把最难啃的那几块活接过去。
等这块基础垫稳了,后面不论是接 MCP、换模型、上新的编排框架,都会轻松很多。