一个基于JSON的lambda演算运行时系统,具有元循环评估功能,设计为MCP(模型上下文协议)服务器。它使语言模型能够通过元编程动态演化工具。
# 构建项目
cabal build
# 启用调试模式以进行详细的评估跟踪
MCP_DEBUG=1 cabal run mcp-pif
# 调试输出(到stderr)显示:
# - 每个评估步骤
# - 每步的环境键
# - 闭包创建和应用
# - 工具代码查找
// 创建一个工具
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "evolve",
"arguments": {
"name": "square",
"description": "计算一个数的平方",
"code": {"lam": "x", "body": {"mul": [{"var": "x"}, {"var": "x"}]}}
}
}
}
// 使用工具
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "run",
"arguments": {
"tool": "square",
"input": 7
}
}
}
// 返回:49
// 获取帮助
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "help",
"arguments": {"category": "lists"}
}
}
// 返回:列表原始操作的文档
对于纯计算的语言模型需要计算工具,但现有的系统要么提供固定的API,要么允许无限制的代码执行。在元编程和自我修改的背景下,这两种都不是理想的解决方案。这个程序提供了一个中间结构,用于安全、可检查、可演化的计算。
从理想的角度来看,MCP-PIF可以被视为一种通用的、变形的计算机接口,其中语言模型作为执行功能插入。这里我们提供了一组简单的lambda演算原始操作,仅用于访问纯计算。
MCP-PIF 提供了带有三个元编程原始操作的lambda演算:
quote - 将代码视为数据(防止评估)eval - 动态执行被引用的代码code_of - 查看任何工具的源代码这创建了一个元循环系统,其中工具可以分析和转换其他工具,同时保持确定性和燃料有限的执行。它是同构的,但受到约束。
值得注意的是,quote 和 eval 并非完全对称:
-- eval 清理环境:
let cleanEnv = M.filterWithKey (\k _ -> not $ k `elem` ["__tool_name", "__self"]) env
这防止了被 eval 执行的代码继承错误的工具上下文。当 eval 执行被引用的代码时:
__tool_name 和 __self 以防止工具上下文混淆)code_of 查看)__eval_depth 计数器(最大深度:1100,防止无限 eval 循环)因为这个框架是在 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]} | 除法(除以零会出错) | |
| 取模 | {"mod": [a, b]} | 取模(除以零会出错) | |
| 比较 | 等于 | {"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} | 获取第二个元素 | |
| 元 | 引用 | {"quote": term} | 防止评估 |
| 执行 | {"eval": quoted} | 执行被引用的代码 | |
| 源码 | {"code_of": "tool_name"} | 获取工具源码 | |
| 自身 | {"self": true} | 当前闭包引用(仅限工具) |
42, -17, 3.14 → 整数(浮点数四舍五入)true, false"hello", "world"[1, 2, 3] → 转换为 cons 列表null → 单位值run 工具自动规范化输入,以便更方便地使用 CLI:
"42" → 42"true" → true, "false" → false"{\"x\": 5}" → 解析为 JSON 对象这允许灵活的输入格式,同时在评估期间保持类型安全性。
有关详细模式和示例,请参阅**用户指南**。
"square")、内联lambda或通过 code_of 引用。{"self": true} 仅在通过 evolve 创建的已注册工具内部工作,不在传递给 run 的内联lambda中工作。需要递归吗? 使用此决策树:
你可以用累加器来构建吗?
├─ 是 → 使用 `continue`(适用于任何深度)
│ 模式:取配对 [状态, 累加器]
│ 基础情况:返回累加器
│ 递归:计算新累加器,继续 [新状态, 新累加器]
│
└─ 不,需要立即结果?
├─ 小输入(n < 20)→ 使用 `self`
└─ 大输入 → 重新设计使用累加器或使用折叠
示例:
continue 和累加器模式continue 和累加器模式selfeval + code_of工具可以使用基于延续的递归进行逐步执行。由于 continue 暂停评估并将控制权返回给 MCP 层,因此必须使用累加器模式,在递归过程中进行计算,而不是之后:
{
"name": "evolve",
"arguments": {
"name": "factorial",
"description": "使用延续和累加器计算阶乘",
"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": "需要递归步骤。再次调用 run:",
"tool": "factorial_acc",
"next_input": {
"pair": [4, 5]
},
"step": 1
}
这呈现给客户端为 Haskell 表示:
Object (fromList [("message",String "需要递归步骤。再次调用 run:"),("next_input",Object (fromList [("pair",Array [Number 4.0,Number 5.0])])),("step",Number 1.0),("tool",String "factorial_acc"),("type",String "continuation")])
重要: 工具接受一个配对 [n, 累加器] 作为输入。从 [5, 1] 开始计算 5!。每次延续步骤将累加器乘以当前 n,然后递减 n。
continue 原始操作不会返回一个可以直接计算的值——它返回一个延续标记。所有计算必须在调用 continue 之前完成,并存储在累加器中。模式是:
[n, acc] 其中 acc 持有部分结果n ≤ 1 时,返回累加器n * acc),继续 [n-1, 新累加器]替代方案:直接递归使用 self(燃料有限):
{
"name": "evolve",
"arguments": {
"name": "factorial_self",
"description": "简单阶乘使用 self(仅适用于小 n)",
"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": "将函数映射到列表上",
"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": "统计工具中的算术操作次数",
"code": {
"lam": "tool_name",
"body": {
"eval": {
"quote": {
"analyze": [{"code_of": {"var": "tool_name"}}]
}
}
}
}
}
code_of 原始操作返回工具的源码作为引用数据,从而支持程序分析和转换。
JSON 输入 → 解析器 → 项 → 评估器 → 运行时值 → 编码器 → JSON 输出
验证 语法 执行 值 序列化
| 模块 | 目的 |
|---|---|
| Main.hs | 入口点,JSON-RPC 循环 |
| Server.hs | MCP 协议,请求路由 |
| Core/Parser.hs | JSON → 项验证 |
| Core/Evaluator.hs | 带燃料的项执行 |
| Core/Encoder.hs | 运行时值 → JSON |
| Core/Types.hs | 核心类型定义 |
| Core/Syntax.hs | 项 ADT |
| Tools/Registry.hs | 工具存储 |
MCP-PIF 实现了模型上下文协议,用于工具发现和执行:
evolve - 创建新工具(存储在注册表中)run - 执行工具或内联 lambda 表达式list - 显示所有已注册的工具help - 显示原始操作和系统工具的文档# 示例使用 Python MCP SDK
import mcp
async with mcp.Client() as client:
await client.connect(stdio_transport("cabal run mcp-pif"))
# 创建一个工具
await client.call_tool("evolve", {
"name": "double",
"description": "将一个数加倍",
"code": {"mul": [{"var": "x"}, 2]}
})
# 使用它
result = await client.call_tool("run", {
"tool": "double",
"input": 21
})
print(result) # 42
MCP-PIF 当前的设计保持了纯计算和效应操作之间的明确界限。已经考虑了几种扩展,这些扩展将以有趣的方式扩大这些界限:
当前系统主要是合成的——使用原始操作组合新函数。一个自然的扩展将是分析能力: