Imp 是 DSPy 到 BEAM 的完整移植。
Imp is a full port of DSPy to the BEAM

原始链接: https://github.com/deepfates/imp

Imp 是 DSPy 到 Elixir/BEAM 生态系统的完整移植,为构建大模型驱动的应用程序提供了一个声明式、类型安全的框架。你无需手动进行提示词工程,只需定义“签名”(signatures),并利用内置的优化器(如 GEPA 或 MIPROv2)根据标注示例和自定义指标来提升性能。 主要特性包括: * **可靠性与并发性:** 与 OTP 集成,允许代理作为受监督的进程运行,并支持状态管理、超时控制以及对工具执行过程的精细化控制。 * **工具集成:** 可轻松将 Elixir 函数转换为大模型工具,并支持模型上下文协议 (MCP) 和代理通信协议 (ACP)。 * **优化:** 自动调优指令和少样本示例,生成的程序具备可读性且支持版本控制,而非难以理解的晦涩提示词。 * **灵活性:** 支持复杂的工作流,包括思维链、自主代理和 RLM(基于检索的语言模型)。 Imp 专为生产环境设计,让你能够像编写标准 Elixir 代码一样,以严谨的方式构建、测试和评分 AI 组件。作为实验性版本 (v0.5),它利用 `ReqLLM` 来实现对主流模型提供商的广泛支持,并提供 Livebook 教程供用户快速上手。

Hacker News 上的讨论探讨了 **Imp** 的发布,这是一个将 **DSPy** 框架完整移植到 BEAM(Erlang/Elixir 虚拟机)的项目。 DSPy 旨在通过编程方式构建大语言模型(LLM)应用,以取代手动提示工程。然而,讨论中也体现了社区对该框架普及程度持有的持续质疑。评论者指出,自 DSPy 诞生以来,行业环境已发生显著变化;许多开发者已转向工具调用和基于代理(Agent)的架构。一些人认为,随着更新、更强大的模型在可靠地遵循结构和指令方面表现提升,DSPy 最初的价值主张——即帮助较弱的模型保持输出语法——已变得不再那么重要。
相关文章

原文

Declarative, self-improving language-model programs for Elixir.

An imp studies a hand of cards through a lens while a smaller imp springs from its tail.

Imp is a full port of DSPy to the BEAM. You describe what each language-model step takes and returns, choose how it thinks, and let an optimizer improve it against examples of what good looks like. You get signatures, modules, optimizers, agent loops and retrieval, running with the reliability and concurrency of OTP.

DSPy makes each call to a model a declared, typed function that you can measure and improve. On the BEAM, an agent is a process: it keeps its own state, receives messages, and runs under a supervisor alongside the rest of your application. With both, you can build anything from one typed call to many long-running agents, and improve each part by measuring it.

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("Triage a GitHub issue.")
  |> Imp.predict(lm: lm)

{:ok, prediction} =
  Imp.call(triage, %{issue: "App crashes on startup since 0.4 with ** (KeyError) key :lm not found"})

{Imp.get(prediction, :kind), Imp.get(prediction, :summary)}
#=> {"bug", "App crashes on startup since version 0.4 with a KeyError for `:lm` not found."}

You never write a prompt or a parser. Imp builds the prompt from the signature, checks the reply against it, and gives you typed fields: kind is always one of the three values, or the call returns an error. To make the same task reason first, use Imp.chain_of_thought/2; to give it tools, use Imp.react/3. The signature stays the same.

Measure it and improve it

Give Imp labeled examples and a metric, and it scores the program and optimizes it. You need three lists of issues you have already labeled: trainset, which the optimizer learns from; valset, which it uses to choose between the programs it tries; and testset, which you score on before and after. strong_lm is a more capable model that GEPA uses to read failures and write new instructions.

# Each set is a list of labeled issues like this one:
example =
  Imp.example(%{issue: "Please add a dark mode to the dashboard", 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 runs the program, reads where it failed, and rewrites its instructions. Other optimizers choose worked examples (LabeledFewShot, BootstrapFewShot), search over combinations of instructions and examples (MIPROv2), learn rules and examples from the program's own better and worse attempts (SIMBA), or train the model's weights (fine-tuning, GRPO). The result is a new program whose instructions and examples you can read, save as JSON, and review as a diff.

A tool is an Elixir function. Imp.react/3 builds an agent that calls tools until it can answer. This one reads web pages with Req. Imp depends on Req; if your own code calls it, as this tool does, add {:req, "~> 0.6"} to your dependencies:

fetch =
  Imp.tool(:fetch, "Read a web page as text.", 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 =
  "What version does https://raw.githubusercontent.com/elixir-lang/elixir/v1.18.0/VERSION say? " <>
    "Reply with just the version."

{:ok, prediction} = Imp.call(researcher, %{question: question})
Imp.get(prediction, :answer)
#=> "1.18.0"

Imp.call/2 runs a program in your process. Imp.start_run/3 runs it as its own supervised process instead, so you can watch it, stop it, and decide which tool calls it may make:

{: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 also includes:

  • MCP: import the tools of any MCP server you approve, and they work like your own.
  • ACP: serve any Imp program as an agent to Zed and other ACP clients.
  • OTP: a run is a process you can watch, stop and limit, and a run ends when the process that started it does. Model requests are cut to a deadline you set. A tool call that may already have taken effect is reported as unknown, never silently retried.
  • More shapes: RLM for inputs far larger than a context window, CodeAct and program of thought, which compute with small sandboxed expressions, and your own modules composed from these.

The optimizers work on agents too. GEPA reflects on whole agent runs and rewrites the instructions that steer them. Optimize Anything rewrites any text or JSON you can score, such as an agent's tool descriptions.

Imp needs Elixir 1.19 or later and a C++ compiler for one dependency (erlexec). It reaches models through ReqLLM, so any provider ReqLLM supports works.

Imp 0.5 is experimental and is its first release on Hex. Its API may still change, and its optimizers need large-scale benchmarking. Bug reports and pull requests are welcome.

  • Getting started builds one program step by step, from the first call to a supervised server, with real scores.
  • Coming from DSPy maps DSPy's names to Imp's.
  • Tutorials are Livebook notebooks you can run offline or with a key.
  • The cheatsheet has the common calls on one page.

Imp is MIT licensed.

联系我们 contact @ memedata.com