Harness Academy
Tài liệu gốc 09 11 phútTiếng Việt + English
docs/en/s06-error-recovery.mdXem bản gốc
Ngôn ngữ bài đọc
Bản dịch tiếng Việt

Bản dịch đầy đủ theo đúng cấu trúc tài liệu; code, liên kết và định danh kỹ thuật được giữ nguyên.

s06: Khôi phục lỗi

s00 > s01 > s02 > s03 > s04 > s05 > [ s06 ] > s07 > s08 > s09 > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > s18 > s19 > s20 > s21 > s22 > s23 > s24 > s25 > s26 > s27

Lỗi không phải là ngoại lệ -- chúng là một nhánh bình thường mà vòng lặp chính phải giải quyết.

Chương này giải quyết vấn đề gì

Bởi s05, agent có hệ thống công cụ đầy đủ, tính bền bỉ, lắp ráp prompt và nén ngữ cảnh.

Tại thời điểm này, hệ thống không còn là bản demo nữa mà là một chương trình thực sự thực hiện mọi việc. Các vấn đề đi kèm với nó:

  • Đầu ra của mô hình bị cắt giữa dòng (finish_reason: length)
  • Ngữ cảnh quá dài và API trả về 400
  • Hết thời gian chờ mạng, giới hạn tốc độ, lỗi dịch vụ
  • Khóa API đã hết hạn hoặc hết hạn ngạch
  • Model không tồn tại hoặc đã ngừng hoạt động

Nếu không có cơ chế phục hồi, vòng lặp chính sẽ gặp lỗi ở lỗi đầu tiên.

Nhưng nhiều lần thất bại không có nghĩa là “nhiệm vụ đã thực sự thất bại”. Họ chỉ có nghĩa là:

Lượt này cần có cách khác để tiếp tục.

agent Hermes không sử dụng phép thử/ngoại trừ + thử lại đơn giản. Đầu tiên, nó phân loại lỗi, sau đó chọn chiến lược khôi phục dựa trên phân loại. Và vì nó hỗ trợ hơn 200 mô hình trên nhiều nhà cung cấp nên phạm vi lỗi sẽ rộng hơn nhiều so với một agent một nhà cung cấp.

Phân loại và khôi phục lỗi

Giải thích các thuật ngữ chính

Phân loại lỗi là gì

Các lỗi khác nhau đòi hỏi cách xử lý khác nhau.

Giới hạn tốc độ (429) nên dừng lại và thử lại. Tràn ngữ cảnh (400) sẽ kích hoạt quá trình nén. Lỗi xác thực (401) sẽ xoay vòng thông tin xác thực. Không tìm thấy mô hình (404) sẽ chuyển về mô hình dự phòng.

Nếu không phân loại trước, mọi lỗi sẽ đi theo cùng một đường dẫn -- thứ gì đó nên thử lại sẽ bị nén, thứ gì đó nên từ bỏ sẽ đi vào một vòng lặp vô hạn.

Chuyển đổi dự phòng là gì

Khi mô hình hoặc nhà cung cấp hiện tại gặp sự cố không thể khắc phục, hãy tự động chuyển sang mô hình dự phòng.

Ví dụ: mô hình chính bị giới hạn tốc độ nên hệ thống sẽ tự động chuyển sang mô hình dự phòng và tiếp tục hoạt động. Không cần can thiệp thủ công.

Thử lại lùi lại là gì

Thay vì thử lại ngay sau khi xảy ra lỗi, hãy đợi một khoảng thời gian.

Bao lâu? Tăng theo cấp số nhân cộng với jitter ngẫu nhiên. Lần đầu chờ 5 giây, lần thứ hai đợi 10 giây, lần thứ ba đợi 20 giây... cộng với khoảng chênh lệch ngẫu nhiên để ngăn nhiều phiên Cổng thử lại cùng lúc.

Tiếp tục là gì

Đầu ra mô hình đã bị cắt ngắn (finish_reason: length). Điều này không có nghĩa là mô hình không thể thực hiện được -- điều đó có nghĩa là không có đủ không gian đầu ra ở lượt này.

