Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
157 lines
19 KiB
JSON
157 lines
19 KiB
JSON
{
|
|
"artifact_type": "agent_control_code_patch",
|
|
"failure_cluster": [
|
|
{
|
|
"cluster_id": "charge_card:PAYMENT_DECLINED",
|
|
"tool": "charge_card",
|
|
"error_code": "PAYMENT_DECLINED",
|
|
"source_case_ids": [
|
|
"run-payment-001",
|
|
"run-payment-002"
|
|
],
|
|
"cross_trajectory_support": 2,
|
|
"total_redundant_calls": 5
|
|
}
|
|
],
|
|
"source_trajectories": [
|
|
{
|
|
"id": "run-payment-001",
|
|
"trajectory_sha256": "4a0b38498b006bf988279b0ff7984f04245db21ee0668986a49f3ca3f4969008",
|
|
"evidence": "The same declined payment was submitted four times."
|
|
},
|
|
{
|
|
"id": "run-payment-002",
|
|
"trajectory_sha256": "6407eb8005fb39702005d553963405329607b34e67acbb18901c79060dbe1e59",
|
|
"evidence": "A second task repeated the identical non-retryable call three times."
|
|
}
|
|
],
|
|
"inferred_root_cause": "The deterministic control policy ignores retryable=false and does not open the circuit for permanent errors. The root cause belongs in retry/circuit-breaker code, not a prompt.",
|
|
"target_component": "retry_and_circuit_breaker_control",
|
|
"target_file": "stable/retry_policy.py",
|
|
"code_diff": "--- stable/retry_policy.py\n+++ candidate/retry_policy.py\n@@ -1,16 +1,18 @@\n-\"\"\"Stable version 1.0.0. It intentionally contains the reproduced bug.\"\"\"\n-\n-VERSION = \"1.0.0\"\n+VERSION = \"1.0.1\"\n MAX_RETRIES = 3\n CIRCUIT_BREAKER_THRESHOLD = 5\n-NON_RETRYABLE_CODES = {\"AUTH_DENIED\", \"INVALID_ARGUMENT\"}\n+NON_RETRYABLE_CODES = {\"AUTH_DENIED\", \"INVALID_ARGUMENT\", \"PAYMENT_DECLINED\"}\n \n \n def should_retry(error_code, retryable, attempt):\n \"\"\"Return whether another tool call should be attempted.\"\"\"\n+ if not retryable or error_code in NON_RETRYABLE_CODES:\n+ return False\n return attempt < MAX_RETRIES\n \n \n def should_open_circuit(consecutive_failures, *, error_code=\"\", retryable=True):\n \"\"\"Open after repeated failures.\"\"\"\n+ if not retryable or error_code in NON_RETRYABLE_CODES:\n+ return consecutive_failures >= 1\n return consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD\n",
|
|
"impact_prediction": {
|
|
"non_retryable_calls": {
|
|
"before": "up to 4",
|
|
"after": 1
|
|
},
|
|
"temporary_timeout_recovery_rate": {
|
|
"before": 1.0,
|
|
"after": 1.0
|
|
}
|
|
},
|
|
"expected_fix": [
|
|
"non-retryable tool-call attempts fall to one",
|
|
"the circuit opens on the first permanent failure"
|
|
],
|
|
"potential_regressions": [
|
|
"temporary timeouts stop retrying",
|
|
"the five-failure circuit threshold changes for retryable errors",
|
|
"public function signatures change"
|
|
],
|
|
"stable_version": "fcd37db6ebb1",
|
|
"stable_sha256": "fcd37db6ebb19ed2e2492f086365a9f8addfcc4410242151185a1481d130041a",
|
|
"candidate_version": "801604c8b4eb",
|
|
"candidate_sha256": "801604c8b4ebe0611dc0d78c4b0131736341bcac990df6ad817607b8486d402b",
|
|
"rollback_version": "fcd37db6ebb1",
|
|
"rollback_sha256": "fcd37db6ebb19ed2e2492f086365a9f8addfcc4410242151185a1481d130041a",
|
|
"diff": "--- stable/retry_policy.py\n+++ candidate/retry_policy.py\n@@ -1,16 +1,18 @@\n-\"\"\"Stable version 1.0.0. It intentionally contains the reproduced bug.\"\"\"\n-\n-VERSION = \"1.0.0\"\n+VERSION = \"1.0.1\"\n MAX_RETRIES = 3\n CIRCUIT_BREAKER_THRESHOLD = 5\n-NON_RETRYABLE_CODES = {\"AUTH_DENIED\", \"INVALID_ARGUMENT\"}\n+NON_RETRYABLE_CODES = {\"AUTH_DENIED\", \"INVALID_ARGUMENT\", \"PAYMENT_DECLINED\"}\n \n \n def should_retry(error_code, retryable, attempt):\n \"\"\"Return whether another tool call should be attempted.\"\"\"\n+ if not retryable or error_code in NON_RETRYABLE_CODES:\n+ return False\n return attempt < MAX_RETRIES\n \n \n def should_open_circuit(consecutive_failures, *, error_code=\"\", retryable=True):\n \"\"\"Open after repeated failures.\"\"\"\n+ if not retryable or error_code in NON_RETRYABLE_CODES:\n+ return consecutive_failures >= 1\n return consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD\n",
|
|
"patch_size": {
|
|
"added_lines": 6,
|
|
"deleted_lines": 4,
|
|
"changed_lines": 10
|
|
},
|
|
"checks": {
|
|
"static_compile": true,
|
|
"public_api_compatible": true,
|
|
"security_scan": true,
|
|
"failure_replay": true,
|
|
"nonretryable_circuit": true,
|
|
"temporary_recovery": true,
|
|
"old_task_regression": true,
|
|
"canary_ready": true,
|
|
"rollback_ready": true,
|
|
"protected_surfaces_unchanged": true
|
|
},
|
|
"failed_checks": [],
|
|
"canary_gate": {
|
|
"eligible": true,
|
|
"scope": "shadow traffic only; stable remains unchanged",
|
|
"rollback_trigger": "any non-retryable repeat or temporary-recovery regression"
|
|
},
|
|
"rollback_gate": {
|
|
"artifact_hash_matches_stable": true,
|
|
"rollback_version": "fcd37db6ebb1"
|
|
},
|
|
"provenance": {
|
|
"generator": "real_llm_coding_agent",
|
|
"model": "doubao-seed-1-6-250615",
|
|
"provider": "ark",
|
|
"seed": 8501,
|
|
"api_calls": 1,
|
|
"receipt": {
|
|
"backend": {
|
|
"provider": "ark",
|
|
"endpoint": "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
|
"credential_env": "ARK_API_KEY",
|
|
"model": "doubao-seed-1-6-250615",
|
|
"credential_value_recorded": false
|
|
},
|
|
"request": {
|
|
"model": "doubao-seed-1-6-250615",
|
|
"messages": [
|
|
{
|
|
"role": "user",
|
|
"content": "You are the Coding Agent in a controlled self-modification pipeline.\n\nModify only the supplied retry-policy module. Preserve both public function\nsignatures and retry behavior for temporary failures. Permanent failures\n(retryable=false or a listed permanent code) must not be retried and must open\nthe circuit on the first occurrence. Update VERSION to a candidate version.\nDo not import modules, access files, or alter validation/release logic.\n\nBefore the source, predict the intended impact. Return JSON only:\n{\"impact_prediction\": {\"non_retryable_calls\": {\"before\": \"up to 4\", \"after\": 1},\n\"temporary_timeout_recovery_rate\": {\"before\": 1.0, \"after\": 1.0}},\n\"source\": \"the complete Python module\"}\n\nFailure diagnosis:\n{\n \"change_required\": true,\n \"target\": \"stable/retry_policy.py\",\n \"target_component\": \"retry_and_circuit_breaker_control\",\n \"source_case_ids\": [\n \"run-payment-001\",\n \"run-payment-002\"\n ],\n \"source_trajectories\": [\n {\n \"id\": \"run-payment-001\",\n \"trajectory_sha256\": \"4a0b38498b006bf988279b0ff7984f04245db21ee0668986a49f3ca3f4969008\",\n \"evidence\": \"The same declined payment was submitted four times.\"\n },\n {\n \"id\": \"run-payment-002\",\n \"trajectory_sha256\": \"6407eb8005fb39702005d553963405329607b34e67acbb18901c79060dbe1e59\",\n \"evidence\": \"A second task repeated the identical non-retryable call three times.\"\n }\n ],\n \"patterns\": [\n {\n \"cluster_id\": \"charge_card:PAYMENT_DECLINED\",\n \"tool\": \"charge_card\",\n \"error_code\": \"PAYMENT_DECLINED\",\n \"source_case_ids\": [\n \"run-payment-001\",\n \"run-payment-002\"\n ],\n \"cross_trajectory_support\": 2,\n \"total_redundant_calls\": 5\n }\n ],\n \"reason\": \"The deterministic control policy ignores retryable=false and does not open the circuit for permanent errors. The root cause belongs in retry/circuit-breaker code, not a prompt.\",\n \"change_contract\": {\n \"expected_fix\": [\n \"non-retryable tool-call attempts fall to one\",\n \"the circuit opens on the first permanent failure\"\n ],\n \"potential_regressions\": [\n \"temporary timeouts stop retrying\",\n \"the five-failure circuit threshold changes for retryable errors\",\n \"public function signatures change\"\n ]\n }\n}\n\nPreviously rejected candidates (do not repeat their failure):\n[\n {\n \"candidate_sha256\": \"824bbfb244a7b17ac835706be588542a63ad03e0d737d6c9d5da719c969652d2\",\n \"failed_checks\": [\n \"temporary_recovery\",\n \"old_task_regression\",\n \"canary_ready\"\n ],\n \"rejection_reason\": \"failed gates: temporary_recovery, old_task_regression, canary_ready\",\n \"failure\": \"disabled temporary-timeout retries\"\n }\n]\n\nStable module:\n\"\"\"Stable version 1.0.0. It intentionally contains the reproduced bug.\"\"\"\n\nVERSION = \"1.0.0\"\nMAX_RETRIES = 3\nCIRCUIT_BREAKER_THRESHOLD = 5\nNON_RETRYABLE_CODES = {\"AUTH_DENIED\", \"INVALID_ARGUMENT\"}\n\n\ndef should_retry(error_code, retryable, attempt):\n \"\"\"Return whether another tool call should be attempted.\"\"\"\n return attempt < MAX_RETRIES\n\n\ndef should_open_circuit(consecutive_failures, *, error_code=\"\", retryable=True):\n \"\"\"Open after repeated failures.\"\"\"\n return consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD\n\n"
|
|
}
|
|
],
|
|
"temperature": 0,
|
|
"seed": 8501,
|
|
"max_tokens": 1400,
|
|
"response_format": {
|
|
"type": "json_object"
|
|
}
|
|
},
|
|
"response": {
|
|
"id": "021785344994260f79d90d738734a45cb3eac38ebc4a0743606af",
|
|
"choices": [
|
|
{
|
|
"finish_reason": "stop",
|
|
"index": 0,
|
|
"message": {
|
|
"content": "{\"impact_prediction\": {\"non_retryable_calls\": {\"before\": \"up to 4\", \"after\": 1}, \"temporary_timeout_recovery_rate\": {\"before\": 1.0, \"after\": 1.0}}, \"source\": \"VERSION = \\\"1.0.1\\\"\\nMAX_RETRIES = 3\\nCIRCUIT_BREAKER_THRESHOLD = 5\\nNON_RETRYABLE_CODES = {\\\"AUTH_DENIED\\\", \\\"INVALID_ARGUMENT\\\", \\\"PAYMENT_DECLINED\\\"}\\n\\n\\ndef should_retry(error_code, retryable, attempt):\\n \\\"\\\"\\\"Return whether another tool call should be attempted.\\\"\\\"\\\"\\n if not retryable or error_code in NON_RETRYABLE_CODES:\\n return False\\n return attempt < MAX_RETRIES\\n\\n\\ndef should_open_circuit(consecutive_failures, *, error_code=\\\"\\\", retryable=True):\\n \\\"\\\"\\\"Open after repeated failures.\\\"\\\"\\\"\\n if not retryable or error_code in NON_RETRYABLE_CODES:\\n return consecutive_failures >= 1\\n return consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD\"}",
|
|
"role": "assistant",
|
|
"reasoning_content": "Okay, let's tackle this problem. The user wants me to modify the retry-policy module to fix the issues where non-retryable calls are being retried multiple times and the circuit isn't opening on the first permanent failure. \n\nFirst, I need to understand the current code. The existing code has two functions: should_retry and should_open_circuit. The problem is that should_retry isn't considering the retryable flag or the error code. Right now, it just checks if the attempt is less than MAX_RETRIES (which is 3), so even if the error is non-retryable, it would retry up to 3 times, leading to 4 total attempts (initial + 3 retries). That explains the evidence from the cases where non-retryable calls were made multiple times.\n\nThe change contract says that non-retryable attempts should fall to one. So, for a non-retryable error, there should be no retries. That means the initial call is made, and if it's non-retryable, we don't retry. So the should_retry function needs to check if the error is retryable. If retryable is False, then should_retry returns False. Also, if the error code is in NON_RETRYABLE_CODES, then it's not retryable.\n\nWait, looking at the existing code: the NON_RETRYABLE_CODES is a set containing \"AUTH_DENIED\" and \"INVALID_ARGUMENT\". But the patterns in the failure diagnosis mention \"PAYMENT_DECLINED\" as an error code that's causing issues. So maybe \"PAYMENT_DECLINED\" should be added to NON_RETRYABLE_CODES? Wait, the problem says that the current code ignores retryable=false and doesn't open the circuit for permanent errors. So perhaps the NON_RETRYABLE_CODES in the current code doesn't include \"PAYMENT_DECLINED\", which is why those errors are being retried. But the user's problem is that even when retryable is false, the code retries. So the should_retry function needs to check both the retryable flag and the error code.\n\nSo modifying the should_retry function: if retryable is False, then return False. Also, if the error_code is in NON_RETRYABLE_CODES, return False. Otherwise, check if the attempt is less than MAX_RETRIES.\n\nWait, the parameters for should_retry are error_code, retryable, attempt. So the function should return True only if the error is retryable (retryable=True), the error code is not in NON_RETRYABLE_CODES, and the attempt count is less than MAX_RETRIES.\n\nSo the corrected should_retry would be:\n\ndef should_retry(error_code, retryable, attempt):\n if not retryable or error_code in NON_RETRYABLE_CODES:\n return False\n return attempt < MAX_RETRIES\n\nThat way, if either retryable is False or the error code is non-retryable, we don't retry. That would prevent retries for those cases, leading to only one attempt (the initial call) for non-retryable errors.\n\nNext, the circuit breaker. The current should_open_circuit function returns True when consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD (which is 5). But according to the problem, permanent failures (retryable=false or listed permanent code) should open the circuit on the first occurrence. So the circuit should open immediately for permanent failures, but for temporary failures, it should open after 5 consecutive failures as before.\n\nSo the should_open_circuit function needs to check if the error is a permanent failure. If it is, then open the circuit (return True) on the first failure. Otherwise, use the existing threshold.\n\nThe parameters for should_open_circuit are consecutive_failures, error_code, retryable. So, if the error is not retryable (retryable=False) or error_code is in NON_RETRYABLE_CODES, then it's a permanent failure. In that case, if consecutive_failures >= 1, return True. For temporary failures (retryable=True and error_code not in NON_RETRYABLE_CODES), check if consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD.\n\nSo modifying should_open_circuit:\n\ndef should_open_circuit(consecutive_failures, *, error_code=\"\", retryable=True):\n # Check if it's a permanent failure\n if not retryable or error_code in NON_RETRYABLE_CODES:\n # Open circuit on first occurrence\n return consecutive_failures >= 1\n # For temporary failures, use the threshold\n return consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD\n\nWait, but the original code's parameters have the keyword-only argument for error_code and retryable. The original signature is:\n\ndef should_open_circuit(consecutive_failures, *, error_code=\"\", retryable=True):\n\nSo the keyword arguments are error_code and retryable, with defaults. So in the case where a permanent failure occurs (either retryable=False or error_code is in NON_RETRYABLE), then consecutive_failures (which counts the number of consecutive failures) would be 1 after the first failure. So returning consecutive_failures >=1 would open the circuit immediately.\n\nThat would satisfy the requirement that the circuit opens on the first permanent failure.\n\nAdditionally, the VERSION needs to be updated to a candidate version. The current version is 1.0.0, so perhaps 1.0.1 or 1.1.0. Since it's a bug fix, maybe 1.0.1.\n\nAlso, the NON_RETRYABLE_CODES in the original code includes {\"AUTH_DENIED\", \"INVALID_ARGUMENT\"}, but the problem mentions \"PAYMENT_DECLINED\" as an error code that's causing redundant calls. So we need to add \"PAYMENT_DECLINED\" to NON_RETRYABLE_CODES. Because in the patterns, the cluster is \"charge_card:PAYMENT_DECLINED\", so that error code should be considered non-retryable. So modifying NON_RETRYABLE_CODES to include \"PAYMENT_DECLINED\".\n\nSo NON_RETRYABLE_CODES = {\"AUTH_DENIED\", \"INVALID_ARGUMENT\", \"PAYMENT_DECLINED\"}\n\nPutting this all together:\n\n- Update VERSION to \"1.0.1\"\n- Add \"PAYMENT_DECLINED\" to NON_RETRYABLE_CODES\n- Modify should_retry to check retryable flag and error code\n- Modify should_open_circuit to open on first permanent failure\n\nTesting the changes:\n\nFor a non-retryable error (like PAYMENT_DECLINED), should_retry would return False, so no retries. The initial attempt is 1, and since should_retry is False, only one call is made. Then, should_open_circuit would see retryable=False (or error_code in NON_RETRYABLE), so consecutive_failures=1 would trigger the circuit to open.\n\nFor temporary errors (retryable=True and not in non-retry codes), should_retry allows up to MAX_RETRIES (3) retries, so 4 attempts total (initial + 3 retries). The circuit opens after 5 consecutive failures, which is the same as before, so temporary timeout recovery rate remains 1.0.\n\nThis should fix the problem where non-retryable calls were being retried multiple times, reducing them to 1 attempt. Also, the circuit opens on the first permanent failure.\n\nPotential regressions: The problem mentions that temporary timeouts should still retry. Since the code now checks retryable=True and error code not in non-retryable, temporary timeouts (which are retryable) should still be retried. So that's covered.\n\nSo the modified code would look like:\n\nVERSION = \"1.0.1\"\nMAX_RETRIES = 3\nCIRCUIT_BREAKER_THRESHOLD = 5\nNON_RETRYABLE_CODES = {\"AUTH_DENIED\", \"INVALID_ARGUMENT\", \"PAYMENT_DECLINED\"}\n\n\ndef should_retry(error_code, retryable, attempt):\n \"\"\"Return whether another tool call should be attempted.\"\"\"\n if not retryable or error_code in NON_RETRYABLE_CODES:\n return False\n return attempt < MAX_RETRIES\n\n\ndef should_open_circuit(consecutive_failures, *, error_code=\"\", retryable=True):\n \"\"\"Open after repeated failures.\"\"\"\n # Check for permanent failure (non-retryable or permanent error code)\n if not retryable or error_code in NON_RETRYABLE_CODES:\n return consecutive_failures >= 1\n # Temporary failures use the threshold\n return consecutive_failures >= CIRCUIT_BREAKER_THRESHOLD\n\nYes, this should meet the requirements. The non-retryable calls will have only one attempt, the circuit opens on first permanent failure, and temporary failures are retried as before.\n"
|
|
}
|
|
}
|
|
],
|
|
"created": 1785345050,
|
|
"model": "doubao-seed-1-6-250615",
|
|
"object": "chat.completion",
|
|
"service_tier": "default",
|
|
"usage": {
|
|
"completion_tokens": 2176,
|
|
"prompt_tokens": 1128,
|
|
"total_tokens": 3304,
|
|
"completion_tokens_details": {
|
|
"reasoning_tokens": 1919
|
|
},
|
|
"prompt_tokens_details": {
|
|
"cached_tokens": 0
|
|
}
|
|
}
|
|
},
|
|
"request_sha256": "fe5695eec21fe9f9674035f307d8e5f6bfacafa244a8184eb734188d6be405af",
|
|
"response_sha256": "d43b17e9ab510e2ae7abb852841316177686d1e425c4214ac6e7d0a4183c9fee",
|
|
"elapsed_seconds": 56.861222,
|
|
"usage": {
|
|
"prompt_tokens": 1128,
|
|
"completion_tokens": 2176,
|
|
"total_tokens": 3304,
|
|
"provider_reported_cost_usd": null,
|
|
"cost_qualification": "provider did not expose monetary cost; no price was guessed"
|
|
}
|
|
}
|
|
},
|
|
"decision": "release_to_canary",
|
|
"rejection_reason": null
|
|
} |