Skip to main content

README.md

# ElGraph

**BEAM(Elixir/OTP) 위에서 도는 graph-first 에이전트 프레임워크.** 내구 실행·HITL(사람 개입)·
time-travel·체크포인트 — LangGraph가 Python에서 라이브러리로 재구현한 것이, 여기선 런타임 기본이다.
Python 없음.

`el_graph`는 **코어 런타임**이다: 그래프 실행기, 체크포인터, 에이전트 런타임, LLM/MCP 어댑터.
런타임 의존성은 `:telemetry` 하나.

> [ElGraph 우산 프로젝트](https://github.com/showjihyun/ElGraph)의 일부. 실시간 관측 UI(ElTrace),
> A2A/AG-UI HTTP 서버, Postgres/Redis 체크포인터, OpenTelemetry 브리지는 형제 패키지에 있다.
>
> 전체 문서: [한국어](https://github.com/showjihyun/ElGraph/blob/main/README.md) ·
> [English](https://github.com/showjihyun/ElGraph/blob/main/README.en.md)

## 설치

아직 Hex 미출시 — git 의존성으로 가져온다(공개 저장소라 **설치 인증 불필요**):

```elixir
def deps do
  [
    # 우산 서브앱이라 sparse로 코어만 가져온다:
    {:el_graph, github: "showjihyun/ElGraph", sparse: "apps/el_graph"}
    # (향후 Hex 출시 시) {:el_graph, "~> 0.3"}
  ]
end
```

ElGraph는 전역 프로세스를 스스로 시작하지 않는다 — 필요한 것(Task.Supervisor, 체크포인터 테이블
소유 프로세스 등)을 호스트 앱의 슈퍼비전 트리에 마운트한다.

## 첫 그래프 (30초)

```elixir
graph =
  ElGraph.new()
  |> ElGraph.state(:n, default: 0)
  |> ElGraph.add_node(:double, fn %{n: n}, _ctx -> %{n: n * 2} end)
  |> ElGraph.add_node(:inc, fn %{n: n}, _ctx -> %{n: n + 1} end)
  |> ElGraph.add_edge(:double, :inc)
  |> ElGraph.compile(entry: :double)

ElGraph.invoke(graph, %{n: 10})
#=> {:ok, %{n: 21}}
```

노드는 `(state, ctx)`를 받아 상태 부분 업데이트 맵을 돌려준다. 그게 전부다.

## 첫 에이전트 — API 키 불필요

`ElGraph.Test.ScriptedLLM`은 미리 정한 응답을 돌려주므로, 자격증명 없이 ReAct 에이전트 루프를
그대로 돌려볼 수 있다:

```elixir
alias ElGraph.{LLM, Presets}
alias ElGraph.Test.ScriptedLLM

{:ok, pid} = ScriptedLLM.start_link([LLM.assistant("안녕하세요! 무엇을 도와드릴까요?")])
graph = Presets.react({ScriptedLLM, pid}, [])

ElGraph.invoke(graph, %{messages: [LLM.user("안녕")]})
#=> {:ok, %{messages: [%{role: :user, ...}, %{role: :assistant, content: "안녕하세요! ..."}], ...}}
```

준비되면 실제 어댑터로 교체 — `ElGraph.LLM.OpenAI` / `.Anthropic` / `.Gemini`.

## 내구 실행 + 사람 개입(HITL)

```elixir
# BEAM 내장 체크포인터 — 외부 인프라 0
cp = {ElGraph.Checkpointer.ETS, ElGraph.Checkpointer.ETS.config(owner_pid)}

# 승인이 필요한 지점에서 멈춘다...
{:interrupted, %{node: :approve, payload: _}} =
  ElGraph.invoke(graph, input, checkpointer: cp, thread_id: "t1")

# ...사람의 답을 주입해 재개 — 완료된 노드는 재실행하지 않는다
{:ok, final} = ElGraph.resume(graph, checkpointer: cp, thread_id: "t1", resume: "approved")
```

코어에 내장된 체크포인터: `ETS`(인메모리), `DETS` / `Mnesia`(BEAM 내장 디스크 영속, 인프라 0).
Postgres·Valkey/Redis 백엔드는 형제 패키지(`el_graph_ecto`, `el_graph_redis`).

## 라이선스

[MIT](LICENSE) © 2026 Poor Coin Pepe