Thiết kế Lỗi API cho AI: Các Lỗi AI Có Thể Tự Phục Hồi

Thông báo "Đầu vào không hợp lệ" không cho tác nhân biết được điều gì, nên nó thử lại mãi mãi. Hãy tìm hiểu định dạng lỗi mà tác nhân có thể xử lý: chi tiết vấn đề theo RFC 9457, một cờ chỉ báo có thể thử lại, các nguyên nhân ở cấp độ trường, và các đường dẫn lỗi đã được kiểm thử.

Ashley Innocent

Ashley Innocent

26 tháng 8 2026

Thiết kế Lỗi API cho AI: Các Lỗi AI Có Thể Tự Phục Hồi

Apidog cho doanh nghiệp

Triển khai tại chỗ

SSO & RBAC

Tuân thủ SOC 2

Khám phá Apidog Enterprise

API của bạn trả về 400 Bad Request với nội dung {"error": "invalid input"}. Một nhà phát triển con người mở tài liệu, kiểm tra payload, phát hiện trường bị thiếu và sửa lỗi trong một phút. Một tác nhân (agent) đọc hai từ đó, không có gì để hành động, và làm điều duy nhất nó có thể: gửi lại yêu cầu tương tự. Rồi lại gửi. Sau đó, nó từ bỏ và thông báo cho người dùng rằng API bị hỏng.

Phản hồi lỗi là phần mà các tác nhân API phụ thuộc nhiều nhất và là phần mà các nhóm thiết kế sau cùng. Một lỗi tốt cho người gọi biết điều gì đã sai, liệu việc thử lại có giúp ích không và cần thay đổi điều gì. Một tác nhân có thể hành động dựa trên cả ba. Một lỗi mơ hồ biến một vấn đề có thể khắc phục được thành một tác vụ thất bại.

Hướng dẫn này được viết cho phía API của mối quan hệ. Bài viết của chúng tôi về phục hồi lỗi tác nhân bao gồm những gì client nên làm với các lần thử lại, backoff và cầu dao. Bài viết này bao gồm những gì API của bạn phải trả về để logic client đó hoạt động được.

Apidog rất quan trọng ở đây vì phản hồi lỗi là phần ít được kiểm thử nhất của hầu hết các API. Bạn có thể định nghĩa chúng trong spec, giả lập chúng và kiểm tra chúng ở cùng một nơi bạn kiểm thử luồng thành công (happy path).

Ba câu hỏi mà một lỗi phải trả lời

Mọi phản hồi lỗi mà một tác nhân nhận được phải cho phép nó trả lời ba điều mà không cần phỏng đoán.

Đây là lỗi của tôi hay của bạn? Một lỗi 4xx có nghĩa là yêu cầu sai và việc lặp lại nó mà không thay đổi sẽ lại thất bại. Một lỗi 5xx có nghĩa là có điều gì đó trên máy chủ đã sai và yêu cầu tương tự có thể thành công sau này. Các tác nhân không thể phân biệt được điều này hoặc sẽ thử lại mãi mãi khi gặp lỗi xác thực hoặc từ bỏ khi gặp sự cố tạm thời.

Tôi có nên thử lại không, và khi nào? Một số lỗi 4xx có thể thử lại được và một số thì không. 429 có thể thử lại sau một thời gian chờ. 409 có thể thử lại sau khi đọc lại trạng thái. 422 không thể thử lại nếu không thay đổi payload. Hãy nói rõ điều đó.

Chính xác thì tôi phải thay đổi gì? Đây là trường mà hầu hết các API bỏ qua. "Xác thực thất bại" là vô dụng. "Trường customer.postal_code là bắt buộc khi countryUS" là một cách sửa lỗi mà tác nhân có thể áp dụng trong lần thử tiếp theo.

Đưa ba điều đó vào mọi lỗi và hầu hết các "cơn bão" thử lại của tác nhân sẽ biến mất.

