ESSAY
PTC:Agent架构中最值得学但最被忽视的设计模式
“每次看到一个不相关领域的优秀项目,你都有一个选择:滑走,或者停下来拆解它的架构。“
核心观点 / 起源
最近在HN上看到一个叫LangAlpha1的项目被推到前面,标题是”Claude Code for Wall Street”。一个面向金融研究的AI Agent平台,功能包括DCF模型、财报分析、投资报告生成——全是金融领域的垂直场景。
如果你不做金融,很容易滑走。但我建议你停下来看看,不是因为它的领域知识,而是因为它内部藏着一个主流Agent框架都没解决好的问题——并且给出了一个优雅的答案。
这个答案叫Programmatic Tool Calling,简称PTC。它不是多Agent编排,不是记忆系统,不是中间件——这些LangGraph、CrewAI、OpenAI SDK都有。PTC是LangAlpha最有价值的架构创新,而且我在目前的主流框架里没看到同级别的实现。
细节展开
Agent上下文窗口的诅咒
先回想一个常见场景。
你写了一个Agent,给它配了一个数据查询工具。用户问”帮我分析过去一年每个月的销售趋势”,Agent调用数据库工具,返回了1000行记录。每行50 tokens,一共50k tokens——还没开始分析,上下文已经塞进去半本书。
然后Agent调用第二个工具做聚合,又返回50k tokens。等它终于开始写分析报告时,上下文里已经堆了150k tokens的各种原始数据,模型在噪音中找信号,中间结果和最终推理混在一起,Token成本越来越高,输出质量反而下降。
这不是金融特有的问题。任何涉及数据处理的Agent都面临这个诅咒:数据越多,上下文越胀,模型越笨。
传统的AI Agent工作流是这么走的:
Model → tool_A → Model → tool_B → Model → tool_C → Model
每一步,工具返回的原始数据都经过模型推理,塞回上下文窗口。到第5步的时候,上下文里的原始数据可能已经占了90%的Token,留给模型思考的空间不到10%。
LangChain Deep Agents的研究把这个问题总结为”三个上下文面”(Three Context Surfaces):
| 上下文面 | 作用 | 特性 |
|---|---|---|
| Message history | 模型推理的直接对象 | 昂贵,注意力受限 |
| Filesystem | 持久化工件和长期工作记忆 | 廉价,容量大 |
| Interpreter state | 工作值(数组、对象、计数器) | 在eval间持久,不进模型 |
传统Tool Loop的问题在于:把所有数据都塞进Message history,把最昂贵的上下文面当存储用。
PTC做的事情很简单:把数据处理从Message history移到Filesystem和Interpreter state。
PTC的核心思想:把数据留在外面
一句话定义: Agent不在LLM上下文中处理原始数据,而是在沙箱里写代码执行数据处理,只把结果摘要返回给LLM。
两种模式的对比:
传统模式: Model → tool_A → Model → tool_B → Model → tool_C → Model
PTC模式: Model → code(tool_A, tool_B, tool_C) → Model
在PTC模式下,模型”一步到位”:它先生成一段Python代码,这段代码串起所有工具调用,在沙箱里依次执行。只有最终的print()结果回到LLM上下文——中间产生的所有数据,包括原始数据、中间变量、过滤结果、聚合统计,全部留在沙箱里。
这不是”让模型写代码”这么简单。这是对Agent-工具交互方式的范式转换:
- 调用主体变了:不是LLM逐个”请求”工具,而是代码在沙箱内”操作”工具
- 数据流向变了:原始数据不经过LLM的上下文窗口,只进沙箱内存
- 上下文角色变了:上下文不再是数据仓库,而是推理工作台——只放它需要推理的信息
为什么比Tool Loop更可靠?这是一个反直觉但被实验验证的事实:让模型写代码处理数据,比让模型直接推理数据更可靠。
原因在于数字处理是LLM的弱项。你让模型”从1000条记录里找出异常值”,它可能会凭”感觉”挑几条,而非精确计算。但你让模型”写一段Python代码用3-sigma方法找出异常值”,代码在沙箱里执行的结果是确定的。
AWS Bedrock团队在2025-2026年的系统测试中发现:在非PTC模式下,8个主流模型中只有Claude能完成任务;而在PTC模式下,所有8个模型全部正确。PTC是一个模型无关的架构模式——它不依赖模型本身的推理能力,而是把推理负载从模型转移到代码。
过程 / 推演
LangAlpha的PTC实现:案例解剖
LangAlpha的PTC实现分为四个层次:
1. MCP → Python API 自动转换
这是整个PTC架构的起点。大多数Agent框架的做法是把MCP Server的工具schema丢给LLM,让LLM在推理过程中决定调哪个工具、传什么参数。LangAlpha走了完全不同的路:
当工作区初始化时,每个MCP Server被自动翻译成一个Python模块,放在沙箱的tools/目录下。翻译后的模块包含完整的类型签名、docstring和函数定义。LLM的系统提示里只写一句话:
你有一个
tools/fundamentals模块,里面有get_financial_statements()函数,支持传入ticker和period参数。完整文档在沙箱的tools/fundamentals.md。
Agent需要用到时,写一句:
from tools.fundamentals import get_financial_statements
import pandas as pd
data = get_financial_statements("AAPL", period="5y")
result = data.groupby("year").eps.mean()
print(result)
这个”MCP→Python”自动转换层带来的Token节省是巨大的。工具schema不再占用上下文——一个MCP Server可能有几十个函数、每个函数有复杂的参数结构,这些全部留在沙箱的.py文件里,LLM只需要记住一行import语句。
2. 渐进式工具发现
传统做法是一次性加载所有工具的schema到上下文,即使Agent根本用不到。LangAlpha的实现是懒加载的:Agent在首次调用一个工具之前,才去读取它的markdown文档。这进一步降低了首轮对话的Token消耗,而且更符合真实工作模式——人类开发者也是用到什么库才查什么文档。
3. 沙箱执行引擎
LangAlpha使用Daytona沙箱(也可用本地Docker)作为代码执行环境。Daytona的核心能力是一个90ms启动的容器预热池:
container_pool = {
'python': [Container1, Container2, ...],
}
sandbox = container_pool['python'].pop()
执行流程:
- Agent生成包含
print()的Python代码 - 框架提取代码块,写入沙箱文件系统
- 沙箱内执行(默认配置:无网络、只读文件系统、非root用户、CPU/内存限制、30s超时)
- stdout/stderr捕获后返回给Agent
- 只有
print()输出的摘要回到LLM上下文
如果执行超时或有语法错误,Agent会收到错误信息并自行修复代码重试。这个”自动修复”环节本身就是LLM擅长的——比让LLM直接从原始数据里推理可靠得多。
4. 配合工作区的持久化
PTC不是孤立运行的。LangAlpha把每次沙箱执行视为一次研究会话(session),中间产物自动写入工作区:
agent.md — 跨会话的研究记忆
work/<task_name>/ — 本次会话的工作区
data/ — 原始数据和中间结果
charts/ — 可视化输出
results/ — 最终报告
这意味着Agent周一开始的研究,周二可以无缝续上。agent.md记录了目标、进展、关键发现、文件索引,下一次会话自动注入。
一点补充
PTC的效果数据
数据最有说服力。以下来自AWS Bedrock团队在2025-2026年的系统测试,涵盖8个模型、跨多个数据密集型任务。
| 模型 | PTC Token | 非PTC Token | 减少率 |
|---|---|---|---|
| DeepSeek v3.2 | 19,543 | 245,967 | 92.1% |
| Kimi 2.5 | 10,875 | 148,085 | 92.7% |
| Claude Sonnet 4.6 | 12,739 | 128,043 | 90.1% |
| Claude Opus 4.6 | 13,043 | 126,152 | 89.7% |
| Qwen3-Coder-480B | 34,159 | 305,114 | 88.8% |
| Qwen3-Next-80B | 28,878 | 233,332 | 87.6% |
| MiniMax M2.1 | 11,787 | 101,990 | 88.4% |
| GLM 4.7 | 11,550 | 115,829 | 90.0% |
平均Token减少率约90%。这个数字不是实验室里的极端值,而是跨多种任务类型的平均值。也就是说,你把输入Token的预算乘10,用PTC就能做到同等效果。
正确率方面:非PTC模式下8个模型中只有Claude能正确完成任务;PTC模式下所有8个模型全部正确。这意味着PTC抹平了模型之间的能力差距——一个弱模型加上PTC,可能在特定数据处理任务上超过一个强模型不加PTC。
成本方面:按每日1000次查询估算,非PTC约$15,600/月,PTC约$1,560/月,约10倍差距。
PTC的实现方案取舍
LangAlpha不是唯一的PTC实现,2025-2026年出现了多个方案:
| 方案 | 沙箱方式 | 启动速度 | 隔离级别 | 适用场景 |
|---|---|---|---|---|
| LangAlpha | Daytona/Docker | ~90ms | 容器级 | 长期研究,需持久化工作区 |
| AWS Bedrock PTC | ECS Docker + IPC | ~秒级 | 容器级 | AWS生态,模型无关 |
| Deep Agents Interpreter | QuickJS | ~毫秒级 | 语言级 | 轻量JS计算 |
| Anthropic官方 | 托管沙箱 | 托管 | 托管 | Claude专用 |
| open-ptc-agent | Daytona | ~90ms | 容器级 | 开源,可自托管 |
选择的核心取舍点有三个:
- 隔离级别:Docker/容器级隔离提供完整进程隔离和网络限制;QuickJS轻量但不够安全
- 持久化需求:超过一次会话的任务需要工作区持久化;独立会话用轻量方案即可
- 运维成本:托管方案减少运维负担但绑定平台;自建方案灵活但需维护
PTC不适合的场景:简单单次查询(开销>收益)、Agent需要对中间结果做语义推理、沙箱启动成本高于数据处理本身。金标准:如果你的Agent调一个工具返回100k tokens,PTC就该上;如果只处理200 tokens,PTC是过度设计。
结语 / 反思
如果你已经被说服,下面是一条经过验证的落地路径,不需要一步到位。
Phase 0:手动验证(1天)——手动写Python脚本处理数据,只传摘要给LLM,感受Token节省量和输出质量的变化。
Phase 1:容器沙箱(1-2周)——引入Docker沙箱让Agent自动写代码执行。关键配置:无网络、只读文件系统、非root用户、内存/CPU限制、超时机制。
Phase 2:MCP→Python自动转换(1-2周)——用MCP SDK的list_tools()接口自动生成Python模块,或手写封装模块。
Phase 3:持久化工作区(持续优化)——如果Agent涉及跨会话研究,引入工作区文件系统(agent.md + work/ + results/)。
不是每个Agent都需要PTC。但如果你做处理数据的Agent,你应该试过PTC再决定不用它。
每次看到一个”不相关”领域的优秀项目,你都有一个选择:滑走,或者停下来拆解它的架构。LangAlpha被贴上”金融”的标签,这也许是一个损失——因为最值得学的PTC是模型无关、领域无关的设计模式。它解决的是每个Agent开发者都会遇到、但很多人没意识到可以解决的问题:不是所有数据都需要进上下文。
下次你写一个需要查数据再分析的Agent,问自己一个简单的问题:这些数据真的需要进LLM的上下文吗?
答案通常是不需要。
Footnotes
-
LangAlpha 仓库:https://github.com/ginlix-ai/langalpha。PTC相关研究:AWS Bedrock PTC实现、LangChain Deep Agents、open-ptc-agent。 ↩