本页目录
Advanced / 高级能力Note 78

01 Interpreter 与 PTC

Interpreter 是 Deep Agents 的一个轻量代码执行层:模型不只发普通工具调用,还可以写一段 JavaScript 让运行时执行。PTC 是 Programmatic Tool Calling,它把允许的工具暴露到 Interpreter 的 tools 命名空间里,让代码循环、分支、并行调用工具。它

Interpreter 是 Deep Agents 的一个轻量代码执行层:模型不只发普通工具调用,还可以写一段 JavaScript 让运行时执行。PTC 是 Programmatic Tool Calling,它把允许的工具暴露到 Interpreter 的 tools 命名空间里,让代码循环、分支、并行调用工具。它解决的问题是:一批中间工具结果不必每一步都回到模型上下文,最后只把聚合结果交回模型。

最小代码

本章代码在:

deepagent_src/advanced_teach/01_interpreters_ptc.py

核心配置只有两处:

from langchain_quickjs import CodeInterpreterMiddleware

agent = create_deep_agent(
    model=get_gpt_model(disable_tool_streaming=True),
    tools=[lookup_plan, discount_amount],
    middleware=[CodeInterpreterMiddleware(ptc=["lookup_plan", "discount_amount"])],
    subagents=[],
)

这里 lookup_plandiscount_amount 仍然是普通 LangChain tool。区别在于:加了 CodeInterpreterMiddleware(ptc=[...]) 后,模型可以在 eval 里写 JavaScript,并通过 tools.lookupPlan(...)tools.discountAmount(...) 调这些工具。

运行命令

uv run python -m deepagent_src.advanced_teach.01_interpreters_ptc

预期现象:

  1. 会发生一次真实 LLM 调用。轨迹里应该能看到模型调用 eval,而不是直接连续调用 lookup_plan / discount_amount
  2. eval 里的 JavaScript 会通过 tools.lookupPlan(...)tools.discountAmount(...) 批量处理三个套餐。
  3. 最终输出会包含 starterteamenterprise 三个套餐的原价和 20% 折后价。

常见误区

最容易搞混的是把 Interpreter 当成 sandbox。Interpreter 默认没有文件系统、shell、网络、包管理器和系统时间;它只是 QuickJS 内存运行时。需要跑命令、装依赖、改真实文件时,用 sandbox;需要循环、分支、聚合中间工具结果时,用 Interpreter/PTC。

还有一个安全边界要记住:官方文档说明 PTC 通过 interpreter bridge 执行,当前不会走普通 tool calling 的逐工具 interrupt_on 审批流程。所以能进 PTC allowlist 的工具必须提前收窄,别把危险工具一股脑放进去,艹,这玩意放错就是自己给自己挖坑。

与普通工具调用的区别

普通 tool calling 的节奏是:模型决定调用哪些工具,工具结果回到模型,模型再决定下一步。PTC 的节奏是:模型先写一段代码,代码在 Interpreter 里批量调用工具、过滤和聚合结果,模型只看到最终返回。

所以本章判断是否跑通,不看工具函数能不能被 Python 直接调用,而看真实 Agent 轨迹里有没有 eval 工具调用。

版本说明

官方文档要求:

langchain-quickjs>=0.2.0
Python >=3.11

本项目已经确认升级到 deepagents[quickjs]==0.7.0b2uv.lock 中解析到 langchain-quickjs==0.3.4。示例仍保留依赖门控,这样换到新环境时不会用一个晦涩的 ModuleNotFoundError 把人整懵。

升级后实测关键签名:

deepagents 0.7.0b2
CodeInterpreterMiddleware(..., ptc: list[str | BaseTool] | None = None, mode: "thread" | "turn" | "call" | None = None, ...)
FilesystemMiddleware(..., tools: list["ls" | "read_file" | "write_file" | "edit_file" | "delete" | "glob" | "grep" | "execute"] | "all" | None = None, ...)

相关资源

  • 查看示例代码:deepagent_src/advanced_teach/01_interpreters_ptc.py
    from __future__ import annotations
    
    import os
    from importlib.util import find_spec
    from typing import Any
    
    from deepagents import create_deep_agent
    from langchain.messages import HumanMessage
    from langchain_core.tools import tool
    
    from deepagent_src.llms import get_gpt_model
    
    
    PRODUCTS: dict[str, dict[str, Any]] = {
        "starter": {"price": 19, "seats": 3},
        "team": {"price": 79, "seats": 12},
        "enterprise": {"price": 249, "seats": 80},
    }
    
    
    @tool
    def lookup_plan(plan: str) -> dict[str, Any]:
        """Look up a subscription plan by name."""
        key = plan.strip().lower()
        if key not in PRODUCTS:
            return {"plan": plan, "found": False}
        return {"plan": key, "found": True, **PRODUCTS[key]}
    
    
    @tool
    def discount_amount(price: int, percent: int) -> dict[str, int]:
        """Calculate the final price after a percentage discount."""
        return {"price": price, "percent": percent, "final": round(price * (100 - percent) / 100)}
    
    
    def require_quickjs() -> None:
        if find_spec("langchain_quickjs") is not None:
            return
        raise SystemExit(
            "缺少 langchain_quickjs,当前项目还不能运行 Deep Agents Interpreter/PTC。\n"
            "安装命令:uv add \"deepagents[quickjs]\"\n"
            "注意:这是包管理操作,执行前要确认依赖变更。"
        )
    
    
    def tool_call_names(messages: list[Any]) -> list[str]:
        names: list[str] = []
        for message in messages:
            for call in getattr(message, "tool_calls", None) or []:
                names.append(call.get("name", ""))
        return names
    
    
    def main() -> None:
        os.environ["LANGSMITH_TRACING"] = "false"
        os.environ["LANGCHAIN_TRACING_V2"] = "false"
        require_quickjs()
    
        from langchain_quickjs import CodeInterpreterMiddleware
    
        agent = create_deep_agent(
            model=get_gpt_model(disable_tool_streaming=True),
            tools=[lookup_plan, discount_amount],
            middleware=[CodeInterpreterMiddleware(ptc=["lookup_plan", "discount_amount"])],
            subagents=[],
            system_prompt=(
                "你是 Interpreter/PTC 教学助手。必须用 eval 写 JavaScript,"
                "在 eval 里通过 tools.lookupPlan 和 tools.discountAmount 查询 starter、team、enterprise,"
                "计算每个套餐打 20% 折后的价格,只返回一张简短中文表。"
            ),
        )
    
        state = agent.invoke(
            {
                "messages": [
                    HumanMessage(
                        content=(
                            "请用 Interpreter 的 PTC 一次性处理 starter、team、enterprise 三个套餐,"
                            "查询原价并计算 20% 折后价。"
                        )
                    )
                ]
            }
        )
    
        for message in state["messages"]:
            message.pretty_print()
    
        names = tool_call_names(state["messages"])
        assert "eval" in names, names
        final_text = state["messages"][-1].text
        assert "starter" in final_text.lower()
        assert "team" in final_text.lower()
        assert "enterprise" in final_text.lower()
        print("interpreter ptc real call ok")
    
    
    if __name__ == "__main__":
        main()