id: tool-claude-code-native
module: A
week: 2
title: "工具调用范式③:claude-code 原生车道(把工具调用外包给运行时)"

concept:
  textbook: |
    第三种范式不在平台内实现工具循环,而是把整段任务**外包**给一个已经内建了
    工具执行能力的智能体运行时(这里是 claude-code CLI)。平台只负责:起进程、
    喂 prompt、把运行时吐出的事件流翻译成自己的 SSE,以及划定它能动的目录与工具集。

    这对应工程上"何时自己写 agent loop、何时复用现成 agent"的取舍:当任务是
    "在一个文件夹里自由读写、跑命令"的单体助手时,直接调一次成熟运行时,往往优于
    把它套进自家 react-over-text 封装(那是给"必须派活给子智能体"的 orchestrator 设计的)。
  key_point: >
    单体工作区助手套进 react-over-text 全是成本没收益(实测 76 调用 0 执行);
    正解是开第二条车道:直接原生跑 claude-code,native 工具开、流式事件映射回平台 SSE。

code:
  - file: lambdagent/src/lambdagent/providers/claude_code_native.py
    symbol: run_native
    lines: "127-210"
    note: >
      claude -p --output-format stream-json --add-dir <文件夹>
      --allowedTools Read/Write/Edit/Bash/... --strict-mcp-config
      --permission-mode bypassPermissions;解析 stream-json 事件映射成现有 SSE。
  - file: lambdagent/src/lambdagent/providers/claude_code_native.py
    symbol: pending_tools (idle 看门狗)
    lines: "44-110"
    note: >
      坑:长命令(实验/pdflatex)期间 stream-json 在工具返回前完全静默,会被 idle
      看门狗误杀。修法:跟踪 pending_tools(tool_use+1 / tool_result-1),idle 仅在
      pending_tools==0 时生效,在飞工具靠 hard_timeout 兜底。

formal:
  term: "外包算子 RunNative(cwd, allowedTools) : Task → (Effects, Result)"
  effect: "IO[fs, shell] ⊗ Cap(allowedTools, cwd)"
  note: >
    与①②不同,这里的 effect 不是平台逐步推断的,而是把一整束能力(读写文件、跑 shell)
    **以能力集 Cap 的形式**授予外部运行时,并用 cwd + allowedTools 划界。形式化关注点
    从"每步 effect"转为"授权边界是否正确"——这正好引出 week 8 沙箱/能力的讨论。

live_demo:
  run: ""
  highlight: >
    对照实验同样进对比台。已 live 验证:真起 claude -p 在临时目录写出文件,
    产出 tool_call / tool_result 事件,cost/session 正确。注意路由按 provider 分流——
    切到 qwen/ollama 必须退回 react 车道(claude -p 只认 sonnet/opus/haiku)。

related:
  - tool-react-over-text
  - tool-native-fc
  - sandbox-effect-cap

takeaway: |
  三范式并排,学生应能在"自己写 loop 的精细控制"与"外包给成熟运行时的省心"之间
  做权衡判断,并说清:范式③把 effect 推断换成了能力授权边界(Cap),
  可靠性问题(静默/卡死/路由)随之从"解析正确性"转移到"进程与授权管理"。
