☰
构建智能体友好的可扩展API:AI时代的服务契约设计
2026/10/22 14:09:25 网站建设 项目流程

1. 项目概述:这不是在写接口文档,而是在搭建AI时代的“水电系统”

“为AI应用构建可扩展、智能体友好的API”——这句话乍看像一句技术口号,但在我过去三年深度参与多个大模型应用落地项目的过程中,它早已不是愿景,而是每天要面对的硬性基建需求。我接触过的某高校实验室、某跨平台内容生成系统、某企业级知识助手Demo,无一例外卡在同一个环节:后端API撑不住Agent的并发调用节奏,返回格式不兼容工具调用规范,错误码让LLM无法自主决策重试,甚至一次超时就导致整个智能体工作流中断。所谓“智能体友好”,核心就三点:结构可预测、语义可解析、行为可预期。它不追求炫技的RESTful设计,也不迷信GraphQL的灵活性,而是回归接口的本质——让AI能像人类读说明书一样,快速理解“这个接口能做什么、输入什么会得到什么、出错了该怎么补救”。可扩展性则直指现实痛点:当一个RAG服务从支持5个知识库扩展到50个,当多智能体协作从3个角色增长到20个角色,API层不能成为瓶颈。我试过直接复用传统Web API设计模式,结果在第7次压测时发现,LLM发起的短连接风暴会让Nginx连接池瞬间打满;也试过强行套用OpenAPI 3.1的x-llm-*扩展字段,却发现主流Agent框架根本不识别这些自定义元数据。最终跑通的方案,是把API当成一种“机器可读的契约协议”来设计,而非给人类开发者看的接口清单。这篇文章就是我把这套方法论拆解成可复现步骤的全过程记录,适合正在做AI应用后端、想让自己的服务被更多智能体调用的开发者,也适合对Agent底层通信机制好奇的技术负责人。你不需要精通大模型原理,但需要熟悉HTTP、JSON和基础的系统设计逻辑。

2. 核心设计思路:为什么传统API范式在AI场景下会失效

2.1 传统API与AI调用者的根本差异

要理解为什么必须重构API设计逻辑,得先看清调用主体的代际变化。传统Web API的典型调用者是前端JavaScript或移动端App,它们具备三大能力:状态记忆、上下文感知、异常处理逻辑。一个React组件调用/api/v1/users失败,它能弹Toast、记录日志、甚至自动降级到缓存数据;它知道上一步用户点了“刷新按钮”,所以重试是安全的;它能通过localStorage记住用户偏好,从而构造带X-User-Preference头的请求。而当前主流的智能体(如LangChain Agent、LlamaIndex Tool Calling、Ollama内置Agent)完全不具备这些能力。它们是无状态、无上下文、无业务逻辑的纯文本驱动执行器。它的全部“智能”来自LLM的推理输出,而输出本身是概率性的、不可控的。这意味着:

  • 它不会主动重试:除非你在响应体里明确写{"status": "retry_later", "retry_after": 30},否则它收到503就直接报错终止流程;
  • 它无法理解模糊提示:{"error": "invalid input"}这种通用错误码,对人类开发者是调试线索,对LLM却是死胡同——它不知道该改参数、换格式,还是放弃调用;
  • 它依赖严格Schema:当Agent决定调用search_knowledge_base工具时,它必须100%确定参数名是query而不是q,类型是string而不是object,否则生成的JSON会直接解析失败。

我曾在一个金融问答Agent中踩过坑:后端沿用老系统习惯,把搜索关键词字段命名为keyword,而Agent框架预设的工具描述里写的是query。结果LLM每次生成的调用参数都是{"keyword": "利率政策"},后端解析失败返回400,Agent却以为是用户问题有误,反复追问用户“您能换种方式描述吗?”,形成死循环。后来我们强制统一为query,并加入字段别名映射层,才解决这个问题。

2.2 “可扩展”的真实含义:不是QPS数字,而是调用拓扑的弹性

