imp README

The project's README from github.com/deepfates/imp. As one markdown file: README.md.

Imp

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.

Declare a task

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.

Build agents

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"

Run agents as processes

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:

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.

Install

{:imp, "~> 0.8"}

Imp needs Elixir 1.19 or later and a C and C++ compiler, for the native code in two dependencies (jaxon and erlexec). The first compile needs network access, because erlexec's build fetches rebar3 plugins. It reaches models through ReqLLM, so any provider ReqLLM supports works.

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

Learn

Imp is MIT licensed.

imp (deepfates/imp)

Searching Database…

255 ·GitHub
这是什么?Imp 是 DSPy 在 BEAM 上的完整移植:一个用于构建并自动改进语言模型程序的 Elixir 框架。你把一个步骤的输入和输出声明为带类型的签名,优化器再根据标注数据调整它的提示词或示例。
它是库,还是用于构建应用的框架?它是添加到你自己的 BEAM 应用中的 Elixir 框架/库:你定义签名和模块,Imp 负责构建并解析提示词,代理则以受监督的 OTP 进程形式与你的其余代码一起运行。
怎么把 Imp 加进 Elixir 项目?在 mix.exs 中把 Imp 添加为依赖,并用 mix deps.get 拉取;首次编译需要联网,因为它有一个原生依赖(erlexec)会在构建期间获取 rebar3 插件。
运行之前需要安装什么?你需要 Elixir 1.19 或更高版本,外加 C 和 C++ 编译器,因为有两个依赖(jaxon 和 erlexec)要编译原生代码。模型访问通过 ReqLLM 进行,所以你还需要为想使用的 LLM 服务商准备 API 密钥(须是 ReqLLM 支持的服务商)。
它现在稳定到可以依赖了吗?还没有——README 将 Imp 0.8 标记为实验性,警告其 API 仍可能变化,并说明其优化器还需要大规模基准测试。
使用它需要先懂 DSPy 吗?不一定——文档包含一份从零构建程序的“Getting started”指南,还有一份“Coming from DSPy”指南,为已经熟悉这个 Python 库的人把 DSPy 的名称对应到 Imp。
Imp 自带哪些优化器?包括:GEPA,用更强的模型对失败进行反思并重写指令;LabeledFewShot 和 BootstrapFewShot,挑选现成的有效示例;MIPROv2,搜索指令与示例的组合;SIMBA,从程序自身较好和较差的尝试中学习;以及微调/GRPO,训练模型权重。
代理工具是怎么工作的?工具就是一个普通的 Elixir 函数;Imp.react/3 会构建一个反复调用这些工具直到能给出答案的代理,而 Imp.startrun/3 把该代理作为独立的受监督 OTP 进程运行,你可以观察、停止或限制它。
Imp 能使用现有的 MCP 服务器吗?可以——Imp 可以导入你批准的任何 MCP 服务器的工具并像原生函数一样使用,它还能把任何 Imp 程序作为代理提供给 Zed 等 ACP 客户端。
如果运行中途被停止,正在进行的工具调用会怎样?对于可能已经生效的工具调用,Imp 绝不会默默重试——而是以“未知”状态报告回来,并且模型请求会在你设定的截止时限处被切断。
Creator
deepfates
Size
46.9 MB
Stars
255
Age
3mo
Commits · 90 Days