fix: audit findings (fallback dead code, silent fallback, registry re-parse, shadow sort, gateway capability)
- RoutingPolicy.fallback_models: move the cleaning loop into __init__ (it was unreachable after tier_mode's return, so the attribute never existed); still parsed but not applied. - Capability fallback: RoutingDecision gains capability_fallback; the worker audits worker_capability_fallback when it fires, so a tier-model rejection followed by a worker-default fallback is no longer invisible (source stays worker_default, no model_routing event, so shadow samples are unaffected). - Registry: resolve_registry_agent checks skills directly against the loaded definition instead of re-parsing a hand-built dict, so future fields cannot be silently dropped the way tool_approvals was in v9. - Shadow: evaluate_shadow sorts rows earliest-first by created_at (id tiebreak) when timestamps are present, so dedup no longer depends on caller row order; the server path (which strips created_at) is unchanged. - Gateway: when sub2api fills the default model, the worker now checks it against the tier's capability requirements with the same mode semantics as v8 -- shadow only audits, active fails fast with worker_capability_gate_rejected and never calls the runner. No worker-default fallback here: the gateway model is the task's gateway model, falling back to a worker default would run against the wrong gateway.
This commit is contained in:
1 parent
a51919f24b
commit
f9a77577c5
5 files changed
+392
-34
No files matched your search
@@ -156,6 +156,7 @@ from cloud.db import connect as _db_connect
|
||||
from cloud.goal_command import parse_goal_command as _parse_goal_command
|
||||
from cloud.logging_config import get_logger, log_event, setup_logging
|
||||
from cloud.models import ModelRouter, RoutingPolicy, decide_routing, \
|
||||
check_model_capabilities, classify_task, \
|
||||
is_fallbackable, load_routing_policy, parse_routing_policy, \
|
||||
record_fallback, record_routing
|
||||
from cloud.auto_memory import AutoMemoryStore
|
||||
@@ -6019,10 +6020,12 @@ class Worker:
|
||||
# xcmapi 模型名去撞 sub2api)。xcmapi 下 _ensure_gateway
|
||||
# 直接返回 (None, None),不做任何事。注意:这里改的是
|
||||
# 原 task dict,随后 task = dict(task) 会原样拷走。
|
||||
_gateway_filled = False
|
||||
try:
|
||||
gateway_provider, gateway_env = self._ensure_gateway(task)
|
||||
if not task.get("model") and gateway_provider == "sub2api":
|
||||
task["model"] = self._sub2api_default_model(gateway_env)
|
||||
_gateway_filled = True
|
||||
except Exception as exc:
|
||||
error = "网关准备失败: %s" % exc
|
||||
self._set_status(task_id, "failed")
|
||||
@@ -6044,6 +6047,78 @@ class Worker:
|
||||
"stdout": "", "error": error, "events": [],
|
||||
"cost_record_id": None}
|
||||
|
||||
# Audit fix:网关能力检查——此前路由决策只在模型
|
||||
# 为空时跑,sub2api 网关先填默认模型后决策直接跳
|
||||
# 过,能力门在生产路径上完全不生效。补齐:网关填
|
||||
# 的模型同样按档需求检查,档模式与 v8 一致:shadow
|
||||
# 只审计不拦,active 不满足则 fail-fast(同款工具
|
||||
# 门形状,清占位、不调 runner)。没有策略、没有档
|
||||
# 需求、档模式 off 时一律不查,旧行为不变。注意:
|
||||
# 这里没有 worker 默认可回落(网关模型就是该任务
|
||||
# 的网关内模型,换成 worker 默认会跑错网关),所以
|
||||
# active 下不满足即拦,不存在“先试 worker 默认”
|
||||
# 的中间态;运营方须先把网关模型写进能力目录,
|
||||
# 否则 unknown 同样拦(fail-closed,与 v8 一致)。
|
||||
if _gateway_filled and self.routing_policy is not None:
|
||||
_gw_tier = classify_task(task)
|
||||
_gw_requires = self.routing_policy.tier_requirements.get(
|
||||
_gw_tier) or {}
|
||||
if _gw_requires:
|
||||
_gw_mode = self.routing_policy.tier_mode(_gw_tier)
|
||||
if _gw_mode in ("active", "shadow"):
|
||||
_gw_ok, _gw_missing, _gw_unknown = \
|
||||
check_model_capabilities(
|
||||
self.routing_policy.model_capabilities,
|
||||
task.get("model"), _gw_requires)
|
||||
if not _gw_ok:
|
||||
_gw_note = ("gateway model %s %s capability:"
|
||||
" %s" % (
|
||||
task.get("model"),
|
||||
"cannot verify"
|
||||
if _gw_unknown else "lacks",
|
||||
", ".join(_gw_missing)))
|
||||
if _gw_mode == "shadow":
|
||||
self._audit(
|
||||
task["project_id"],
|
||||
"worker_capability_gateway_shadow",
|
||||
"%s tier=%s %s (shadow, not blocked)"
|
||||
% (task_id, _gw_tier, _gw_note))
|
||||
else:
|
||||
_gw_error = (
|
||||
"%s tier gateway model %s; refusing"
|
||||
" to dispatch an incapable or"
|
||||
" unverifiable model"
|
||||
% (_gw_tier, _gw_note))
|
||||
self._set_status(task_id, "failed")
|
||||
self._write_execution_result(
|
||||
task_id, error_text=_gw_error,
|
||||
actual_model=self.model,
|
||||
started_at=started_at,
|
||||
finished_at=time.time(),
|
||||
only_if_status="failed")
|
||||
self._audit(
|
||||
task["project_id"],
|
||||
"worker_capability_gate_rejected",
|
||||
"%s: %s" % (task_id, _gw_error))
|
||||
log_event(
|
||||
self.logger,
|
||||
"worker_capability_gate_rejected",
|
||||
task_id=task_id,
|
||||
project_id=task["project_id"],
|
||||
status="failed")
|
||||
self._running_clear(task_id)
|
||||
return {"dispatched": True,
|
||||
"task_id": task_id,
|
||||
"host_name":
|
||||
self.executor._host_name(task)
|
||||
if hasattr(self.executor,
|
||||
"_host_name")
|
||||
else task_id,
|
||||
"status": "failed",
|
||||
"executor": self._executor_name(),
|
||||
"stdout": "", "error": _gw_error,
|
||||
"events": [], "cost_record_id": None}
|
||||
|
||||
task = dict(task)
|
||||
if not task.get("model"):
|
||||
# 规则路由 v0(只在模型仍为空时跑一次,整个派发只算一次,
|
||||
@@ -6103,6 +6178,19 @@ class Worker:
|
||||
decision.applied,
|
||||
decision.suggested_model or "-",
|
||||
decision.chosen_model or "-"))
|
||||
# Audit fix:能力回落以前不记任何审计(source 是
|
||||
# worker_default,不在 policy/shadow 之列),运营
|
||||
# 方看不到档模型被能力门否掉过。现在用 decision
|
||||
# 上的 capability_fallback 标志单独记一条审计,
|
||||
# 不改 source、不记 model_routing 事件(影子样本
|
||||
# 只算 policy/shadow 决策,回落不是一次路由决策)。
|
||||
if getattr(decision, "capability_fallback", False):
|
||||
self._audit(task["project_id"],
|
||||
"worker_capability_fallback",
|
||||
"%s tier=%s chosen=%s reason=%s" % (
|
||||
task_id, decision.tier,
|
||||
decision.chosen_model or "-",
|
||||
decision.reason))
|
||||
task["model"] = decision.chosen_model or self.model
|
||||
# agent 指令通道(v2):local/docker 由 sandbox 的
|
||||
# --developer-instructions 派发,prompt 保持干净;通用
|
||||
|
||||
Reference in new issue
Block a user