Tiếp tục có nghĩa là thêm một thông báo cho mô hình biết "tiếp tục chính xác từ nơi bạn đã dừng, không bắt đầu lại", sau đó thực hiện một lệnh gọi API khác.

Mô hình tinh thần tối thiểu

Phiên bản dạy học chỉ cần phân biệt 4 loại vấn đề:

1. Output truncated (finish_reason: length)
   -> Inject a continuation prompt and retry

2. Context too long (400 / context overflow)
   -> Trigger compression (s05) and retry

3. Transient failure (429 rate limit / 503 overloaded / timeout)
   -> Back off and retry

4. Unrecoverable (401 auth failure / 404 model not found / quota exhausted)
   -> Attempt failover to a backup model, or give up
API call
  |
  +-- Success, finish_reason: stop
  |      -> Normal completion
  |
  +-- Success, finish_reason: tool_calls
  |      -> Execute tools, continue loop
  |
  +-- Success, finish_reason: length
  |      -> Continuation (up to 3 times)
  |
  +-- Failure, recoverable
  |      -> Classify -> backoff / compress / rotate credentials
  |
  +-- Failure, unrecoverable
         -> Failover / give up

Cấu trúc dữ liệu chính

1. Kết quả phân loại lỗi

classified = {
    "reason": "rate_limit",        # Why it failed
    "retryable": True,             # Can it be retried
    "should_compress": False,      # Should compression be triggered
    "should_fallback": False,      # Should it fail over to a backup model
}

Điều này tách biệt "lỗi trông như thế nào" khỏi "việc cần làm tiếp theo". Vòng lặp không cần hiểu chi tiết cụ thể về lỗi -- nó chỉ đọc một vài cờ boolean từ kết quả phân loại để biết nên đi theo đường dẫn nào.

2. Nguyên nhân thất bại

agent Hermes xác định hơn chục lý do thất bại, nhưng vì mục đích giảng dạy nên bắt đầu bằng những lý do sau:

rate_limit     -> Back off and retry
overloaded     -> Back off and retry
timeout        -> Rebuild connection + retry
context_overflow -> Trigger compression
billing        -> Rotate credentials or fall back to backup model
auth           -> Rotate credentials or fall back to backup model
model_not_found -> Fall back to backup model

Mỗi lý do ánh xạ tới một hành động khôi phục khác nhau. Đó là giá trị của việc phân loại.

3. Lời nhắc tiếp tục

CONTINUE_MESSAGE = (
    "Your response was cut off. Continue EXACTLY from where you stopped. "
    "Do not restart, do not repeat, do not summarize what came before."
)

Lời nhắc này rất quan trọng. Nếu bạn chỉ nói "tiếp tục", mô hình thường bắt đầu lại bằng phần tóm tắt hoặc bắt đầu từ đầu.

Thực hiện tối thiểu

Bước 1: Viết trình phân loại lỗi

def classify_error(status_code, error_message):
    if status_code == 429:
        return {"reason": "rate_limit", "retryable": True, "should_compress": False, "should_fallback": False}
    
    if status_code == 400 and "context" in error_message.lower():
        return {"reason": "context_overflow", "retryable": True, "should_compress": True, "should_fallback": False}
    
    if status_code in (500, 502, 503):
        return {"reason": "server_error", "retryable": True, "should_compress": False, "should_fallback": False}
    
    if status_code in (401, 403):
        return {"reason": "auth", "retryable": False, "should_compress": False, "should_fallback": True}
    
    if status_code == 404:
        return {"reason": "model_not_found", "retryable": False, "should_compress": False, "should_fallback": True}
    
    return {"reason": "unknown", "retryable": False, "should_compress": False, "should_fallback": False}

Ý tưởng chính đằng sau bước này:

Trình phân loại dịch "Mã trạng thái HTTP + thông báo lỗi" thành "nên thử lại / nên nén / nên thất bại / nên bỏ cuộc." Vòng lặp chỉ đọc bản dịch.

Bước 2: Viết backoff thử lại

def jittered_backoff(attempt, base_delay=5.0, max_delay=120.0):
    delay = min(base_delay * (2 ** (attempt - 1)), max_delay)
    jitter = random.uniform(0, delay * 0.5)
    return delay + jitter

