
【Bug已解决】create_agent: model_to_tools router can return model but path_map omits it - KeyError(model)一、现象长什么样create_agent支持用一个model_to_tools路由器根据当前模型/上下文决定走哪条模型工具的处理路径。路由器根据输入返回某个路由键route key再用path_map路由键→处理路径的映射找到对应逻辑。问题在于路由器在某些情况下会返回键model但path_map里根本没有model这个键。于是派发时path path_map[route]route model命中不了 →KeyError: modelcreate_agent直接崩。表现大多数情况正常路由到path_map里有的键但一旦输入触发了返回 model这个分支就 100% 崩溃且报错是裸KeyError不提示path_map 缺了 model 这条路由。二、背景create_agent的设计里model_to_tools是一个可调用对象/路由函数签名类似def model_to_tools(state) - str: # 返回路由键 ...调用方再拿返回的键去path_map查处理路径route model_to_tools(state) path path_map[route] # 若 route 不在 path_map - KeyError路由器的输出空间可能返回的键集合和path_map的键集合本应完全一致。但路由器是用户/框架提供的可能返回model比如当判定只需模型、无需工具时而path_map只定义了tools/no_tools等漏了model。两端无单一事实来源约定靠默契于是 KeyError。三、根因根因两点路由输出空间与 path_map 不同步路由器能返回modelpath_map没对应项。派发用裸下标path_map[route]直接取下标键缺失即KeyError无兜底、无清晰错误。本质把路由键集合和路径映射键集合当成两处独立维护的字典缺单一事实来源一端加了路由另一端忘了加映射。四、最小可运行复现下面缩略逻辑复现 KeyErrordef model_to_tools(state): if state.get(need_model_only): return model # 路由器返回了 model return tools path_map {tools: tool_path, no_tools: no_tool_path} # 没有 model route model_to_tools({need_model_only: True}) path path_map[route] # KeyError: model修复path_map 补齐 model且派发用.get 清晰错误。path_map {tools: ..., no_tools: ..., model: model_only_path} route model_to_tools(state) path path_map.get(route) if path is None: raise KeyError(froute {route} not in path_map; keys{list(path_map)})五、解决方案第一层最小直接修复最小修法在create_agent装配时校验路由器可能返回的键都存在于path_map派发用.get并给清晰错误。def create_agent(model_to_tools, path_map, sample_states): # 校验路由器对样例输入返回的键都必须在 path_map for st in sample_states: route model_to_tools(st) if route not in path_map: raise ValueError(frouter returned {route} but path_map lacks it) def dispatch(state): route model_to_tools(state) path path_map.get(route) if path is None: raise KeyError(froute {route} not in path_map; have {list(path_map)}) return path(state) return dispatch这一层让缺映射在装配期就暴露而非运行期裸 KeyError。六、解决方案第二层结构化改进把路由键 ↔ path_map 一致性固化成策略对象作为单一事实来源明确路由输出空间必须 ⊆ path_map 键。from dataclasses import dataclass, field from typing import Callable, Dict, List dataclass(frozenTrue) class LangChainCreateAgentRouterPolicy: create_agent 路由/path_map 一致性策略的单一事实来源。 path_map: Dict[str, Callable] field(default_factorydict) expected_routes: List[str] field(default_factorylist) fail_closed: bool True def validate_router(self, router: Callable, probes) - None: for st in probes: route router(st) if route not in self.path_map: if self.fail_closed: raise AssertionError( frouter returned {route} not in path_map keys {list(self.path_map)}) def dispatch(self, router, state): route router(state) path self.path_map.get(route) if path is None: raise KeyError(froute {route} missing; keys{list(self.path_map)}) return path(state) def validate(self) - None: if self.fail_closed and not self.path_map: raise AssertionError(path_map empty but fail_closed)create_agent用policy.validate_routerpolicy.dispatch一致性集中。七、解决方案第三层断言 / CI 守护用 pytest 锁死一致性import pytest from policy import LangChainCreateAgentRouterPolicy as P def test_router_key_in_path_map(): p P(path_map{tools: lambda s: s, model: lambda s: s}) p.validate_router(lambda s: model, [{x: 1}]) # 不抛 def test_missing_key_rejected(): p P(path_map{tools: lambda s: s}) # 无 model with pytest.raises(AssertionError): p.validate_router(lambda s: model, [{x: 1}]) def test_dispatch_clear_error(): p P(path_map{tools: lambda s: s}) with pytest.raises(KeyError) as e: p.dispatch(lambda s: model, {}) assert model in str(e) def test_policy_valid(): P(path_map{tools: lambda s: s}).validate()CI 加一条用覆盖各路由分支的样例 state 跑validate_router断言所有路由键都在 path_map。八、排查清单create_agent 崩KeyError: model→ 路由器返回 model 但 path_map 缺它。路由输出空间和 path_map 是否同步→ 必须 ⊆用 policy 校验。派发是否裸path_map[route]→ 改用.get 清晰错误。缺映射能否装配期发现→ validate_router 在装配时校验。是否只在某些输入才崩→ 路由到 model 分支才触发需样例覆盖。是否有路由键⊆path_map测试→ 必须有。九、小结create_agent的model_to_tools路由器能返回键model但path_map漏了它派发path_map[route]直接KeyError(model)。根因是路由输出空间与 path_map 键集合无单一事实来源、不同步。第一层装配期校验路由键都在 path_map、派发用.get给清晰错误第二层用LangChainCreateAgentRouterPolicy把一致性固化成单一事实来源第三层用 pytest 守护。路由派发的通用原则路由器的输出空间必须是 path_map 键集合的子集且派发绝不用裸下标缺键要给可读错误。