Sử dụng định dạng lỗi có cấu trúc

Đừng tự mình phát minh một định dạng. RFC 9457, Problem Details for HTTP APIs, định nghĩa một định dạng và nó được hỗ trợ tốt:

{
  "type": "https://api.example.com/errors/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "The field 'customer.postal_code' is required when 'country' is 'US'.",
  "instance": "/v1/orders",
  "errors": [
    {
      "field": "customer.postal_code",
      "code": "required_conditional",
      "message": "Required when country is US. Provide a 5-digit or 9-digit US postal code.",
      "example": "94107"
    }
  ],
  "retryable": false,
  "next_action": "Add customer.postal_code to the request body and send again."
}

Bốn phần mang trọng lượng cho một tác nhân.

detail là một câu hoàn chỉnh nêu tên trường thực tế và quy tắc thực tế. Không phải một danh mục. Là điều cụ thể đã thất bại trong yêu cầu này.

Mảng errors có thể đọc được bằng máy, mỗi mục ứng với một vấn đề, với đường dẫn trường mà tác nhân có thể ánh xạ lại vào payload mà nó đã gửi. Trả về tất cả các lỗi cùng một lúc. Trả về từng lỗi một sẽ biến một lần sửa thành năm lần truy cập khứ hồi.

retryable là một giá trị boolean, không phải thứ cần suy ra từ mã trạng thái. Đây là phần mở rộng giúp các tác nhân nhiều nhất, và nó chỉ tốn một trường.

next_action là văn bản hướng dẫn đơn giản. Các mô hình tuân theo các hướng dẫn rõ ràng trong nội dung phản hồi đáng tin cậy hơn là chúng suy luận từ mã lỗi, và một câu ở đây thường biến một tác vụ thất bại thành một tác vụ đã hoàn thành.

Hướng dẫn thiết kế lỗi API của Google đưa ra những kết luận tương tự từ một hướng khác, đáng chú ý là chi tiết lỗi nên thuộc về một danh sách có cấu trúc chứ không phải là văn xuôi.

Nói khi nào nên quay lại

Đối với bất kỳ điều gì tạm thời, hãy nói khi nào. Một tác nhân biết phải chờ 30 giây sẽ chờ 30 giây. Một tác nhân không biết sẽ chọn một khoảng thời gian, và khoảng thời gian đó thường quá ngắn.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json

{
  "type": "https://api.example.com/errors/rate-limited",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "You have used 1000 of 1000 requests in the current minute window.",
  "retryable": true,
  "retry_after_seconds": 30,
  "next_action": "Wait 30 seconds before sending this request again. Do not retry sooner."
}

Tiêu đề Retry-After chấp nhận một độ trễ tính bằng giây hoặc một ngày HTTP; giây là cái dễ dàng hơn để client hành động. Gửi nó dưới dạng tiêu đề cho các client tiêu chuẩn và lặp lại nó trong nội dung cho mô hình. Việc trùng lặp là rẻ và cả hai người tiêu dùng đều nhận được thứ mà họ đọc tốt nhất. Các chi tiết cụ thể về giới hạn tốc độ được đề cập trong hướng dẫn vượt quá giới hạn tốc độ của chúng tôi và trong cách triển khai giới hạn tốc độ API nếu bạn đang ở phía máy chủ.

Mẫu tương tự áp dụng cho 503 trong quá trình bảo trì và 409 trên một tài nguyên bị khóa. Bất kỳ lỗi nào mà việc chờ đợi là phản hồi đúng đắn đều phải mang theo một con số.

Không bao giờ để lộ nội bộ, không bao giờ trả về trống rỗng

Hai chế độ lỗi nằm ở hai thái cực đối lập, và cả hai đều gây hại cho các tác nhân.

