跳转至

程序化工具调用 (PTC)

PTC 让 LLM 编写 Python 代码,在一个代码块中编排多个工具调用,减少往返次数和 token 消耗。

快速开始

from toolregistry import ToolRegistry

registry = ToolRegistry()
registry.register(search)
registry.register(summarize)
registry.enable_code_execution()  # 注册 "code_execution" 工具

# LLM 现在可以生成 tool_use("code_execution", {code: "..."})

工作原理

LLM: tool_use("code_execution", {code: "..."})
  → CodeExecutionTool.execute(code)
    → 子进程: exec(code, {search: stub, summarize: stub})
      → search(query="weather")
        → IPC → 主进程 → registry.invoke("search", {...})
        → 结果通过 IPC 返回
      → summarize(data)
        → IPC → 主进程 → registry.invoke("summarize", {...})
        → 结果通过 IPC 返回
      → print(final_output)
    → 返回 stdout 给 LLM

关键点:

  • 代码在隔离子进程中运行——崩溃不会影响主进程
  • 工具调用通过 registry.invoke() 执行——权限和日志均被强制执行
  • 只有 print() 输出返回给 LLM——中间结果保留在变量中
  • AST 验证阻止危险代码(文件 I/O、网络、不安全的导入)

示例:多工具编排

不使用 PTC(3 次往返):

Turn 1: LLM → tool_use("search", {query: "..."})     → result
Turn 2: LLM → tool_use("filter", {data: result, ...}) → filtered
Turn 3: LLM → tool_use("summarize", {data: filtered}) → summary

使用 PTC(1 次往返):

# LLM 生成此代码:
data = search(query="climate change")
filtered = [item for item in data if item["year"] >= 2024]
summary = summarize(data=filtered)
print(f"找到 {len(filtered)} 篇近期文章。\n{summary}")

安全模型

层级 保护
AST 验证 阻止 import osopen()eval()subprocess、网络访问等
子进程隔离 代码在新进程中运行——段错误、OOM、死循环被隔离
权限强制 工具调用通过 registry.invoke() 并执行完整权限检查
命名空间限制 只有已注册的工具可用——无法访问 registry 内部

调用追踪

每次 PTC 执行生成一个 tr_ptc_ 调用 ID,该执行中的所有工具调用共享此 ID:

registry.enable_logging()
registry.enable_code_execution()

tool = registry.get_tool("code_execution")
tool.run({"code": "print(add(a=1, b=2))"})

# 获取调用 ID
executor = registry._code_execution
inv_id = executor.last_invocation_id  # "tr_ptc_a1b2c3d4"

# 查询此次执行的所有工具调用
log = registry.get_execution_log()
entries = log.get_entries(invocation_id=inv_id)

配置

# 自定义超时(默认:30 秒)
registry.enable_code_execution(timeout=60)

# 不需要时禁用
registry.disable_code_execution()

依赖

PTC 需要 codecell 包:

pip install toolregistry[ptc]

PTC 的限制

  • 无法调用未注册的工具 — 只有命名空间注入的工具可用
  • 无法在执行间保持状态 — 每次 execute() 在新子进程中运行
  • 无法直接访问文件或网络 — 所有 I/O 必须通过已注册的工具
  • 无法导入任意 Python 包 — 只允许安全的计算模块