Ý tưởng chính đằng sau bước này:

Tăng theo cấp số nhân cộng với jitter ngẫu nhiên. Sự gia tăng mang lại cho máy chủ khoảng trống; jitter ngăn nhiều phiên Cổng thử lại cùng một lúc.

Bước 3: Móc vào vòng lặp chính

retry_count = 0
continuation_count = 0

while iteration < max_iterations:
    try:
        response = client.chat.completions.create(...)
    except Exception as e:
        classified = classify_error(getattr(e, "status_code", None), str(e))
        
        if classified["should_compress"]:
            messages = compress(messages)
            continue
        
        if classified["should_fallback"] and fallback_model:
            switch_to_fallback_model()
            continue
        
        if classified["retryable"] and retry_count < 3:
            retry_count += 1
            time.sleep(jittered_backoff(retry_count))
            continue
        
        raise  # Unrecoverable, propagate upward
    
    # After getting a response
    finish_reason = response.choices[0].finish_reason
    
    if finish_reason == "length" and continuation_count < 3:
        continuation_count += 1
        messages.append({"role": "user", "content": CONTINUE_MESSAGE})
        continue
    
    # Normal processing of tool_calls or completion
    ...

Hãy lưu ý điểm mấu chốt ở đây: phân loại và khôi phục là hai bước riêng biệt. Phân loại trước, sau đó thực hiện đường dẫn khôi phục tương ứng. Mỗi đường dẫn có ngân sách thử lại riêng.

Bước 4: Chuyển đổi dự phòng

def switch_to_fallback_model():
    # Switch to the backup model from config
    self.model = fallback_model["model"]
    self.base_url = fallback_model["base_url"]
    self.api_key = fallback_model["api_key"]
    # Rebuild the API client
    self._client = OpenAI(base_url=self.base_url, api_key=self.api_key)

Ý tưởng chính đằng sau bước này:

Vì agent Hermes hỗ trợ bất kỳ nhà cung cấp nào thông qua base_url, chuyển đổi dự phòng chỉ đơn giản là "hoán đổi một tập hợp giá trị cấu hình khác." Không cần thay đổi mã, không cần thay đổi định dạng tin nhắn.

Lựa chọn thiết kế độc đáo của agent Hermes tại đây

1. Nhiều nhà cung cấp đồng nghĩa với nhiều loại lỗi hơn

agent một nhà cung cấp chỉ cần xử lý định dạng lỗi của một API. Hermes Agent hỗ trợ OpenRouter, Anthropic, điểm cuối cục bộ, v.v. -- mỗi điểm có định dạng lỗi và ngữ nghĩa mã trạng thái hơi khác nhau.

Trình phân loại phải trích xuất một lý do lỗi thống nhất từ ​​các thông báo lỗi trên các nhà cung cấp khác nhau. Ví dụ: "hết hạn ngạch" được các nhà cung cấp khác nhau diễn đạt khác nhau nhưng kết quả phân loại luôn là billing.

2. Kiểm tra tình trạng kết nối

trước đây run_conversation() bắt đầu, hệ thống sẽ kiểm tra xem kết nối của máy khách API có tốt không. Nếu nó phát hiện một kết nối chết còn sót lại từ vòng trước (ví dụ: kết nối TCP vẫn bị treo sau khi hết thời gian chờ), nó sẽ chủ động dọn sạch kết nối đó.

Đừng đợi cho đến khi lệnh gọi API mới bị kẹt trên kết nối zombie.

3. Phục hồi mô hình chính

Sau khi chuyển sang mô hình dự phòng thất bại, bước tiếp theo run_conversation() cuộc gọi bắt đầu bằng cách cố gắng chuyển về mô hình chính.

Cách này chuyển đổi dự phòng là tạm thời. Nếu mô hình chính đã được khôi phục, vòng tiếp theo sẽ tự động chuyển trở lại -- không cần can thiệp thủ công.

4. Phát hiện ngân sách tư duy

Một số mô hình (những mô hình hỗ trợ lý luận/suy nghĩ) có thể dành tất cả token đầu ra cho việc suy nghĩ, để lại 0 cho câu trả lời. Trong trường hợp này finish_reason: length xảy ra nhưng việc tiếp tục là vô nghĩa.

