返回

文章详情

Imp 是 DSPy 在 BEAM 上的完整移植

Hacker News2026年9月27日 19:28

用于 Elixir 的声明式、自我改进语言模型程序。Imp 是 DSPy 在 BEAM 上的完整移植。你描述每个语言模型步骤的输入和返回,选择它的思考方式,并让优化器根据什么是好的例子来改进它。你可以获得签名、模块、优化器、代理循环和检索,运行时具备 OTP 的可靠性和并发性。DSPy 使每次对模型的调用都成为一个声明式的、带类型的函数,你可以对其进行测量和改进。在 BEAM 上,一个代理是一个进程:它保持自己的状态,接收消息,并在监控者的监督下与应用程序的其余部分一起运行。通过这两者,你可以从一个带类型的调用构建到多个长时间运行的代理,并通过测量每个部分来改进它。声明一个任务 lm = Imp.req_llm("openai:gpt-5.4-mini", api_key: System.fetch_env!("OPENAI_API_KEY")) triage = "issue -> kind: enum[bug,feature,question], summary" |> Imp.signature("对 GitHub 问题进行分类。") |> Imp.predict(lm: lm) {:ok, prediction} = Imp.call(triage, %{issue: "应用程序自 0.4 版本以来启动时崩溃,出现 ** (KeyError) 键 :lm 未找到"}) { Imp.get(prediction, :kind), Imp.get(prediction, :summary) } #=> {"bug", "应用程序自 0.4 版本以来启动时崩溃,出现 KeyError: 找不到 ':lm'。"} 你永远不需要编写提示或解析器。Imp 从签名构建提示,检查回复并与之对比,并给你类型化的字段:kind 总是三个值之一,或者调用返回错误。为了使相同的任务先进行推理,使用 Imp.chain_of_thought/2;要给它工具,使用 Imp.react/3。签名保持不变。测量并改进它。给 Imp 标记的示例和一个度量,它会对程序进行评分并优化它。你需要三组已标记的示例:trainset,优化器从中学习;valset,优化器用它来选择它尝试的程序;和 testset,你在之前和之后对其评分。strong_lm 是 GEPA 用于读取失败并编写新指令的更强大的模型。# 每组都是一个标记示例的列表,例如:example = Imp.example(%{issue: "请为仪表板添加深色模式",kind: "feature"}) |> Imp.with_inputs([:issue]) metric = Imp.exact_match(:kind) Imp.evaluate(triage, testset, metric).score optimizer = Imp.Optimizer.GEPA.new(metric, reflection_lm: strong_lm, max_metric_calls: 300) improved = Imp.optimize!(triage, optimizer, trainset, valset) Imp.evaluate(improved, testset, metric).score GEPA 运行程序,读取其失败的地方,并重写指令。其他优化器选择有效的示例(LabeledFewShot、BootstrapFewShot)、在指令和示例组合上搜索(MIPROv2)、从程序的更好和更差尝试中学习规则和示例(SIMBA),或训练模型的权重(微调,GRPO)。结果是一个新程序,其指令和示例你可以阅读、保存为 JSON,并作为差异进行查看。构建代理 工具是一个 Elixir 函数。Imp.react/3 构建一个代理,该代理调用工具直到能够给出答案。这个代理是使用 Req 读取网页的。Imp 依赖于 Req;如果你自己的代码像这个工具一样调用它,添加 {:req, "~> 0.6"} 到你的依赖中:fetch = Imp.tool(:fetch, "将网页内容作为文本读取。", fn %{"url" => url} -> Req.get!(url).body end, schema: %{ "type" => "object", "properties" => %{ "url" => %{ "type" => "string" } }, "required" => ["url"] } }) researcher = Imp.react("question -> answer", [fetch], lm: lm) question = "https://raw.githubusercontent.com/elixir-lang/elixir/v1.18.0/VERSION 说是什么版本?" <> "只回复版本。" {:ok, prediction} = Imp.call(researcher, %{question: question}) Imp.get(prediction, :answer) #=> "1.18.0" 将代理作为进程运行 Imp.call/2 在你的进程中运行程序。Imp.start_run/3 作为其自己的受监督进程运行它,因此你可以监视它、停止它,并决定它可能进行的工具调用:{:ok, run} = Imp.start_run(researcher, %{question: question}, authorize: fn call -> url = call.arguments["url"] || "" if String.starts_with?(url, "https://raw.githubusercontent.com/"),do: :allow,else: {:deny, :untrusted_host} end) {:ok, prediction} = Task.await(run.task, :infinity) for event <- Imp.Run.events(run), do: event.kind #=> [:run_started, :tools_sent, :model_request, :model_response, :tool_call, # :tool_result, :model_request, :model_response, :run_finished] Imp 还包括: MCP:导入你批准的任何 MCP 服务器的工具,它们将像你自己的工具一样工作。 ACP:将任何 Imp 程序作为代理服务于 Zed 和其他 ACP 客户端。 OTP:一次运行是一个你可以监视、停止和限制的进程,当启动它的进程结束时,运行就结束。模型请求的时间限制由你设置。一个可能已经生效的工具调用被报告为未知,从不静默重试。更多形式:R

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

☕请我喝杯咖啡