claw-code 源码详细分析:命令宇宙 vs 工具宇宙——`commands` / `tools` 镜像清单如何驱动路由与 shim 执行?

涉及源码src/reference_data/commands_snapshot.jsontools_snapshot.jsonsrc/commands.pysrc/tools.pysrc/execution_registry.pysrc/runtime.pysrc/main.pysrc/models.pyPortingModule)。


1. 「两个宇宙」是什么

在本仓库里,命令宇宙工具宇宙 是两套有穷、可版本化 的镜像清单,都从 JSON 快照加载为内存中的 PortingModule 元组:

  • 命令src/reference_data/commands_snapshot.jsonPORTED_COMMANDScommands.py
  • 工具src/reference_data/tools_snapshot.jsonPORTED_TOOLStools.py

快照里每条记录至少包含 namesource_hintresponsibility (加载时映射进 PortingModulestatus='mirrored'):

python 复制代码
# 23:33:src/commands.py
def load_command_snapshot() -> tuple[PortingModule, ...]:
    raw_entries = json.loads(SNAPSHOT_PATH.read_text())
    return tuple(
        PortingModule(
            name=entry['name'],
            responsibility=entry['responsibility'],
            source_hint=entry['source_hint'],
            status='mirrored',
        )
        for entry in raw_entries
    )
python 复制代码
# 23:34:src/tools.py
def load_tool_snapshot() -> tuple[PortingModule, ...]:
    raw_entries = json.loads(SNAPSHOT_PATH.read_text())
    return tuple(
        PortingModule(
            name=entry['name'],
            responsibility=entry['responsibility'],
            source_hint=entry['source_hint'],
            status='mirrored',
        )
        for entry in raw_entries
    )

产品语义上 (与具体 IDE/Agent 产品对齐时):命令 多对应用户显式 /slash、调色板动作、工作流入口;工具 多对应模型 function-calling / MCP 暴露给模型的可调用面。claw-code 在 路由层用 kind: command | tool 把两宇宙拆开评分与排序,在 执行层 用不同 shim 签名(prompt vs payload)区分。


2. 清单如何「驱动」------三层流水线

可以把数据流画成:快照 → 全量宇宙 →(可选过滤视图)→ 路由 → 注册表 shim → QueryEnginePort 摘要

阶段 命令侧 工具侧
真相源 commands_snapshot.json tools_snapshot.json
全量常量 PORTED_COMMANDS PORTED_TOOLS
过滤视图 get_commands(..., include_plugin_commands, include_skill_commands) get_tools(simple_mode, include_mcp, permission_context)
路由输入 route_prompt全量 PORTED_COMMANDS 同上 PORTED_TOOLS
精确执行 execute_command / MirroredCommand execute_tool / MirroredTool

关键点路由不看 get_tools 的权限过滤 。即使用户 CLI 里 --deny-prefix mcp 缩小了「可见工具列表」,默认 route_prompt 仍会在完整 PORTED_TOOLS 上匹配 (与「模型实际能调用什么」的严格对齐需在上层再收敛,见 result/05.md)。


3. 路由:PortRuntime.route_prompt

3.1 分轨收集 + 公平首发 + 余量排序

python 复制代码
# 90:107:src/runtime.py
    def route_prompt(self, prompt: str, limit: int = 5) -> list[RoutedMatch]:
        tokens = {token.lower() for token in prompt.replace('/', ' ').replace('-', ' ').split() if token}
        by_kind = {
            'command': self._collect_matches(tokens, PORTED_COMMANDS, 'command'),
            'tool': self._collect_matches(tokens, PORTED_TOOLS, 'tool'),
        }

        selected: list[RoutedMatch] = []
        for kind in ('command', 'tool'):
            if by_kind[kind]:
                selected.append(by_kind[kind].pop(0))

        leftovers = sorted(
            [match for matches in by_kind.values() for match in matches],
            key=lambda item: (-item.score, item.kind, item.name),
        )
        selected.extend(leftovers[: max(0, limit - len(selected))])
        return selected[:limit]

工程含义

  1. 分词 :把 /- 替换成空格再 split,得到小写 token 集合(适合从自然语言 prompt 里「捞」到与 name/source_hint 重叠的词)。
  2. 双宇宙并行评分 :对命令、工具各跑一遍 _collect_matches,得到两个已按分数排序的列表。
  3. 各取第一名 :先从 command 榜、tool 榜 各拿一个最高分 (若存在),避免某一宇宙霸占整个 limit
  4. 合并余量 :剩余候选按 分数降序 ,同分按 kindname 稳定排序,填满 limit

3.2 单条评分:名字 + 路径提示 + 职责描述