agent Hermes phát hiện tình huống này và báo cáo lỗi trực tiếp thay vì lãng phí 3 lần thử lại liên tục.

Những lỗi thường gặp của người mới bắt đầu

1. Coi tất cả các lỗi là cùng một lỗi

Cái gì đó đáng lẽ phải tiếp tục lại bị nén lại; thứ gì đó đáng lẽ phải chờ sẽ đi vào một vòng lặp vô hạn; một cái gì đó nên từ bỏ sẽ thử lại mãi mãi.

2. Không có ngân sách thử lại

Mọi đường dẫn khôi phục đều phải có giới hạn. Tiếp tục tối đa 3 lần, lùi tối đa 3 lần. Nếu không có ngân sách, vòng lặp có thể không bao giờ kết thúc.

3. Viết lời nhắc tiếp tục mơ hồ

Chỉ viết "tiếp tục" thường là không đủ. Bạn cần yêu cầu rõ ràng rằng mô hình không lặp lại, không tóm tắt lại và chọn trực tiếp từ điểm giới hạn.

4. Backoff không có jitter ngẫu nhiên

Thời gian chờ xác định (ví dụ: luôn chờ 10 giây) khiến tất cả các lần thử lại gặp xung đột trong kịch bản Cổng nhiều phiên (bầy sấm sét). Jitter ngẫu nhiên lây lan chúng ra.

5. Không cố gắng khôi phục mô hình chính sau khi chuyển đổi dự phòng

Nếu bạn chuyển sang mô hình dự phòng và không bao giờ chuyển trở lại, người dùng sẽ bị mắc kẹt với một mô hình dự phòng có khả năng yếu hơn hoặc đắt tiền hơn.

Làm thế nào chương này phù hợp với vòng lặp chính

Bắt đầu từ chương này, vòng lặp chính không còn là "mô hình cuộc gọi -> công cụ thực thi" đơn giản nữa. Nó trở thành:

1. Call the model
2. If the call fails -> classify the error -> choose a recovery path
3. If the output was truncated -> continuation
4. If successful -> execute tools normally
5. If any recovery path fails -> report upward

Nói cách khác, vòng lặp chính hiện duy trì đồng thời ba thứ:

Task progression (calling the model, running tools)
Context budget (compression from s05)
Error recovery (classification, retry, failover)

Đây là chương cuối cùng của Giai đoạn 1. Đến thời điểm này, bạn có một agent duy nhất có thể hoạt động, có thể tồn tại, có thể tập hợp lời nhắc, có thể nén ngữ cảnh và có thể khôi phục sau lỗi.

Giai đoạn 2 bổ sung lớp thông minh: bộ nhớ, kỹ năng, bảo mật, ủy quyền và cấu hình.

Ranh giới giảng dạy

Chương này chỉ cần trình bày rõ ràng 4 đường dẫn khôi phục:

  1. Đầu ra bị cắt ngắn -> tiếp tục
  2. Ngữ cảnh quá dài -> nén
  3. Thất bại thoáng qua -> thử lại lần nữa
  4. Không thể phục hồi -> chuyển đổi dự phòng hoặc từ bỏ

Cố tình trì hoãn: sự khác biệt về định dạng lỗi cụ thể của từng nhà cung cấp, luân chuyển nhóm thông tin xác thực, chi tiết triển khai kiểm tra tình trạng kết nối.

Nếu người đọc có thể khiến agent "không gặp sự cố khi giới hạn tốc độ, tiếp tục khi bị cắt ngắn và tự động chuyển sang bản sao lưu khi mô hình chính không hoạt động", thì chương này đã đạt được mục tiêu.

Một dòng cần nhớ

Phân loại lỗi trước, sau đó thực hiện khôi phục và chỉ đưa ra lỗi cho người dùng như là phương sách cuối cùng. Vì Hermes Agent hỗ trợ nhiều nhà cung cấp nên trình phân loại phải trích xuất lý do lỗi thống nhất từ ​​các thông báo lỗi ở các định dạng khác nhau.

Mục lục nguồn