monty-go:Pydantic的蒙提·巴塞尔解释器的纯Go封装
安全地从Go运行LLM生成的Python代码——没有容器,没有CGO,没有子进程。一个围绕Pydantic的蒙提·巴塞尔解释器的纯Go包装,编译为WebAssembly并通过wazero加载。你的Go代理编写Python代码,monty-go在沙箱WASM实例中执行代码,启动时间在毫秒级,并在代码调用外部函数时暂停,以便你的Go代码可以处理它。使用命令:go get github.com/fugue-labs/monty-go。为什么?当LLM编写代码而不是进行顺序工具调用时,工作效率更快,更便宜,更可靠。实现方式是:代理→tool_call("search", {query: "weather london"})→结果;代理→tool_call("search", {query: "weather tokyo"})→结果;代理→tool_call("compare", {a: result1, b: result2})→结果。LLM写道:london = search (query = "weather london");tokyo = search (query = "weather tokyo");compare (a = london, b = tokyo)。一次模型调用,而不是三次。Python代码调用你的Go函数,Monty在每次调用时暂停,你的Go代码执行它,然后Monty恢复。没有容器。没有沙箱服务。没有exec()。只是一个2.9MB的WASM二进制文件,嵌入在你的Go二进制文件中。有关动机,请参见:Anthropic的编程工具调用、Anthropic的代码执行与MCP、Cloudflare的代码模式、Hugging Face的Smol Agents。 快速入门 package main import ( "context" "fmt" "log" montygo "github.com/fugue-labs/monty-go" ) func main() { runner, err := montygo.New() if err != nil { log.Fatal(err) } defer runner.Close() result, err := runner.Execute(context.Background(), "x * 2 + y", map[string]any{"x": 10, "y": 5}, ) if err != nil { log.Fatal(err) } fmt.Println(result) // 25 } 外部函数(暂停/恢复) 真正的强大在于外部函数调用。当Python代码调用你声明的函数时,Monty暂停执行,你的Go回调处理它,然后Monty恢复并返回值: result, err := runner.Execute(ctx, `london = get_weather("London") tokyo = get_weather("Tokyo") f"{london['city']}: {london['temp']}°C, {tokyo['city']}: {tokyo['temp']}°C"`, nil, montygo.WithExternalFunc(func(ctx context.Context, call *montygo.FunctionCall) (any, error) { city, _ := call.Args["city"].(string) // 你的真实实现 - HTTP调用,数据库查询,任何内容。 return map[string]any{"city": city, "temp": 22}, nil }, montygo.Func("get_weather", "city")), ) // 结果:"London: 22°C, Tokyo: 22°C" 多个函数以相同的方式工作 - 注册它们并按名称调度: result, err := runner.Execute(ctx, code, nil, montygo.WithExternalFunc(func(ctx context.Context, call *montygo.FunctionCall) (any, error) { switch call.Name { case "search": return doSearch(call.Args) case "calculate": return doCalculate(call.Args) case "store": return doStore(call.Args) default: return nil, fmt.Errorf("unknown function: %s", call.Name) } }, montygo.Func("search", "query"), montygo.Func("calculate", "expression"), montygo.Func("store", "key", "value")), ) 资源限制 通过内存、时间、分配和递归限制防止代码失控: result, err := runner.Execute(ctx, code, inputs, montygo.WithLimits(montygo.Limits{ MaxDuration: 5 * time.Second, MaxMemoryBytes: 10 * 1024 * 1024, // 10 MB MaxAllocations: 100000, MaxRecursionDepth: 100, }), ) 无限循环、内存炸弹和深度递归都会以*MontyError干净终止。Go的context.Context的最后期限也会得到尊重——取消上下文,WASM实例停止。 打印捕获 捕获Python的print()输出: var output strings.Builder _, err := runner.Execute(ctx, `print("step 1 done")`, nil, montygo.WithPrintFunc(func(s string) { output.WriteString(s) }), ) fmt.Print(output.String()) // "step 1 done\n" OS调用 Python文件系统和环境访问通过你的Go回调: result, err := runner.Execute(ctx, `from pathlib import Path data = Path("/config/settings.json").read_text() data`, nil, montygo.WithOsCallFunc(func(ctx context.Context, call *montygo.OsCall) (any, error) { switch call.Function { case "Path.read_text": path, _ := call.Args[0].(string) return readFromYourStorage(path) case "Path.exists": path, _ := call.Args[0].(string) return existsInYourStorage(path), nil default: return nil, fmt.Errorf("blocked: %s", call.Function) } }), ) 除非你的回调允许,否则不会发生文件系统访问。 Golem集成 monty-go旨在为Golem提供代码模式,Golem是Go的生产代理框架。与顺序工具调用不同,LLM编写Python代码来调用你的工具作为函数——Monty安全地执行它,而Golem负责整个过程。以下是Golem与monty-go的结合效果: import ( "github.com/fugue-labs/gollem" "github.com/fugue-labs/gollem/provider/anthropic" montygo "github.com/fugue-labs/monty-go" )
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