Fix Bedrock tool history and add upload limit config

This commit is contained in:
wuyang6
2026-05-14 11:08:05 +08:00
parent d7c62a4929
commit cccdd44ca2
3 changed files with 134 additions and 1 deletions
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
# 配置线上 nginx 上传体积限制。
# 需要 sudo 权限;应用服务本身仍然用普通用户运行。
LIMIT="${CLAW_NGINX_CLIENT_MAX_BODY_SIZE:-300m}"
CONF_PATH="${CLAW_NGINX_UPLOAD_CONF:-/etc/nginx/conf.d/zk-data-agent-upload.conf}"
if ! command -v nginx >/dev/null 2>&1; then
echo "WARN 未发现 nginx,跳过上传限制配置。"
exit 0
fi
if ! command -v sudo >/dev/null 2>&1; then
echo "ERROR 配置 nginx 需要 sudo。" >&2
exit 1
fi
sudo tee "${CONF_PATH}" >/dev/null <<EOF
# Managed by zk-data-agent.
# 放开 Web 文件上传体积,避免大文件上传被 nginx 直接 413。
client_max_body_size ${LIMIT};
EOF
sudo nginx -t
sudo systemctl reload nginx
echo "已配置 nginx client_max_body_size=${LIMIT} (${CONF_PATH})"
+12
View File
@@ -122,6 +122,17 @@ bootstrap_system_packages() {
systemd systemd
} }
configure_nginx_upload_limit() {
if [[ "${BOOTSTRAP_SYSTEM}" != "1" && -f "${ENV_FILE}" ]]; then
return
fi
local script="${ROOT_DIR}/scripts/configure-nginx-upload-limit.sh"
if [[ -x "${script}" ]]; then
log "配置 nginx 上传体积限制"
"${script}" || warn "nginx 上传体积限制配置失败,可稍后手动执行:bash ${script}"
fi
}
prompt_value() { prompt_value() {
local var_name="$1" local var_name="$1"
local prompt="$2" local prompt="$2"
@@ -345,6 +356,7 @@ health_check() {
main() { main() {
log "ZK Data Agent Ubuntu 部署" log "ZK Data Agent Ubuntu 部署"
bootstrap_system_packages bootstrap_system_packages
configure_nginx_upload_limit
update_git update_git
ensure_env_file ensure_env_file
configure_instance_names configure_instance_names
+94 -1
View File
@@ -91,6 +91,99 @@ def _parse_tool_arguments(raw_arguments: Any) -> dict[str, Any]:
) )
def _tool_call_ids(message: dict[str, Any]) -> list[str]:
raw_tool_calls = message.get('tool_calls')
if not isinstance(raw_tool_calls, list):
return []
ids: list[str] = []
for index, raw_call in enumerate(raw_tool_calls):
if not isinstance(raw_call, dict):
continue
call_id = raw_call.get('id')
ids.append(call_id if isinstance(call_id, str) and call_id else f'call_{index}')
return ids
def _contiguous_tool_result_ids(
messages: list[dict[str, Any]],
start_index: int,
) -> set[str]:
ids: set[str] = set()
index = start_index
while index < len(messages) and messages[index].get('role') == 'tool':
tool_call_id = messages[index].get('tool_call_id')
if isinstance(tool_call_id, str) and tool_call_id:
ids.add(tool_call_id)
index += 1
return ids
def _filter_tool_calls_by_ids(
message: dict[str, Any],
valid_ids: set[str],
) -> dict[str, Any]:
raw_tool_calls = message.get('tool_calls')
if not isinstance(raw_tool_calls, list):
return message
filtered: list[dict[str, Any]] = []
for index, raw_call in enumerate(raw_tool_calls):
if not isinstance(raw_call, dict):
continue
call_id = raw_call.get('id')
normalized_id = (
call_id if isinstance(call_id, str) and call_id else f'call_{index}'
)
if normalized_id in valid_ids:
filtered.append(raw_call)
if len(filtered) == len(raw_tool_calls):
return message
next_message = dict(message)
if filtered:
next_message['tool_calls'] = filtered
else:
next_message.pop('tool_calls', None)
return next_message
def _sanitize_anthropic_tool_messages(
messages: list[dict[str, Any]],
) -> list[dict[str, Any]]:
"""清理 Anthropic/Bedrock 严格要求的 tool_use/tool_result 邻接关系。
OpenAI 风格历史在异常中断、裁剪或前端恢复时可能留下 assistant tool_calls
但下一条消息不再紧跟对应 tool 结果。Anthropic Messages API 会直接拒绝
这种历史。这里在出网关前删除缺失结果的 tool_call,并跳过没有前置
tool_use 的孤儿 tool 结果,保留普通文本上下文继续对话。
"""
sanitized: list[dict[str, Any]] = []
pending_tool_ids: set[str] = set()
for index, message in enumerate(messages):
role = message.get('role')
if role == 'assistant':
tool_ids = _tool_call_ids(message)
if tool_ids:
result_ids = _contiguous_tool_result_ids(messages, index + 1)
valid_ids = set(tool_ids).intersection(result_ids)
message = _filter_tool_calls_by_ids(message, valid_ids)
pending_tool_ids = valid_ids
else:
pending_tool_ids = set()
sanitized.append(message)
continue
if role == 'tool':
tool_call_id = message.get('tool_call_id')
if isinstance(tool_call_id, str) and tool_call_id in pending_tool_ids:
sanitized.append(message)
continue
pending_tool_ids = set()
sanitized.append(message)
return sanitized
def _optional_int(value: Any) -> int: def _optional_int(value: Any) -> int:
if isinstance(value, bool): if isinstance(value, bool):
return 0 return 0
@@ -341,7 +434,7 @@ class OpenAICompatClient:
) -> dict[str, Any]: ) -> dict[str, Any]:
system_parts: list[str] = [] system_parts: list[str] = []
anthropic_messages: list[dict[str, Any]] = [] anthropic_messages: list[dict[str, Any]] = []
for message in messages: for message in _sanitize_anthropic_tool_messages(messages):
role = message.get('role') role = message.get('role')
if role == 'system': if role == 'system':
content = _normalize_content(message.get('content')).strip() content = _normalize_content(message.get('content')).strip()