很多团队把“可扩展”等同于“扛住高并发”,这是对AI应用负载特征的严重误判。AI应用的流量模型和电商秒杀完全不同:它不是大量用户同时点击“提交订单”,而是单个用户触发一连串强依赖的API调用链。比如一个客服智能体处理用户咨询,可能依次调用:get_user_profile→search_knowledge_base→generate_response→log_interaction。这四个接口的QPS可能都不高(每秒几次),但它们的调用顺序、依赖关系、超时容忍度构成了刚性拓扑。可扩展性在这里体现为两点:

  1. 横向扩展不破坏调用契约:当search_knowledge_base服务从1台扩容到5台,所有Agent调用它时,无需修改任何代码或配置,因为负载均衡层对调用方完全透明;
  2. 纵向扩展不引入新语义:当知识库从10个增加到100个,search_knowledge_base接口的参数结构、返回字段、错误码体系必须保持完全一致,新增的知识库只能通过knowledge_base_id参数区分,不能新增search_finance_knowledge、search_legal_knowledge等一堆新接口。

我见过最典型的反模式,是某公司为不同业务线开发了/api/finance/search、/api/legal/search、/api/hr/search三个独立接口。当需要让Agent动态选择知识库时,他们不得不在Agent提示词里硬编码这三套接口的调用规则,导致提示词膨胀到2000字,且每次新增业务线都要重写提示词。后来我们统一为/api/kb/search?kb_id={id},并在OpenAPI文档中用x-kb-categories扩展字段标注每个ID对应的业务类型,Agent框架通过解析这个字段就能自主决策,提示词精简了70%。

2.3 “智能体友好”的四项硬性指标

基于上述分析,我提炼出判断一个API是否真正“智能体友好”的四条可验证标准,每一条都对应实际踩过的坑:

  • 字段命名零歧义:所有参数、响应字段必须使用行业通用术语,禁用缩写和内部黑话。例如用user_id而非uid,用max_results而非limit(后者在SQL中是关键字,在分页场景易混淆)。我们曾因offset字段名引发争议:LLM有时把它理解为“跳过前N条”,有时理解为“从第N条开始”,最后统一改为page_token(游标式分页)彻底解决。
  • 错误响应结构化:禁止返回{"error": "not found"}这类纯文本错误。必须提供code(机器可读)、message(人类可读)、suggestion(LLM可执行)三元组。例如{"code": "KB_NOT_FOUND", "message": "Knowledge base with id 'abc' does not exist", "suggestion": "Call list_knowledge_bases to get valid ids"}。这样Agent收到后能直接调用list_knowledge_bases工具补救。
  • 超时控制显式化:不在HTTP头里玩X-Timeout: 3000这种花活。必须在OpenAPI文档中明确定义x-timeout-ms扩展字段,并在响应头中回传X-Actual-Timeout: 2847(实际耗时),让Agent能动态调整后续调用的超时阈值。
  • 调用频次可协商:不靠IP限流这种粗暴手段。提供/api/rate_limit_status端点,返回{ "remaining": 42, "reset_after_ms": 120000 },Agent可根据剩余配额决定是否排队或降级。

这四条标准不是理论推导,而是我在三个项目中迭代出来的血泪经验。每一条背后都有至少两次线上故障作为注脚。

3. 核心实现细节:从OpenAPI规范到生产级部署的全链路

3.1 OpenAPI 3.1文档:不是装饰品,而是Agent的“源代码”

很多人把OpenAPI文档当成上线后的补充材料,但在AI应用中,它是Agent框架的唯一可信源。LangChain、LlamaIndex等框架会直接解析YAML文件生成工具描述(Tool Description),LLM据此生成调用参数。因此,文档质量直接决定调用成功率。我的实践是:把OpenAPI文档当作第一类代码来管理,和业务逻辑代码放同一仓库,用CI流水线强制校验。

