把 OpenAPI 接入 Agent Harness:零代码让 Agent 听懂你的 REST API 引言——为什么需要让 AI 听懂 REST API在过去两年里我和团队接手了好几个「上了年纪」的 Java 项目。这些系统有一个共同特征业务逻辑完整API 齐全Swagger 文档也写得规规矩矩但前端换了一波人文档却没人维护了。新来的同事要查一个订单接口得翻好几个 YAML 文件再对着代码比对字段名。更要命的是当我们打算在这些系统上引入 AI 能力时遇到了一个非常实际的矛盾——LLM 不理解 REST API。大语言模型固然强大但它在推理时无法凭空知道你的订单系统有哪些接口、每个接口的入参是什么、响应结构长什么样。让它去查询订单状态它可能会胡编一个接口路径出来。传统做法是为每个接口写工具函数Tool注册到 Agent 里。这个思路没错但当你面对一个拥有上百个 REST 端点的系统时一个一个写 Tool 注册——成本太高了。Solon AI Harness 的addApiServer就是为了解决这个问题而生的。它能从 OpenAPISwagger描述文件或文档 URL 中自动读取 API 定义自动为每个端点生成 Tool 签名Agent 直接通过自然语言调用。零代码让大模型听懂你的 REST API。这篇文章我会用一个完整的订单系统场景带你从头把 OpenAPI 接入 Harness。读完你就能在自己的项目里用起来。场景描述——一个遗留订单系统的窘境假设我们有一个典型的电商订单系统叫order-service。它暴露了大概二十几个 REST 接口涵盖订单 CRUD、库存查询、物流跟踪、售后处理等模块。这个系统有几样「遗产」接口文档存在一个内网地址http://internal-api-doc/order-service/v3/api-docs基于 OpenAPI 3.0代码没有全部迁移到微服务部分逻辑还是单体团队想引入 AI 助手但没人力给每个接口写 Agent Tool这个窘境在不少 Java 项目里都出现过——API 定义得很规范但 AI/LLM 不认识它们。我们需要做的是把 Harness 引擎架设在系统前面告诉它「这是一份 OpenAPI 文档」然后 Agent 就能听懂「帮我查一下订单 20250320001 的物流状态」这样的自然语言请求。添加依赖第一步在pom.xml中加入 Solon AI Harness 的核心依赖。dependencygroupIdorg.noear/groupIdartifactIdsolon-ai-harness/artifactIdversion${solon.version}/version/dependency这里${solon.version}请替换成你项目实际使用的 Solon 版本建议使用 v4.0.2 以上。Harness 本身依赖了 LLM 调用层、Agent 会话管理和 Tool 调度框架不需要额外引入其他库。实现——用 addApiServer 构建 AI 引擎构建 HarnessEngineHarness 的入口是HarnessEngine我们需要给它配置三样东西模型Agent 背后的大脑负责理解用户意图并决定调用哪个工具工作目录Harness 存放会话和缓存的工作区API 源告诉 Harness 你的 OpenAPI 文档在哪直接看代码importorg.noear.solon.ai.harness.HarnessEngine;importorg.noear.solon.ai.harness.permission.ToolPermission;importorg.noear.solon.ai.chat.ChatConfig;importorg.noear.solon.ai.agent.session.AgentSessionProvider;importorg.noear.solon.ai.agent.session.InMemoryAgentSession;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;publicclassOrderAiEngine{publicstaticHarnessEnginecreate(){// 会话提供者AgentSessionProvidersessionProvidernewAgentSessionProvider(){privatefinalMapString,AgentSessionsessionMapnewConcurrentHashMap();OverridepublicAgentSessiongetSession(StringinstanceId){returnsessionMap.computeIfAbsent(instanceId,k-InMemoryAgentSession.of(k));}};// 构建引擎HarnessEngineengineHarnessEngine.of(work,./harness-home).systemPrompt(你是一个订单系统 AI 助手。你可以查询订单、查询库存、跟踪物流、处理售后。使用 OpenAPI 工具时按照文档说明传递参数。如果用户意图不明确主动追问确认。).sessionProvider(sessionProvider).modelAdd(newChatConfig().then(slf-{slf.setApiUrl(https://api.openai.com/v1);slf.setApiKey(sk-xxxxxx);slf.setModel(gpt-4o-mini);})).sandboxEnabled(true);returnengine;}}几个关键点HarnessEngine.of(workspace, harnessHome)需要两个目录参数workspace 定位工作目录harnessHome 放配置和框架产物systemPrompt用来约束 Agent 行为会注入到每次对话的系统消息中生产环境建议开启sandboxEnabled(true)避免 Agent 执行危险操作接入 OpenAPI——addApiServer 的核心用法现在是最关键的一步让 Harness 理解你的 REST API。importorg.noear.solon.ai.harness.source.ApiSource;// 在引擎上注册 OpenAPI 源engine.addApiServer(newApiSource().then(s-{s.setDocUrl(http://internal-api-doc/order-service/v3/api-docs);s.setApiBaseUrl(http://order-service.internal:8080);}));就这么简单。ApiSource通过setDocUrl指定 OpenAPI 文档地址setApiBaseUrl指定实际调用的 Base URL。Harness 会自动做以下几件事拉取 OpenAPI 文档支持 JSON 和 YAML兼容 OpenAPI 3.0 / 3.1 / Swagger 2.0解析每个路径、方法、参数、请求体和响应结构自动生成对应的 Tool 描述和参数签名注册到 Agent 的可用工具列表整个过程完全不需要你写一行工具代码。二十个接口如此两百个接口也是如此。完整的引擎初始化publicstaticHarnessEnginecreateEngine(){AgentSessionProvidersessionProvidernewAgentSessionProvider(){privatefinalMapString,AgentSessionsessionMapnewConcurrentHashMap();OverridepublicAgentSessiongetSession(StringinstanceId){returnsessionMap.computeIfAbsent(instanceId,k-InMemoryAgentSession.of(k));}};HarnessEngineengineHarnessEngine.of(work,./harness-home).systemPrompt(你是一个订单系统 AI 助手。你可以查询订单、查询库存、跟踪物流。始终使用 OpenAPI 工具获取实时数据。如果参数不完整询问用户补充。).sessionProvider(sessionProvider).modelAdd(newChatConfig().then(slf-{slf.setApiUrl(https://api.openai.com/v1);slf.setApiKey(sk-xxxxxx);slf.setModel(gpt-4o-mini);})).sandboxEnabled(true).memoryEnabled(true).maxTurns(10).compressionThreshold(30,30_000).addApiServer(newApiSource().then(s-{s.setDocUrl(http://internal-api-doc/order-service/v3/api-docs);s.setApiBaseUrl(http://order-service.internal:8080);}));returnengine;}memoryEnabled(true)Agent 在多轮对话中记住上下文maxTurns(10)限制对话最大轮次compressionThreshold(30, 30_000)超过 30 条消息或 30000 token 时自动压缩实战——用自然语言查询订单和库存引擎搭好了来看实际对话效果。场景一查订单publicvoiddemoQueryOrder(){HarnessEngineenginecreateEngine();Stringquestion帮我查一下订单 20250320001 的物流状态看看今天能不能到。;varresultengine.prompt(question).call();System.out.println(result.getContent());}引擎收到这个 prompt 后内部工作流程如下大模型判断意图属于「查询订单物流」从 OpenAPI 生成的 Tool 列表中选中对应的GET /api/orders/{orderId}/logisticsLLM 自动提取参数orderId 20250320001Harness 组装 HTTP 请求拿到响应 JSON 后LLM 把结构化数据转成自然语言回答输出类似订单 20250320001 的物流状态如下 - 承运方顺丰速运 - 运单号SF1234567890 - 当前状态运输中 - 最新节点2025-03-20 14:30已到达【上海市浦东新区分拨中心】 - 预计送达今天3月21日18:00 前 目前看今天能到建议您保持电话畅通。场景二查库存 组合意图StringquestionSKU 88002 还有库存吗如果缺货的话什么时候能补上;varresultengine.prompt(question).call();Harness 会自动判断需要调用GET /api/inventory/{sku}这个接口。如果 OpenAPI 中还有一个补货计划接口初次查询发现库存为 0LLM 会主动发起第二次工具调用查询补货计划然后整合给出回答。SKU 88002商品极简蓝牙耳机当前库存为 0。 根据补货计划下一批预计在 3 月 25 日到货数量 500 件。建议您开启到货提醒。LLM 不是一次把所有工具都调一遍而是根据上下文的实际需要动态决定调用顺序。场景三多轮会话publicvoiddemoMultiTurn(){HarnessEngineenginecreateEngine();AgentSessionsessionengine.getSession(order-002);varr1engine.prompt(帮我查一下订单 20250320001 的物流).session(session).call();System.out.println(r1.getContent());// Agent 记得上文讨论的是同一个订单varr2engine.prompt(这个订单什么时候能到).session(session).call();System.out.println(r2.getContent());}关键点在于session对象——Harness 会把历史消息存到 session 中LLM 看到上文就知道「这个订单」指的是20250320001。进阶——注册自定义业务工具补充 OpenAPIaddApiServer解决了 80% 的通用问题但总有一些逻辑无法用纯粹的 REST API 表达。比如一个「极速发货」操作内部涉及检查支付状态 → 检查库存 → 调拨 → 更新状态 → 发通知不适合暴露为单个 REST 端点。这时可以用extensionAdd注册自定义 Toolimportorg.noear.solon.ai.harness.tool.*;publicclassExpeditedShippingToolextendsAbstractTool{publicExpeditedShippingTool(){super(expedited_shipping,对已支付的订单执行极速发货。需要订单 ID 和发货仓库。);}OverridepublicToolResultexecute(MapString,Objectargs){StringorderId(String)args.get(orderId);Stringwarehouse(String)args.get(warehouse);// 业务逻辑...returnToolResult.success(订单 orderId 已执行极速发货。);}}// 注册到引擎engine.extensionAdd((agentName,agentBuilder)-{agentBuilder.defaultToolAdd(newExpeditedShippingTool());});注册后Agent 的工具列表就变成了「OpenAPI 自动生成的接口」「自定义 Tool」LLM 会根据意图自动选择用哪个。最佳实践经过多个项目的落地我总结了几条实用的经验1. 文档质量决定 Agent 表现addApiServer的输入是 OpenAPI 文档输出就是 Agent 的工具集。每个接口的summary和description要写清楚参数要有明确的description标明格式、范围、是否必填。/api/orders/{orderId}:get:summary:查询订单详情description:根据订单 ID 获取订单完整信息包括商品、金额、状态等。parameters:-name:orderIdin:pathrequired:truedescription:订单编号格式为 YYYYMMDD 6 位流水号schema:{type:string}2. 设置合理的系统提示词systemPrompt建议包含角色定义、能力边界、输出规范、安全约束。3. 生产环境务必开启沙箱.sandboxEnabled(true)沙箱会限制 Agent 的工具调用范围防止 LLM 幻觉导致意外调用写接口。4. 合理使用会话管理面向最终用户时建议用AgentSessionProvider实现持久化存储。同时设置compressionThreshold避免长对话撑爆上下文。5. 自定义 Tool 粒度适中一个 Tool 不要做太多事情否则 LLM 难以理解。反过来也尽量不要一个接口一个 Tool——既然有addApiServer了没必要重复造轮子。自定义 Tool 只封装那些需要业务逻辑组合或特殊权限控制的操作。总结这篇文章从实际问题出发完整走了一遍 Solon AI Harness 接入 OpenAPI 的流程。回顾核心要点addApiServer是零代码方案传入 OpenAPI 文档地址自动生成 ToolAgent 直接用自然语言调用引擎配置灵活通过systemPrompt、modelAdd、sessionProvider等 API 精确控制 Agent 行为多轮会话和记忆管理让 Agent 具备上下文感知能力extensionAdd提供扩展点当 OpenAPI 覆盖不了业务场景时可以注册自定义 Tool最佳实践集中在文档质量、提示词设计、安全沙箱三个方面在我带团队落地 Harness 的几个项目中addApiServer是让大家最「爽」的功能。以前接入一个十几接口的系统配置 Tool 要半天现在改两行配置、传一个文档 URL 就搞定了。更重要的是API 变化时不需要改 Agent 代码——更新 OpenAPI 文档就行了。如果你手头正有一个带 Swagger/OpenAPI 文档的 Java 系统强烈建议试试用 Harness 架一层 AI 能力。你会惊讶地发现让大模型「听懂」你的 REST API原来可以这么简单。参考文档Harness 概述Harness 配置参考Harness 进一步扩展定制