python 复制代码
# 176:192:src/runtime.py
    def _collect_matches(self, tokens: set[str], modules: tuple[PortingModule, ...], kind: str) -> list[RoutedMatch]:
        matches: list[RoutedMatch] = []
        for module in modules:
            score = self._score(tokens, module)
            if score > 0:
                matches.append(RoutedMatch(kind=kind, name=module.name, source_hint=module.source_hint, score=score))
        matches.sort(key=lambda item: (-item.score, item.name))
        return matches

    @staticmethod
    def _score(tokens: set[str], module: PortingModule) -> int:
        haystacks = [module.name.lower(), module.source_hint.lower(), module.responsibility.lower()]
        score = 0
        for token in tokens:
            if any(token in haystack for haystack in haystacks):
                score += 1
        return score

学习点 :路由是 子串命中计数 ,不是 embedding;source_hint 含文件路径 ,用户说 MCPreview 等词时容易同时命中命令与工具条目,正好检验 双轨公平 逻辑。

RoutedMatch 携带 kind ,后续执行与 QueryEnginePortmatched_commands / matched_tools 分拆都依赖它:

python 复制代码
# 16:21:src/runtime.py
@dataclass(frozen=True)
class RoutedMatch:
    kind: str
    name: str
    source_hint: str
    score: int

4. Shim 执行:ExecutionRegistryexecute_*

4.1 注册表 = 全量清单的薄包装

python 复制代码
# 47:51:src/execution_registry.py
def build_execution_registry() -> ExecutionRegistry:
    return ExecutionRegistry(
        commands=tuple(MirroredCommand(module.name, module.source_hint) for module in PORTED_COMMANDS),
        tools=tuple(MirroredTool(module.name, module.source_hint) for module in PORTED_TOOLS),
    )

registry.command(name) / registry.tool(name) 大小写不敏感 查找,与 get_command / get_tool 一致。

4.2 命令 shim:prompt 字符串

python 复制代码
# 75:80:src/commands.py
def execute_command(name: str, prompt: str = '') -> CommandExecution:
    module = get_command(name)
    if module is None:
        return CommandExecution(name=name, source_hint='', prompt=prompt, handled=False, message=f'Unknown mirrored command: {name}')
    action = f"Mirrored command '{module.name}' from {module.source_hint} would handle prompt {prompt!r}."
    return CommandExecution(name=module.name, source_hint=module.source_hint, prompt=prompt, handled=True, message=action)

4.3 工具 shim:payload 字符串

python 复制代码
# 81:86:src/tools.py
def execute_tool(name: str, payload: str = '') -> ToolExecution:
    module = get_tool(name)
    if module is None:
        return ToolExecution(name=name, source_hint='', payload=payload, handled=False, message=f'Unknown mirrored tool: {name}')
    action = f"Mirrored tool '{module.name}' from {module.source_hint} would handle payload {payload!r}."
    return ToolExecution(name=module.name, source_hint=module.source_hint, payload=payload, handled=True, message=action)

命名差异 是接口层对未来 「命令吃自然语言 / 工具吃 JSON payload」 的预留;当前 bootstrap 里工具也传了用户 prompt 当 payload

python 复制代码
# 119:120:src/runtime.py
        command_execs = tuple(registry.command(match.name).execute(prompt) for match in matches if match.kind == 'command' and registry.command(match.name))
        tool_execs = tuple(registry.tool(match.name).execute(prompt) for match in matches if match.kind == 'tool' and registry.tool(match.name))

即:路由后的统一「用户 utterance」 先灌进两条执行链,真实现时可再分支解析。


5. bootstrap_session:路由 + shim + 引擎一条链

python 复制代码
# 117:133:src/runtime.py
        matches = self.route_prompt(prompt, limit=limit)
        registry = build_execution_registry()
        command_execs = tuple(registry.command(match.name).execute(prompt) for match in matches if match.kind == 'command' and registry.command(match.name))
        tool_execs = tuple(registry.tool(match.name).execute(prompt) for match in matches if match.kind == 'tool' and registry.tool(match.name))
        denials = tuple(self._infer_permission_denials(matches))
        stream_events = tuple(engine.stream_submit_message(
            prompt,
            matched_commands=tuple(match.name for match in matches if match.kind == 'command'),
            matched_tools=tuple(match.name for match in matches if match.kind == 'tool'),
            denied_tools=denials,
        ))
        turn_result = engine.submit_message(
            prompt,
            matched_commands=tuple(match.name for match in matches if match.kind == 'command'),
            matched_tools=tuple(match.name for match in matches if match.kind == 'tool'),
            denied_tools=denials,
        )