Đầu tiên là dấu vết ngăn xếp (stack trace). Việc trả về văn bản ngoại lệ nội bộ làm lộ các phiên bản framework, đường dẫn tệp và đôi khi là các đoạn truy vấn. Đây là vấn đề bảo mật trước khi là vấn đề của tác nhân, và những lo ngại trong bài viết của chúng tôi về kiểm thử API chống lại đầu vào không đáng tin cậy áp dụng trực tiếp. Nó cũng làm ngập cửa sổ ngữ cảnh với văn bản mà mô hình không thể hành động.

Thứ hai là lỗi trống rỗng: một 500 không có nội dung, hoặc {"error": true}. Tác nhân không học được gì, và các lựa chọn duy nhất của nó là thử lại hoặc bỏ cuộc.

Con đường trung gian là một lỗi công khai ổn định với một ID tương quan:

{
  "type": "https://api.example.com/errors/internal",
  "title": "Internal error",
  "status": 500,
  "detail": "The order could not be created due to an internal error. No order was created.",
  "retryable": true,
  "retry_after_seconds": 5,
  "request_id": "req_01J8ZK3M2Q",
  "next_action": "Retry once after 5 seconds. If it fails again, stop and report request_id req_01J8ZK3M2Q."
}

Câu "Không có đơn hàng nào được tạo" là phần có giá trị nhất. Các tác nhân đối mặt với một thao tác ghi mơ hồ phải quyết định liệu việc thử lại có gây rủi ro trùng lặp hay không, và hầu hết đều đưa ra quyết định sai lầm. Hãy cho họ biết bạn đang ở trạng thái nào. Khi bạn không thể đảm bảo điều đó, hãy làm cho thao tác trở nên bất biến (idempotent) và nói rõ điều đó, đây là mẫu trong bài viết của chúng tôi về khóa bất biến cho tác nhân AI.

request_id giúp bạn truy vết lại các nhật ký của mình khi một con người cuối cùng đọc bản ghi. Hãy kết hợp nó với các thực tiễn trong hướng dẫn khả năng quan sát API của chúng tôi để ID thực sự giải quyết được điều gì đó.

Lỗi thuộc về đặc tả

Nếu một định dạng lỗi không có trong tài liệu OpenAPI của bạn, thì nó không tồn tại đối với các client được tạo, các mock và các công cụ tác nhân. Hầu hết các đặc tả mô tả chi tiết một 200 và sau đó chỉ chung chung về mọi thứ khác.

responses:
  '201':
    description: Order created
    content:
      application/json:
        schema: { $ref: '#/components/schemas/Order' }
  '422':
    description: >
      Validation failed. Not retryable without changing the request body.
      The errors array names each invalid field.
    content:
      application/problem+json:
        schema: { $ref: '#/components/schemas/Problem' }
  '429':
    description: >
      Rate limited. Retryable. Wait for retry_after_seconds before sending again.
    content:
      application/problem+json:
        schema: { $ref: '#/components/schemas/Problem' }

Những mô tả đó không phải là vật trang trí. Khi bạn tạo công cụ tác nhân từ đặc tả, như trong hướng dẫn của chúng tôi về biến đặc tả OpenAPI thành công cụ tác nhân, văn bản đó trở thành thứ mà mô hình đọc về trường hợp lỗi. Một mô tả nói "có thể thử lại, chờ trước" sẽ tạo ra hành vi tốt hơn so với mô tả nói "Too Many Requests".

Kiểm thử các lỗi, không chỉ các trường hợp thành công

Các đường dẫn lỗi là nơi phạm vi kiểm thử bị thu hẹp, vì việc kích hoạt chúng đòi hỏi nỗ lực. Giả lập loại bỏ nỗ lực đó.

Định nghĩa từng phản hồi lỗi trong dự án API của bạn, sau đó giả lập chúng để tác nhân có thể gặp mọi trường hợp theo yêu cầu. Trong Apidog bạn có thể thêm các phản hồi lỗi vào định nghĩa điểm cuối và chuyển đổi giữa các mock, điều này cung cấp cho bạn một cách lặp lại để chạy tác nhân chống lại 422, 429500 mà không làm hỏng bất cứ điều gì thực tế. Bài viết của chúng tôi về chạy tác nhân với mock thay vì sản phẩm bao gồm thói quen rộng lớn hơn.