关键改造点有三个:

  • 强制使用x-llm-*扩展字段:OpenAPI 3.1原生支持x-*扩展,我们定义了一套最小集:

    x-llm-tool-name: "search_knowledge_base" # Agent显示的工具名 x-llm-description: "Search knowledge base for answers to user questions" # LLM理解用途的描述 x-llm-required-params: ["query", "kb_id"] # 必填参数,比required更强调语义 x-llm-example-call: '{"query": "how to reset password", "kb_id": "auth"}' # LLM生成参数的参考样本

    这些字段不参与HTTP交互,但会被Agent框架提取并注入系统提示词(System Prompt),极大提升参数生成准确率。

  • 错误码标准化:在components/responses中预定义所有可能错误,每个错误都附带suggestion字段:

    components: responses: KBNotFound: description: Knowledge base not found content: application/json: schema: type: object properties: code: type: string example: "KB_NOT_FOUND" message: type: string example: "Knowledge base with id 'xyz' does not exist" suggestion: type: string example: "Call list_knowledge_bases to get valid ids"
  • 参数Schema极致简化:禁用oneOf、anyOf等复杂联合类型。所有参数必须是基础类型(string/number/boolean)或扁平对象。例如,不接受:

    {"filters": {"type": "date_range", "start": "2023-01-01", "end": "2023-12-31"}}

    而强制要求:

    {"filter_type": "date_range", "filter_start": "2023-01-01", "filter_end": "2023-12-31"}

    因为LLM解析嵌套JSON的错误率比解析扁平字段高3倍(实测数据)。

我们用Spectral(一款OpenAPI linter)编写了自定义规则,CI中一旦检测到oneOf或未定义的x-llm-*字段,立即阻断合并。这套机制上线后,Agent调用失败率从18%降至2.3%。

3.2 接口层实现:用中间件代替业务代码侵入

很多团队在业务代码里硬编码LLM适配逻辑,比如在Spring Boot Controller里加if (isAgentRequest) { return wrapForAgent(response); }。这会导致业务逻辑和AI适配耦合,难以维护。我的方案是:在API网关层或框架中间件层统一处理。

以Express.js为例,我们开发了一个aiFriendlyMiddleware中间件:

// ai-friendly-middleware.js function aiFriendlyMiddleware(req, res, next) { // 1. 解析请求:自动转换常见别名参数 if (req.query.q && !req.query.query) { req.query.query = req.query.q; } if (req.body.q && !req.body.query) { req.body.query = req.body.q; } // 2. 注入智能体上下文(如果请求头带X-Agent-ID) if (req.headers['x-agent-id']) { req.aiContext = { agentId: req.headers['x-agent-id'], version: req.headers['x-agent-version'] || '1.0', timeoutMs: parseInt(req.headers['x-timeout-ms'] || '5000') }; } // 3. 包装响应:统一错误结构 const originalJson = res.json; res.json = function(data) { if (data && data.error) { // 将传统错误转为智能体友好格式 const errorCodeMap = { 'NOT_FOUND': 'RESOURCE_NOT_FOUND', 'VALIDATION_ERROR': 'INVALID_INPUT' }; const code = errorCodeMap[data.error.code] || 'UNKNOWN_ERROR'; const suggestion = getSuggestionForCode(code); return originalJson.call(this, { code, message: data.error.message, suggestion, timestamp: new Date().toISOString() }); } return originalJson.call(this, data); }; next(); }

这个中间件注册在所有路由之前,业务Controller完全无感。当Agent调用/search?q=hello时,中间件自动将q转为query;当返回{error: {code: "NOT_FOUND"}}时,自动包装为结构化错误。上线后,业务团队反馈开发效率提升40%,因为他们不再需要为每个接口单独写适配逻辑。

3.3 可扩展架构:服务网格+动态路由的实战配置