顺序matches → 按 kind 跑 shim → 推断 denials → 把 名称元组 (不是路径)交给 QueryEnginePort
报告RuntimeSession.as_markdown() 分别打印 Routed Matches、Command Execution、Tool Execution、Stream Events、Turn Result (见 runtime.pyas_markdown),便于肉眼核对 「路由到了谁 → shim 说了啥 → 引擎摘要里写了啥」


6. CLI:清单自省、路由调试、按名 shim

子命令 作用
commands / tools get_commands / get_tools(可带过滤),列表视图
route <prompt> 直接 route_prompt,打印 kind\tname\tscore\tsource_hint
show-command / show-tool get_command / get_tool 按精确名 看单条条目
exec-command / exec-tool 不经路由,按名执行 shim(失败 exit 1)
python 复制代码
# 142:148:src/main.py
    if args.command == 'route':
        matches = PortRuntime().route_prompt(args.prompt, limit=args.limit)
        if not matches:
            print('No mirrored command/tool matches found.')
            return 0
        for match in matches:
            print(f'{match.kind}\t{match.name}\t{match.score}\t{match.source_hint}')
python 复制代码
# 200:207:src/main.py
    if args.command == 'exec-command':
        result = execute_command(args.name, args.prompt)
        print(result.message)
        return 0 if result.handled else 1
    if args.command == 'exec-tool':
        result = execute_tool(args.name, args.payload)
        print(result.message)
        return 0 if result.handled else 1

测试锚点test_route_and_show_entry_cli_runtest_exec_command_and_tool_cli_runtest_execution_registry_runs 等把 快照非空、路由可命中、shim 有固定子串 锁在 CI 里。


7. 镜像数据的「真实形状」与陷阱

7.1 同名多条目

快照里 同一 name 可对应多条 (例如多个 source_hint 指向不同文件):

json 复制代码
  { "name": "add-dir", "source_hint": "commands/add-dir/add-dir.tsx", ... },
  { "name": "add-dir", "source_hint": "commands/add-dir/index.ts", ... },

get_command / get_tool 线性扫描 ,返回 首个 大小写匹配项:

python 复制代码
# 52:56:src/commands.py
def get_command(name: str) -> PortingModule | None:
    needle = name.lower()
    for module in PORTED_COMMANDS:
        if module.name.lower() == needle:
            return module

后果 :shim 与 show-command 展示的 source_hint 可能只是其中一条;路由里 每条快照行仍是独立 PortingModule ,同名可多次命中、多次出现在不同 RoutedMatch(若分数与 limit 允许)。产品化时常需要 primary_fqnentry_id 消歧。

7.2 工具快照里的「非顶层工具」名

tools_snapshot.json 中会出现 UIagentMemory 等与「用户心智中的工具类名」混在一起的条目,它们来自归档路径镜像。路由 仍按 token 子串匹配------这是 移植期忠实还原表面 ,不是最终 UX;成熟产品会在 清单层 再标 callable: bool分层 tool spec


8. 小结

  • 命令宇宙 / 工具宇宙JSON 快照 → PORTED_* →(可选)get_* 过滤 构成;路由固定吃全量 PORTED_*,与列表过滤解耦。
  • 路由 在两条宇宙上 独立打分、各取榜首、再合并余量 ,输出带 kindRoutedMatch
  • ShimExecutionRegistry 统一按名查找;命令prompt 语义,工具payload 语义,bootstrap 阶段暂用同一用户字符串灌入。
  • CLI 提供 列表 / 路由 / 精确查看 / 精确执行 四条路径,支撑移植期 可测试、可调试 闭环。
相关推荐
心.c2 小时前
嵌入式 AI 助手的三层意图识别架构:如何在“快、准、稳“之间取得平衡
人工智能·ai·架构
AI自动化工坊2 小时前
HiClaw多Agent协同实战:基于Matrix协议的透明化AI团队架构
人工智能·ai·架构·agent·matrix·hiclaw
三万棵雪松2 小时前
【Linux 物联网网关主控系统-Web部分(二)】
linux·前端·物联网
婷婷_1722 小时前
DWC Ethernet QoS VLAN功能实现详解
网络·学习·程序人生·ethernet·芯片·vlan·gmac
一叶之秋14122 小时前
通信之道:解锁Linux进程间通信的无限可能(一)
linux·运维·服务器
测试摆渡媛2 小时前
UDS诊断
网络
Deitymoon2 小时前
linux——线程的概念
linux
郝学胜-神的一滴2 小时前
Pytorch自动微分模块:从原理到实战,解锁反向传播核心奥秘
服务器·人工智能·pytorch·python·深度学习·机器学习
eF06U766F2 小时前
Ubuntu Linux 上 固定P/E 核混合架构CPU频率
linux·ubuntu·架构