Năm trường hợp để xây dựng:

Lưu tập hợp này dưới dạng các kịch bản để chúng chạy trong CI. Việc xử lý lỗi lặng lẽ thoái hóa, thường là khi ai đó tái cấu trúc một bộ tuần tự hóa, và bộ kiểm thử happy-path sẽ không nhận thấy.

Giá trị của các lỗi tốt hơn

Giá trị xuất hiện ở ba nơi, và dễ dàng đo lường một khi bạn tìm kiếm.

Ít lần thử lại lãng phí hơn. Một tác nhân đối mặt với {"error": "invalid input"} thường thử lại payload giống hệt hai hoặc ba lần trước khi bỏ cuộc. Mỗi lần thử tốn một lượt của mô hình và toàn bộ cuộc trò chuyện làm ngữ cảnh. Một phản hồi nêu tên trường bị thiếu thường tạo ra một lần thử được sửa lỗi. Đó là sự khác biệt giữa bốn cuộc gọi và hai cuộc gọi cho một lỗi xác thực thông thường.

Ít leo thang hơn. Các tác nhân không thể phục hồi sẽ chuyển giao tác vụ cho con người. Mỗi lần chuyển giao không thể tránh khỏi là kết quả đắt đỏ mà tác nhân đáng lẽ phải ngăn chặn. Các lỗi nêu rõ cách khắc phục giúp duy trì quá trình chạy bên trong hệ thống tự động hóa.

Gỡ lỗi ngắn hơn. Khi cần một người, request_id cộng với một detail chính xác sẽ biến việc tìm kiếm trong nhật ký thành một lần tra cứu duy nhất. Đây là cùng một lập luận mà hướng dẫn khả năng quan sát API của chúng tôi đưa ra về mối tương quan, được áp dụng vào thời điểm một tác vụ bị lỗi.

Có một lợi ích thứ tư dễ bỏ qua: những cải tiến tương tự giúp các nhà phát triển con người. Chưa ai từng than phiền rằng một thông báo lỗi quá cụ thể về trường nào sai.

Thiết kế cho cả việc leo thang

Một số lỗi thực sự không thể phục hồi bởi tác nhân. Một phạm vi bị thiếu, một tài khoản đã đóng, một quy tắc cần quyết định của con người. Đối với những trường hợp đó, nhiệm vụ của lỗi là chuyển giao rõ ràng: nói điều gì đã xảy ra, nói điều gì con người cần làm, và mang theo ID tương quan giúp việc chuyển giao trở nên dễ dàng.

Phản hồi đó phải đến một nơi mà con người đọc được. Nếu tác nhân là một môi trường chạy mã hoạt động thông qua các tác vụ được giao, nền tảng xung quanh thường là nơi nó đến. Sharkly giữ kết quả và dấu vết thực thi của tác nhân trên Tác vụ và định tuyến các mục cần phản hồi hoặc xem xét vào Hộp thư đến, vì vậy một lần chạy bị chặn hiển thị dưới dạng công việc chứ không phải một dòng trong nhật ký. Văn bản lỗi của bạn là thứ làm cho việc chuyển giao đó hữu ích, vì một tin nhắn đọc "đầu vào không hợp lệ" không cung cấp cho người đánh giá nhiều hơn những gì nó đã cung cấp cho tác nhân.

Đừng bắt tác nhân phân tích văn xuôi

Một mẫu chống lại cuối cùng, phổ biến trong các API phát triển tự nhiên. Mã trạng thái đúng, nội dung là một câu, và mỗi lỗi khác nhau lại có cách diễn đạt khác nhau:

{ "message": "Sorry, that didn't work. Please check your details and try again." }

