MCP-PIF(注:这是一个专业术语或特定缩写,根据上下文可能有不同的具体含义,但在此直接翻译为中文保持原样)
一个支持元循环评估的原生JSON Lambda演算运行时,设计为MCP(模型上下文协议)服务器。它使语言模型能够通过元编程动态演化工具。
快速入门
# Build the project
cabal build
# Enable debug mode for detailed evaluation tracing
MCP_DEBUG=1 cabal run mcp-pif
# Debug output (to stderr) shows:
# - Each evaluation step
# - Environment keys at each step
# - Closure creation and application
# - Tool code lookups基本示例
// Create a tool
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "evolve",
"arguments": {
"name": "square",
"description": "Squares a number",
"code": {"lam": "x", "body": {"mul": [{"var": "x"}, {"var": "x"}]}}
}
}
}
// Use the tool
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "run",
"arguments": {
"tool": "square",
"input": 7
}
}
}
// Returns: 49
// Get help
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "help",
"arguments": {"category": "lists"}
}
}
// Returns: Documentation for list primitives核心概念
问题
对于纯粹的计算语言模型来说,需要计算工具,但现有的系统要么提供固定的API,要么允许无限制的代码执行。在元编程和自修改的背景下,这两种方式都不理想。本程序提出了一种折中结构,以实现安全、可检查、可演化的计算。
从理想意义上讲,MCP-PIF可以被视为一种通用的、可变形的计算机接口,其中语言模型作为执行函数被集成进去。在这里,我们提供了一组简单的λ演算原语词汇,仅用于访问纯计算功能。
解决方案
MCP-PIF 提供了一种带有三个元编程原语的 λ 演算:
quote- 将代码视为数据(防止求值)eval- 动态执行带引号的代码code_of- 反思任何工具的源代码
这创造了一个 元循环的 这是一个系统,其中工具能够分析并转换其他工具,同时保持确定性和燃料(资源)受限的执行。它具有同像性,但受到限制。
值得注意的是 quote 和 eval 并不完全对称:
-- eval cleans the environment:
let cleanEnv = M.filterWithKey (\k _ -> not $ k `elem` ["__tool_name", "__self"]) env这防止了通过 eval 执行的代码继承错误的工具上下文。当 eval 执行引用的代码:
- 用户变量会被保留 (保持词法作用域)
- 系统变量已清除 (
__tool_name和__self(已移除以避免工具上下文混淆) - 工具代码仍然可用 (for
code_of内省 __eval_depth计数器已添加 (最大深度:100,防止无限评估循环)
事件视界
由于这个框架是基于MCP构建的,因此在纯粹性上做出了重大妥协。具体来说,更新服务器代码并不是元编程循环的一部分。这意味着,基本元素列表、解析策略以及进化和评估过程本身在运行时是不可修改的。
有两种工具:
- MCP工具例如,通过……创建
evolve(有副作用,会修改注册表) - 进化的工具通过工具执行
run(纯粹的,功能性的)
进化后的工具可以相互交互,但在无法访问协议级工具注册表的情况下,它们自身无法进化出新的工具。这种“拟像”实现方式通过隔离那些使丰富元编程成为可能的不变性,防止了无限制的自我修改。
语言参考
核心基本元素
| 类别 | 原始类型 | JSON 语法 | 描述 |
|---|---|---|---|
| Lambda | 变量 | {"var": "x"} | 变量引用 |
| 功能 | {"lam": "x", "body": ...} | Lambda 抽象 | |
| 应用 | {"app": {"func": ..., "arg": ...}} | 函数应用 | |
| 算术 | 添加 | {"add": [a, b]} | 加法 |
| 减去 | {"sub": [a, b]} | 减法 | |
| 乘以 | {"mul": [a, b]} | 乘法 | |
| 分割 | {"div": [a, b]} | 除法(对0的错误) | |
| 模块 | {"mod": [a, b]} | 模块(0时的错误) | |
| 比较 | 等于 | {"eq": [a, b]} | 等值测试 |
| 少于 | {"lt": [a, b]} | 少于 | |
| 小于或等于 | {"lte": [a, b]} 小于或等于 | ||
| 大于 | {"gt": [a, b]} 大于 | ||
| 大于或等于 | {"gte": [a, b]} 大于或等于 | ||
| 逻辑 | 和 | {"and": [a, b]} 逻辑与(短路) | |
| 或者 | {"or": [a, b]} | 逻辑或(短路) | |
| 否 | {"not": a} | 逻辑非 | |
| 控制 | 如果 | {"if": {"cond": c, "then": t, "else": e}} | 条件 |
| 继续 | {"continue": {"input": x}} | 递归步骤 | |
| 列表 | 无 | {"nil": true} | 空列表 |
| 缺点 | {"cons": {"head": ..., "tail": ...}} | 列表构建 | |
| 折叠 | {"fold": [func, init, list]} | 通用减速器(配对参数详情请参见GUIDE.md文件) | |
| 成对(或配对) | 配对 | {"pair": [a, b]} | 配对构造 |
| 第一 | {"fst": pair} | 获取第一个元素 | |
| 第二 | {"snd": pair} | 获取第二个元素 | |
| 元(Meta) | 引用 | {"quote": term} | 防止评估 |
| 评估 | {"eval": quoted} | 执行引用代码 | |
| 代码表 | {"code_of": "tool_name"} | 获取工具源码 | |
| 自己 | {"self": true} | 当前闭包引用(仅限工具) |
字面量
- 数字好的,我来翻译一下。
42,-17,3.14→ 整数(浮点数四舍五入后) - 布尔值(或布尔类型):
true,false - 字符串:
"hello","world" - 数组:
[1, 2, 3]→ 转换为连接列表(或称为表连接) - 空(或“无”):
null→ 单位价值
输入归一化
这个(或“该”) run 该工具会自动对输入进行规范化处理,以使命令行界面(CLI)的使用更加人性化:
- 字符串数字:
"42"→42 - 布尔字符串:
"true"→true,"false"→false - JSON 字符串:
"{\"x\": 5}"→ 解析为JSON对象 - 列表JSON 数组被转换为 cons 列表
这允许灵活的输入格式,同时在评估过程中保持类型安全。
快速参考
如需查看详细模式和示例,请参阅 用户指南。
需要记住的关键概念
- 工具参考工具可以作为字符串进行引用(
"square"), 内联lambda表达式,或通过code_of - 评估范围界定用户变量被保留,系统变量被清除,工具代码仍然可用
- 折叠签名\
fold\函数接收一个单一的配对(accumulator, item),不是两个参数 - 继续(或延续)仅使用注册过的工具,每一步都是一个MCP往返过程
- 自我指涉:
{"self": true}仅在已注册工具中有效 (通过以下方式创建:evolve), 不在内联 lambda 中 传递给run - 事件视界在λ演算中,工具无法创建其他工具
递归快速参考
需要递归吗? 使用这个决策树:
Can you structure it with an accumulator?
├─ Yes → Use `continue` (works for any depth)
│ Pattern: take pair [state, accumulator]
│ Base case: return accumulator
│ Recursive: compute new accumulator, continue with [new_state, new_acc]
│
└─ No, need result immediately?
├─ Small input (n < 20) → Use `self`
└─ Large input → Redesign with accumulator or use fold示例:
- 阶乘 →
continue使用累加器模式 - 总和 →
continue使用累加器模式 - 斐波那契数列(小n)→
self - 偶数/奇数相互递归 →
eval加号code_of
模式与示例
递归阶乘
工具可以使用基于延续的递归来逐步执行。由于 continue 暂停评估并将控制权返回给MCP层时,你必须使用累加器模式,其中计算在递归过程中进行,而不是在递归之后:
{
"name": "evolve",
"arguments": {
"name": "factorial",
"description": "Computes factorial using continuation with accumulator",
"code": {
"lam": "n_acc",
"body": {
"if": {
"cond": {"lte": [{"fst": {"var": "n_acc"}}, 1]},
"then": {"snd": {"var": "n_acc"}},
"else": {
"continue": {
"input": {
"pair": [
{"sub": [{"fst": {"var": "n_acc"}}, 1]},
{"mul": [{"fst": {"var": "n_acc"}}, {"snd": {"var": "n_acc"}}]}
]
}
}
}
}
}
}
}
}用法:
{
"name": "run",
"arguments": {
"code": "factorial",
"input": {"pair": [5, 1]}
}
}该程序将返回一个结构化的响应:
{
"type": "continuation",
"message": "Recursive step needed. Call run again with:",
"tool": "factorial_acc",
"next_input": {
"pair": [4, 5]
},
"step": 1
}这为客户呈现了一个Haskell表示形式:
Object (fromList [("message",String "Recursive step needed. Call run again with:"),("next_input",Object (fromList [("pair",Array [Number 4.0,Number 5.0])])),("step",Number 1.0),("tool",String "factorial_acc"),("type",String "continuation")])重要提示: 这个工具接受一对(数据/参数) [n, accumulator] 作为输入。从……开始 [5, 1] 计算5!。每一步递归都将累加器乘以当前的n,然后将n减1。
为什么是这种模式?
该 continue 原生函数并不返回一个你可以用以进行计算的值,而是返回一个延续标记。所有的计算都必须在调用之前完成 continue存储在累加器中。模式是:
- 输入:
[n, acc]哪里acc保存部分结果 - 基本情况: 当
n ≤ 1,返回累加器 - 递归情况: 计算新的累加器(
n * acc),继续进行[n-1, new_acc]
备选方案:使用直接递归 self (燃料限制):
{
"name": "evolve",
"arguments": {
"name": "factorial_self",
"description": "Simple factorial using self (small n only)",
"code": {
"lam": "n",
"body": {
"if": {
"cond": {"lte": [{"var": "n"}, 1]},
"then": 1,
"else": {
"mul": [
{"var": "n"},
{"app": {"func": {"self": true}, "arg": {"sub": [{"var": "n"}, 1]}}}
]
}
}
}
}
}
}这种方法对于小规模输入有效,但在n≈20时会达到燃料限制(10,000步)。
通过折叠实现高阶映射
{
"name": "map",
"description": "Maps a function over a list",
"code": {
"lam": "f",
"body": {
"lam": "list",
"body": {
"fold": [
{"lam": "acc_item", "body": {
"cons": {
"head": {"app": {"func": {"var": "f"}, "arg": {"snd": {"var": "acc_item"}}}},
"tail": {"fst": {"var": "acc_item"}}
}
}},
{"nil": true},
{"var": "list"}
]
}
}
}
}元编程:代码分析
{
"name": "count_operations",
"description": "Counts arithmetic operations in a tool",
"code": {
"lam": "tool_name",
"body": {
"eval": {
"quote": {
"analyze": [{"code_of": {"var": "tool_name"}}]
}
}
}
}
}该 code_of \primitive\ 返回工具的源代码作为引用数据,从而支持程序分析和转换。
建筑学
管道
JSON Input → Parser → Term → Evaluator → RuntimeValue → Encoder → JSON Output
validation syntax execution values serialization模块
| 模块 | 用途 |
|---|---|
| Main.hs | 入口点,JSON-RPC 循环 |
| Server.hs 翻译为中文是“服务器.hs”(但通常我们不会直接这样翻译文件名,而是保持原样,因为.hs是Haskell编程语言的源代码文件扩展名,所以更自然的表达可能是“Server.hs(Haskell源代码文件)”)。不过,在大多数情况下,文件名会保持不变,直接称为“Server.hs” | MCP协议,请求路由 |
| Core/Parser.hs 翻译为中文是:核心/解析器.hs | JSON → 术语验证 |
| Core/Evaluator.hs 翻译为中文是:核心/评估器.hs | 以燃料为条件的条款执行 |
| Core/Encoder.hs 翻译为中文是:“核心/编码器.hs” | 运行时值 → JSON |
| Core/Types.hs 翻译为中文可以是“核心/类型.hs”或“核心模块/类型定义.hs”,具体取决于上下文和该文件在项目中的具体用途。在这里,“Core”通常指的是程序或系统的核心部分,“Types”则指的是类型定义或类型相关的代码。而“.hs”是Haskell编程语言的源代码文件扩展名。所以,这个文件名可以理解为“核心模块中的类型定义文件”或“核心部分的类型相关代码文件” | 核心类型定义 |
| Core/Syntax.hs 翻译为中文是:“核心/语法.hs”。不过,这里的“Core”和“Syntax.hs”通常是编程语言或编译器开发中的术语,具体翻译可能需要根据上下文或项目背景来调整。但直接翻译的话,就是上述结果 | 术语 ADT |
| Tools/Registry.hs 翻译为中文是:“工具/注册表.hs” | 工具存放处 |
安全保证
- 基于燃料的终止每次评估都有有限的步骤(默认:10,000步)
- 纯粹的评价在λ演算中没有输入/输出或效果
- 验证后的执行只有结构上有效的术语才能运行
- 不可变注册表工具在执行过程中不能相互修改
MCP集成
MCP-PIF 实现了用于工具发现和执行的模型上下文协议:
系统工具
evolve- 创建新工具(在注册表中存储)run- 执行工具或内联lambda表达式list- 显示所有已注册的工具help- 显示基本元素和系统工具的文档
协议流程
- 客户端向标准输入发送JSON-RPC请求
- 服务器解析并路由到相应的处理程序
- 对于工具执行:
- 解析输入JSON → 术语 - 将工具代码注入到环境中 - 在燃料限制下进行评估 - 编码结果 → JSON
- 响应已发送到标准输出
连接MCP客户端
# Example using Python MCP SDK
import mcp
async with mcp.Client() as client:
await client.connect(stdio_transport("cabal run mcp-pif"))
# Create a tool
await client.call_tool("evolve", {
"name": "double",
"description": "Doubles a number",
"code": {"mul": [{"var": "x"}, 2]}
})
# Use it
result = await client.call_tool("run", {
"tool": "double",
"input": 21
})
print(result) # 42未来方向
MCP-PIF的当前设计在纯计算与具有副作用的操作之间保持了清晰的界限。我们已经考虑了几种扩展方式,这些方式将以有趣的方式拓展这些界限:
分析函数
当前系统主要是 合成的 - 使用基本元素来组合新函数。一个自然的扩展就是 分析性的 能力:
- 验证未进行评估的期限结构静态分析
- 规范化将项简化为规范形式
- 等价性检查证明两个项计算的是同一个函数
- 类型推断为lambda项推导类型
- 复杂性分析估算燃料需求
- 能力分析按基本元素分解工具
这些分析函数将对作为数据的代码(引号中的术语)进行操作,并可能实现强大的元编程模式。然而,它们需要经过精心设计,以在保持核心演算简洁性的同时,提供有意义的保证。
高效的原语(或基本操作)
另一个方向涉及有控制地引入影响:
系统交互:
- 文件输入/输出原语(
readFile,writeFile) - 过程控制(
exec,env) - 网络运营(
fetch,serve)
设计挑战:
- 如何维护纯洁的界限?
- 效果应该是单子式的、代数式的,还是基于延续的?
- 如何处理错误和资源管理?
- 能力控制采用哪种安全模型?
一种方法可能是 基于能力的安全工具可以在创建时声明所需的能力(如文件访问、网络等),而MCP层则负责强制执行访问控制。这与进化工具应承担证明其自身有效性的理念是一致的。
自托管与自举启动
终极的元循环目标:在MCP-PIF自身中实现MCP-PIF的评估器。这将需要:
- 用于JSON操作的基本函数/方法
- 模式匹配构造
- 环境的有效表示
- 元级别的燃料管理
一个自托管的PIF(编程接口框架)能够实现评估策略本身的运行时进化——一个真正具有自省能力的系统。
协议扩展
MCP边界可以支持额外的协议级操作:
- 工具版本管理跟踪工具随时间的演变
- 工具构成工具融合的协议级组合器
- 分布式注册表在MCP服务器之间共享工具
- 证明证书为工具附加正确性证明
这些扩展在保持事件视界原则的同时,增强了协议层的功能。
任何扩展的关键设计原则是:保持PIF作为语言模型计算可靠基底的简洁性和可预测性。
许可证
麻省理工学院(MIT)