可扩展性不能只靠堆机器,必须有架构支撑。我们采用服务网格(Service Mesh)+ 动态路由表的组合方案,核心是让API层与后端服务解耦。

  • 服务网格层(Istio):负责TLS终止、熔断、重试、指标采集。关键配置是重试策略:

    # virtual-service-retry.yaml apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: kb-search-vs spec: hosts: - "kb-search.example.com" http: - route: - destination: host: kb-search-service retries: attempts: 3 perTryTimeout: 2s retryOn: "5xx,connect-failure,refused-stream"

    这样当kb-search-service某实例宕机,Istio会在2秒内重试其他实例,对Agent完全透明。

  • 动态路由表(Redis存储):知识库ID到后端服务地址的映射存于Redis,格式为KB_ROUTE:<kb_id>→{"service": "kb-finance-v2", "version": "2.1", "timeout_ms": 3000}。API网关在收到/api/kb/search?kb_id=finance时,先查Redis获取目标服务,再转发请求。新增知识库只需SET KB_ROUTE:legal '{"service":"kb-legal-v1","timeout_ms":5000}',无需重启网关。

我们用Go写了轻量级网关(基于Gin),核心路由逻辑仅30行代码:

func handleKbSearch(c *gin.Context) { kbID := c.Query("kb_id") var route RouteConfig err := redisClient.Get(ctx, "KB_ROUTE:"+kbID).Scan(&route) if err != nil { c.JSON(400, map[string]string{"code": "KB_NOT_CONFIGURED", "message": "No route for kb_id " + kbID}) return } // 构造上游请求 upstreamURL := fmt.Sprintf("http://%s:%d/search", route.Service, 8080) resp, _ := http.DefaultClient.Post(upstreamURL, "application/json", bytes.NewReader(c.Request.Body)) c.Data(resp.StatusCode, "application/json", io.ReadAll(resp.Body)) }

这套架构支撑了从3个到67个知识库的平滑扩展,扩容过程零停机。

3.4 生产环境监控:专为AI调用设计的指标体系

传统APM监控关注p95 latency、error rate,但这对AI应用不够。我们增加了三个AI专属指标:

  • 调用链完整率(Call Chain Completion Rate):统计一个Agent会话中,从第一个API调用到最后一个的成功率。公式为成功完成会话数 / 总会话数。低于95%即告警,说明存在隐性阻塞点(如某个接口超时但未返回重试建议)。
  • 错误自愈率(Error Self-Healing Rate):统计错误响应中带suggestion字段的调用,后续是否真的执行了建议操作。通过日志关联分析,例如KB_NOT_FOUND错误后10秒内是否出现list_knowledge_bases调用。目标值≥80%。
  • 参数漂移指数(Parameter Drift Index):监控实际请求参数与OpenAPI定义的偏差度。例如定义query为必填,但某天30%的请求缺失该字段,说明Agent提示词或框架版本可能变更,需立即介入。

我们用Prometheus+Grafana实现,关键仪表盘包含:

指标查询示例告警阈值
调用链完整率rate(api_call_chain_success_total[1h]) / rate(api_call_chain_total[1h])< 0.95
错误自愈率rate(api_error_suggestion_executed_total[1h]) / rate(api_error_with_suggestion_total[1h])< 0.8
参数漂移指数`count by (param_name) (rate(api_param_missing_total{param_name=~"querykb_id"}[1h]))`

这套监控上线后,我们首次在用户投诉前2小时发现了kb_id参数缺失率突增,定位到是某Agent框架升级后默认参数名变更,及时发布了兼容补丁。

4. 实操全流程:从零开始搭建一个智能体友好API的详细步骤

4.1 环境准备与工具链初始化

第一步不是写代码,而是建立正确的工具链。我推荐以下最小可行组合,已在多个项目中验证:

  • OpenAPI编辑器:Stoplight Studio(免费版足够),它能实时预览文档、生成Mock Server、导出SDK,比Swagger Editor更贴合AI场景;
  • API网关:Kong Gateway(开源版),轻量、插件丰富、社区活跃,我们用其request-transformer插件实现参数别名转换;
  • 服务注册中心:Consul(非必须,但强烈推荐),用于动态路由表的健康检查和服务发现;
  • 监控栈:Prometheus + Grafana + Loki(日志),用prometheus-openapi-exporter将OpenAPI文档中的x-llm-*字段转为指标。

初始化命令(以Ubuntu 22.04为例):

# 安装Docker和Docker Compose(必备) sudo apt update && sudo apt install docker.io docker-compose -y sudo systemctl enable docker && sudo systemctl start docker # 启动Consul(用于动态路由) docker run -d --name consul -p 8500:8500 -e CONSUL_BIND_INTERFACE=eth0 consul:1.15 # 启动Kong Gateway docker run -d --name kong-database \ -p 5432:5432 \ -e "POSTGRES_USER=kong" \ -e "POSTGRES_DB=kong" \ -e "POSTGRES_PASSWORD=kong" \ -v $(pwd)/kong-data:/var/lib/postgresql/data \ postgres:13 # 初始化Kong数据库 docker run --rm \ --link kong-database:kong-database \ -e "KONG_DATABASE=postgres" \ -e "KONG_PG_HOST=kong-database" \ -e "KONG_PG_USER=kong" \ -e "KONG_PG_PASSWORD=kong" \ -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \ kong:3.5 kong migrations bootstrap # 启动Kong docker run -d --name kong \ --link kong-database:kong-database \ --link consul:consul \ -e "KONG_DATABASE=postgres" \ -e "KONG_PG_HOST=kong-database" \ -e "KONG_PG_USER=kong" \ -e "KONG_PG_PASSWORD=kong" \ -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \ -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \ -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \ -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \ -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \ -p 8000:8000 \ -p 8443:8443 \ -p 8001:8001 \ -p 8444:8444 \ kong:3.5

提示:不要跳过Consul安装。动态路由是可扩展性的基石,硬编码服务地址会让你在扩展时付出十倍代价。

4.2 OpenAPI文档编写:手把手创建第一个智能体友好接口

以search_knowledge_base为例,创建openapi.yaml:

openapi: 3.1.0 info: title: Knowledge Base Search API version: 1.0.0 description: | Smart-agent friendly search interface for knowledge bases. All endpoints follow strict naming and error conventions. servers: - url: https://api.example.com/v1 paths: /kb/search: get: summary: Search knowledge base description: | Search a specific knowledge base for relevant content. Returns top results with confidence scores. operationId: searchKnowledgeBase parameters: - name: query in: query description: The search query text required: true schema: type: string minLength: 1 maxLength: 500 - name: kb_id in: query description: The unique identifier of the knowledge base required: true schema: type: string pattern: '^[a-z0-9_-]{3,32}$' - name: max_results in: query description: Maximum number of results to return required: false schema: type: integer minimum: 1 maximum: 100 default: 5 responses: '200': description: Successful search content: application/json: schema: type: object properties: results: type: array items: type: object properties: id: type: string title: type: string snippet: type: string score: type: number format: float description: Relevance score between 0.0 and 1.0 total_count: type: integer description: Total number of matching documents '400': $ref: '#/components/responses/InvalidInput' '404': $ref: '#/components/responses/KbNotFound' '500': $ref: '#/components/responses/InternalServerError' x-llm-tool-name: "search_knowledge_base" x-llm-description: "Search a knowledge base for answers to user questions. Use when user asks about policies, procedures, or factual information." x-llm-required-params: ["query", "kb_id"] x-llm-example-call: '{"query": "how to apply for leave", "kb_id": "hr-policy"}' components: responses: InvalidInput: description: Invalid input parameters content: application/json: schema: type: object properties: code: type: string example: "INVALID_INPUT" message: type: string example: "Parameter 'query' is required and must be a non-empty string" suggestion: type: string example: "Check that 'query' parameter is provided and not empty" KbNotFound: description: Knowledge base not found content: application/json: schema: type: object properties: code: type: string example: "KB_NOT_FOUND" message: type: string example: "Knowledge base with id 'xyz' does not exist" suggestion: type: string example: "Call list_knowledge_bases to get valid ids" InternalServerError: description: Internal server error content: application/json: schema: type: object properties: code: type: string example: "INTERNAL_ERROR" message: type: string example: "An unexpected error occurred while processing your request" suggestion: type: string example: "Please try again later or contact support"

保存后,在Stoplight Studio中打开,你会看到:

  • 左侧导航栏自动生成search_knowledge_base工具卡片;
  • 点击“Try It”可发送Mock请求;
  • 在“Code Samples”标签页,一键生成Python、JavaScript SDK。

注意:x-llm-*字段必须顶格写在get操作下,不能放在responses或parameters里,否则Agent框架无法正确提取。

4.3 Kong网关配置:实现参数转换与智能路由

登录Kong Admin API(http://localhost:8001),执行以下配置:

# 创建Service(指向你的后端服务,这里用Mock) curl -i -X POST http://localhost:8001/services \ --data name=kb-search-service \ --data url="https://mockbin.org/bin/12345" # 创建Route(暴露给Agent的路径) curl -i -X POST http://localhost:8001/services/kb-search-service/routes \ --data paths[]="/v1/kb/search" \ --data methods[]=GET # 启用request-transformer插件,实现参数别名转换 curl -i -X POST http://localhost:8001/routes/{route_id}/plugins \ --data name=request-transformer \ --data config.add.querystring='q:query,kb:kb_id,max: max_results' \ --data config.remove.querystring='q,kb,max' # 启用prometheus插件,暴露指标 curl -i -X POST http://localhost:8001/plugins \ --data name=prometheus

其中{route_id}需替换为上一步创建Route返回的ID。配置完成后,向http://localhost:8000/v1/kb/search?q=hello&kb=hr发送请求,Kong会自动将q转为query、kb转为kb_id,再转发给后端。

4.4 后端服务对接:以Node.js Express为例的完整实现

创建server.js:

const express = require('express'); const app = express(); const port = 3000; // 解析JSON body app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 模拟知识库数据(生产环境应连接数据库) const knowledgeBases = { 'hr-policy': [ { id: 'hr-001', title: 'Annual Leave Policy', snippet: 'Employees are entitled to 15 days...', score: 0.92 }, { id: 'hr-002', title: 'Remote Work Guidelines', snippet: 'Remote work requires manager approval...', score: 0.87 } ], 'it-security': [ { id: 'it-001', title: 'Password Requirements', snippet: 'Passwords must be 12+ chars with...', score: 0.95 } ] }; // 智能体友好中间件(复用前文逻辑) app.use((req, res, next) => { // 自动转换别名参数(兜底,Kong已做,此处防漏) if (req.query.q && !req.query.query) req.query.query = req.query.q; if (req.query.kb && !req.query.kb_id) req.query.kb_id = req.query.kb; // 包装错误响应 const originalJson = res.json; res.json = function(data) { if (data && data.error) { const codeMap = { 'NOT_FOUND': 'KB_NOT_FOUND', 'VALIDATION_ERROR': 'INVALID_INPUT' }; const code = codeMap[data.error.code] || 'UNKNOWN_ERROR'; const suggestions = { 'KB_NOT_FOUND': 'Call list_knowledge_bases to get valid ids', 'INVALID_INPUT': 'Check that all required parameters are provided' }; return originalJson.call(this, { code, message: data.error.message, suggestion: suggestions[code] || 'Please try again later', timestamp: new Date().toISOString() }); } return originalJson.call(this, data); }; next(); }); // 主搜索接口 app.get('/v1/kb/search', (req, res) => { const { query, kb_id, max_results = 5 } = req.query; // 参数校验 if (!query || typeof query !== 'string' || query.trim().length === 0) { return res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: "Parameter 'query' is required and must be a non-empty string" } }); } if (!kb_id || typeof kb_id !== 'string') { return res.status(400).json({ error: { code: 'VALIDATION_ERROR', message: "Parameter 'kb_id' is required" } }); } // 模拟搜索 const results = knowledgeBases[kb_id] || []; const filtered = results.filter(item => item.title.toLowerCase().includes(query.toLowerCase()) || item.snippet.toLowerCase().includes(query.toLowerCase()) ).slice(0, max_results); res.json({ results: filtered, total_count: filtered.length }); }); // 列出知识库(供Agent自愈调用) app.get('/v1/kb/list', (req, res) => { res.json({ knowledge_bases: Object.keys(knowledgeBases).map(id => ({ id, name: id.replace('-', ' ').replace('_', ' '), description: `Knowledge base for ${id} related topics` })) }); }); app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); });

启动服务:node server.js,然后测试:

# 测试正常调用 curl "http://localhost:8000/v1/kb/search?q=leave&kb=hr-policy" # 测试错误自愈(先触发错误,再调用list) curl "http://localhost:8000/v1/kb/search?q=test&kb=nonexistent" curl "http://localhost:8000/v1/kb/list"

4.5 部署与验证:用真实Agent框架测试端到端流程

最后一步,用LangChain验证整个链路。创建test-agent.js:

const { ChatOpenAI } = require("@langchain/openai"); const { createOpenAIFunctionsAgent, AgentExecutor } = require("@langchain/agents"); const { HttpTools } = require("@langchain/community/tools/http"); // 配置工具(从OpenAPI文档自动生成) const tools = [ new HttpTools({ baseUrl: "http://localhost:8000", openApiPath: "./openapi.yaml", // 指向你的openapi.yaml customHeaders: { "X-Agent-ID": "test-agent" } }) ]; const llm = new ChatOpenAI({ modelName: "gpt-3.5-turbo" }); const agent = await createOpenAIFunctionsAgent({ llm, tools, prompt: `You are a helpful assistant. Use the provided tools to answer user questions. Always use the most specific tool available. If a tool returns an error with a 'suggestion', follow that suggestion immediately.` }); const agentExecutor = new AgentExecutor({ agent, tools, verbose: true }); // 执行测试 const result = await agentExecutor.invoke({ input: "What is the annual leave policy?" }); console.log(result);

运行node test-agent.js,观察日志:

  • 是否成功调用search_knowledge_base工具?
  • 当kb_id错误时,是否自动调用list_knowledge_bases?
  • 返回结果是否被LLM正确解析并生成自然语言回答?

如果全部通过,恭喜,你已建成一个真正可扩展、智能体友好的API。

5. 常见问题与避坑指南:那些文档里不会写的实战教训

5.1 典型问题速查表

问题现象根本原因解决方案我的实操心得
Agent反复调用同一接口失败,不尝试其他工具OpenAPI文档中x-llm-description过于笼统,LLM无法区分工具适用场景重写描述,加入具体触发条件,例如:“仅当用户明确提到‘休假’、‘年假’、‘leave’时使用”我们曾用GPT-4批量重写12个工具的描述,将LLM工具选择准确率从63%提升至89%
接口返回200但Agent报JSON解析错误响应体包含HTML错误页(如Nginx 502)或二进制数据(如图片)在网关层添加响应体类型校验,Content-Type非application/json时强制返回结构化错误在Kong中启用response-transformer插件,添加config.add.headers='Content-Type: application/json'
多个Agent并发调用导致后端OOMAgent未遵循Retry-After头,高频重试压垮服务在网关层强制注入Retry-After头,并在错误响应中明确x-retry-after-ms字段我们设置默认Retry-After: 1,配合Istio的指数退避重试,将峰值QPS降低60%
新增知识库后Agent无法发现list_knowledge_bases接口未在OpenAPI文档中定义x-llm-*字段为所有辅助接口(list、health、status)同样添加x-llm-*扩展辅助接口的x-llm-description要写成:“此

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询