Một tác nhân chỉ có thể phản hồi điều này bằng cách đoán. Tệ hơn nữa, các nhóm thường ghép nó với mã trạng thái 200, vì vậy thư viện client thậm chí không thấy lỗi.

Hai quy tắc sẽ khắc phục điều này. Cung cấp cho mỗi lỗi riêng biệt một mã ổn định có thể đọc được bằng máy, để tác nhân có thể phân nhánh dựa trên insufficient_funds thay vì cụm từ "không đủ". Và không bao giờ trả về lỗi với mã trạng thái thành công, bất kể lập luận tiện lợi phía client là gì. Một mã 200 có lỗi bên trong sẽ không thể nhìn thấy đối với mọi chính sách thử lại, mọi bảng điều khiển và mọi cảnh báo mà bạn sở hữu.

Danh sách kiểm tra cho lỗi mà tác nhân có thể đọc được

Lỗi là một giao diện. Hãy thiết kế chúng cho người gọi mà bạn thực sự có, mà ngày càng là một mô hình sẽ làm chính xác những gì nội dung phản hồi của bạn bảo nó làm. Tải xuống Apidog để định nghĩa các định dạng lỗi và giả lập chúng trước khi tác nhân gặp chúng trong thực tế.

Các câu hỏi thường gặp

Tôi nên sử dụng RFC 9457 hay định dạng lỗi của riêng mình? Sử dụng RFC 9457 trừ khi bạn đã có một định dạng nhất quán trong sản phẩm. Tính nhất quán quan trọng hơn tiêu chuẩn hóa: chuyển đổi một nửa số điểm cuối của bạn sang một định dạng mới tệ hơn là giữ một định dạng ở mọi nơi. Thêm các phần mở rộng retryablenext_action vào bất kỳ định dạng nào bạn sử dụng.

Có an toàn không khi đặt văn bản next_action vào phản hồi API? Có, khi dịch vụ của bạn tạo ra nó từ một tập hợp các mẫu cố định. Không bao giờ lặp lại nội dung do người dùng cung cấp vào trường đó, vì tác nhân đọc nó như một hướng dẫn và đó là một đường dẫn tấn công prompt-injection. Bài viết của chúng tôi về kiểm thử API chống lại đầu vào không đáng tin cậy bao gồm rủi ro này.

Lỗi xác thực nên là 400 hay 422? Sử dụng 400 khi yêu cầu bị định dạng sai, chẳng hạn như JSON hỏng, và 422 khi yêu cầu phân tích được nhưng không tuân thủ các quy tắc kinh doanh. Các tác nhân được hưởng lợi từ sự phân chia này vì các cách sửa lỗi khác nhau. Nếu bạn đã sử dụng một mã cho cả hai, hãy ghi lại nó thay vì thay đổi.

Bao nhiêu chi tiết là quá nhiều? Dừng lại ở điểm mà người gọi có đủ thông tin để hành động. Tên trường, quy tắc và một giá trị ví dụ thường là đủ. Các định danh nội bộ, văn bản truy vấn và khung ngăn xếp là quá mức.

Thông báo lỗi có tính vào cửa sổ ngữ cảnh không? Có, và một lỗi dài dòng lặp lại trong các lần thử lại sẽ nhanh chóng tích lũy. Giữ chúng dưới vài trăm token. Bài viết của chúng tôi về cắt bớt phản hồi API cho tác nhân áp dụng cho cả lỗi và thành công.

Làm cách nào để ngăn tác nhân thử lại một lỗi không thể thử lại? Đặt retryable: false, nói rõ điều đó trong next_action, và thực thi nó trong wrapper công cụ để phán đoán của mô hình không phải là hàng rào bảo vệ duy nhất. Đảm bảo kép ở đây là đúng đắn.

Thực hành thiết kế API trong Apidog

Khám phá cách dễ dàng hơn để xây dựng và sử dụng API