ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

【Bug已解决】Bug: create_agent: model_to_tools router can return “model“ but path_map omits it -> KeyErr…

【Bug已解决】Bug: create_agent: model_to_tools router can return “model“ but path_map omits it -> KeyErr… 【Bug已解决】Bug: create_agent: model_to_tools router can return model but path_map omits it - KeyError(model)一、现象长什么样这是上一篇893同一故障的另一视角893 站在路由键与 path_map 不同步的设计角度本文站在派发代码如何安全处理未知路由键的角度。两者都因model_to_tools路由器返回model而path_map无此项但本文聚焦KeyError 的传播与防御式派发给出更稳健的 dispatch 实现避免任何未知键直接崩。具体痛点派发代码handler path_map[route]对所有未命中键一律KeyError包括model这种合法但漏配的键错误信息只说model不含 path_map 现有键排查要从两端对质。没有未知键兜底路径理想情况下遇到未配置路由应走一个默认/降级处理而非整个 agent 崩溃。当model_to_tools由用户自定义、可能返回任意字符串时这种裸下标尤其脆弱。二、背景create_agent的 dispatch 是路由器输出 → 处理路径的查表。健壮的查表应当具备清晰错误缺键时告知哪个键缺、现有哪些键。兜底路径可配置默认处理default route未知键走默认而非崩溃。装配期校验启动时就确认路由器可能返回的键都被覆盖。上一篇解决了装配期校验 清晰错误本文补上防御式 dispatch 默认路由让即便漏配也不会炸而是优雅降级。三、根因根因是派发缺乏防御与兜底裸下标无兜底path_map[route]对任何未知键崩。无默认路由未知键没有去处只能异常。错误不可读KeyError(model)不含上下文。本质把查表当成必须命中忽略了外部路由器可能返回预期外键需要防御式处理。四、最小可运行复现下面演示裸下标 vs 防御式派发# 脆弱裸下标 def dispatch_vuln(router, path_map, state): return path_map[router(state)] # KeyError(model) # 防御式默认路由 清晰错误 def dispatch_safe(router, path_map, state, default_keydefault): route router(state) if route in path_map: return path_map[route](state) if default_key in path_map: return path_map[default_key](state) # 降级 raise KeyError(froute {route} not in path_map; keys{list(path_map)})五、解决方案第一层最小直接修复最小修法派发用.get 默认路由兜底 含上下文的错误。def create_dispatch(router, path_map, default_keydefault): def dispatch(state): route router(state) handler path_map.get(route) if handler is not None: return handler(state) if default_key in path_map: return path_map[default_key](state) # 未知键走默认 raise KeyError( frouter returned {route} not in path_map; favailable{list(path_map)}, no default {default_key} ) return dispatch这一层让未知键要么降级、要么给出可读错误不再裸崩。六、解决方案第二层结构化改进把防御式派发策略固化成策略对象作为单一事实来源明确默认路由、错误格式、校验。from dataclasses import dataclass, field from typing import Callable, Dict, Optional dataclass(frozenTrue) class LangChainCreateAgentRouterKeyPolicy: create_agent 防御式派发策略的单一事实来源。 path_map: Dict[str, Callable] field(default_factorydict) default_key: Optional[str] default require_explicit_default: bool True def dispatch(self, router, state): route router(state) handler self.path_map.get(route) if handler is not None: return handler(state) if self.default_key and self.default_key in self.path_map: return self.path_map[self.default_key](state) raise KeyError( froute {route} missing; keys{list(self.path_map)}; fdefault{self.default_key} ) def validate(self) - None: if self.require_explicit_default and self.default_key not in self.path_map: raise AssertionError(default route must exist in path_map) if self.default_key is not None and self.default_key not in self.path_map: raise AssertionError(default_key referenced but absent)派发统一走policy.dispatch防御逻辑集中、可测。七、解决方案第三层断言 / CI 守护用 pytest 锁死防御式派发import pytest from policy import LangChainCreateAgentRouterKeyPolicy as P def test_known_route(): p P(path_map{tools: lambda s: T, default: lambda s: D}) assert p.dispatch(lambda s: tools, {}) T def test_unknown_goes_default(): p P(path_map{tools: lambda s: T, default: lambda s: D}) assert p.dispatch(lambda s: model, {}) D # 降级而非崩 def test_no_default_raises_clear(): p P(path_map{tools: lambda s: T}, default_keyNone) with pytest.raises(KeyError) as e: p.dispatch(lambda s: model, {}) assert model in str(e) and keys in str(e) def test_default_must_exist(): with pytest.raises(AssertionError): P(path_map{tools: lambda s: T}, default_keymissing).validate()CI 加一条用会返回model的路由器跑 dispatch断言走默认路由而非KeyError。八、排查清单派发裸KeyError(model)无上下文→ 用.get 含键列表的错误。未知键能否降级而非崩→ 配默认路由 default。默认路由是否必须存在→ require_explicit_default 强制。路由器可能返回任意键→ 防御式派发必须兜底。与 893 的关系→ 893 管装配校验本文管运行期防御。是否有未知键走默认测试→ 必须有。九、小结create_agent派发用path_map[route]裸下标遇到路由器返回的modelpath_map 漏配直接KeyError且无上下文、无兜底。本文与 893 互补——893 管装配期路由/映射一致性校验本文管运行期防御式派发。第一层用.get 默认路由兜底 清晰错误第二层用LangChainCreateAgentRouterKeyPolicy把派发策略固化成单一事实来源第三层用 pytest 守护未知键走默认。路由派发的通用原则查表必须防御式——未知键要么降级到默认路由要么抛出含现有键的可读错误绝不裸崩。
返回列表