ActiveGraph 는 에이전트의 세계를 어떻게 모델링하는가

최근 Mario Zechner 의 recommended reading 을 보다가 Yohei Nakajima 의 ActiveGraph 를 처음 접했다. 기존의 agent framework 를 event log 와 graph 를 중심으로 색다르게 풀어낸 내용이 흥미로웠고, 그 구조를 조금 더 이해해보고 싶어 정리하게 되었다.

ActiveGraph 를 살펴보면서, 핵심은 graph 자체보다 에이전트 시스템의 원천을 어디에 두는가에 있다는 생각이 들었다. 이 글에서는 기존의 model-centered loop 와 ActiveGraph 의 event-sourced 구조를 비교하고, graph 와 behavior 가 어떤 방식으로 동작하는지 정리한다.

출처: https://x.com/badlogicgames/status/2057812929462841796

기존 에이전트 프레임워크는 모델 중심으로 동작한다

기존 에이전트 시스템의 동작을 단순화하면 다음과 같다. 사용자의 입력을 받은 언어 모델이 다음 행동을 결정하고, 필요할 때 tool 을 호출한다. 실행 결과는 다시 모델에게 전달된다.

flowchart TD
    U[사용자 입력] --> M[언어 모델]
    M --> D{다음 행동 결정}
    D --> T[Tool 호출]
    D --> R[응답 생성]
    T --> O[실행 결과]
    O --> M
    R --> E[사용자에게 전달]

이 구조에서 중심은 모델과의 대화 루프다. 주변에는 tool, prompt 규칙, workflow, memory, permission, logging 같은 계층이 추가된다. 짧은 작업에서는 자연스럽지만, 실행이 길어지면 현재 상태와 실행 경로가 여러 메시지와 로그에 분산될 수 있다.

로그는 이 실행이 끝난 뒤 무슨 일이 있었는지 확인하기 위한 기록에 가깝다. 하지만 로그를 남기는 것과 로그를 재생해 같은 상태를 다시 만드는 것은 다르다. 이 차이가 ActiveGraph 가 이벤트 로그를 시스템의 원천으로 삼는 이유로 이어진다.

ActiveGraph Source of Truth

ActiveGraph 의 구조는 다음과 같이 요약할 수 있다.

flowchart TD
    L[Append-only Event Log] --> G[Graph State Projection]
    G --> B[Behavior Reaction]
    B --> M[Graph Mutation]
    M --> N[New Event]
    N --> L

시스템에서 중요한 변화가 발생하면 먼저 이벤트가 기록된다. 현재의 graph state 는 이 이벤트 로그를 바탕으로 만들어지는 projection 이다. 그래프에 변화가 생기면 behavior 가 반응하고, behavior 는 새로운 객체, 관계, 상태 변경, 이벤트를 생성한다.

flowchart TD
    G([goal.created]) --> P[planner behavior]
    P --> T[task object 생성]
    T --> E[evidence 수집]
    E --> C[claim object 생성]
    E -.->|supports| C

이 구조에서 이벤트 로그는 graph state 보다 앞선다. graph 는 별도로 관리되는 최종 상태가 아니라, 이벤트를 재생했을 때 얻어지는 현재 상태다.

이것이 ActiveGraph 의 가장 중요한 전환이라고 생각한다. 기존 시스템에서는 실행이 먼저 있고 로그가 나중에 남는다. ActiveGraph 에서는 반대로 로그가 원천으로 존재하고, graph 와 실행의 흔적이 그 로그를 통해 설명된다.

Graph 는 세계 상태를 표현한다

ActiveGraph 의 graph 는 시스템이 알고 있는 객체와 관계를 표현한다. 다음 그림은 graph 안에서 객체들이 어떤 의미적 관계로 연결될 수 있는지를 보여준다.

flowchart LR
    E[Evidence]
    C1[Claim]
    C2[Claim]
    T1[Task A]
    T2[Task B]
    M[Memo]

    E -->|supports| C1
    C2 -->|contradicts| C1
    T1 -->|depends_on| T2
    M -->|derived_from| E

그림의 화살표는 supports, contradicts, depends_on, derived_from 의 의미를 가지며, 각각 evidence, claim, task, memo 사이의 의미를 표현한다. 즉, graph 는 “다음에 무엇을 할 것인가”보다 “현재 무엇이 존재하고, 그것들이 어떤 관계를 가지는가”를 나타낸다.

그래서 ActiveGraph 의 graph 는 에이전트가 활동하는 세계 상태에 가깝다.

  • 어떤 task 가 존재하는가
  • 어떤 claim 이 만들어졌는가
  • 어떤 evidence 가 claim 을 지지하는가
  • 어떤 객체가 다른 객체에 의존하는가
  • 어떤 주장이 서로 모순되는가
  • 어떤 결과가 어떤 자료에서 파생되었는가

Object

ActiveGraph 의 graph 는 object 와 relation 으로 구성된다. Object 는 graph 안에 존재하는 typed entity 다.

claim = graph.add_object("claim", {
    "text": "Q3 revenue grew 28% YoY.",
    "confidence": 0.85,
})

프로젝트에 따라 다음과 같은 객체를 정의할 수 있다.

  • task
  • claim
  • evidence
  • report
  • memo
  • company
  • decision

ActiveGraph 에서 기본적으로는 typed object 와 relation 을 제공하고, 사용자가 자신의 도메인에 맞는 객체 타입을 정의하는 구조이다.

Relation

Relation 은 object 사이의 의미 있는 관계다.

graph.add_relation(
    claim.id,
    evidence.id,
    "supports",
    {"strength": 0.9},
)

가능한 relation 은 다음과 같다.

  • supports
  • contradicts
  • depends_on
  • references
  • derived_from
  • blocks

Relation 을 통해 관계 자체가 의미를 가지고, behavior 의 실행 조건이 될 수 있다.(예시는 아래 behavior 를 참고)

Behavior

depends_on 이 단순한 연결이 아니라 반응 조건이 된다는 점을 보자. 문서 게시를 위해 검토가 먼저 완료되어야 한다면, 게시 task A 는 검토 task B 에 depends_on 관계를 가진다.

Behavior 는 event 와 graph state 의 변화, 그리고 relation 이 정의한 조건에 따라 실행된다.

flowchart LR
    A[게시 task A<br/>blocked] -->|depends_on| B[검토 task B<br/>completed]
    B -->|completed event| H[unblock behavior]
    H -->|relation lookup| A2[게시 task A<br/>open]
    A2 --> E([task.unblocked])

그림에서 relation 은 A 와 B 를 연결하는 정보이면서, B 의 완료 이후 어떤 behavior 가 실행되어야 하는지를 판단하는 근거이기도 하다. behavior 는 relation 을 읽고 A 의 상태를 변경하며, 그 결과를 다시 새로운 event 로 기록한다.

ActiveGraph 와 Context 설계

ActiveGraph 가 제공하는 것은 단순한 memory 나 graph database 가 아니다. event log 를 source of truth 로 두고, 그 로그에서 graph state 를 projection 하며, graph 의 object 와 relation 을 behavior 가 참조하고 변화시키는 실행 구조다.

이 구조에서는 작업의 결과만 남는 것이 아니다. 어떤 목표에서 시작했는지, 어떤 task 와 evidence 가 있었는지, object 들이 어떤 relation 으로 연결되었는지, 어떤 상태 변화가 다음 behavior 를 촉발했는지가 함께 남는다. 모델은 이전 대화의 일부를 요약해서 전달받는 대신, 현재 작업과 관련된 graph projection 을 참조해 작업 전반의 상태와 관계를 파악할 수 있다.

이것은 장기 작업과 자기개선적 시스템을 만들기 위한 구조적 기반이 될 수 있다. 과거 실행을 다시 구성할 수 있으면 현재 판단이 어떤 상태에서 나왔는지 확인할 수 있고, 이전의 evidence 와 decision 을 새로운 실행의 context 로 사용할 수 있다. 다른 선택을 했을 때의 결과를 비교할 수 있으면, 시스템의 판단과 behavior 를 수정하는 학습 loop 도 만들 수 있다.

다만 graph 를 만든다고 해서 자동으로 좋은 context 가 만들어지는 것은 아니다. 어떤 event 를 기록할 것인지, 어떤 object 와 relation 을 정의할 것인지, 모델에게 어떤 graph view 를 어떤 시점에 전달할 것인지 등이 설계 문제로 남는다. 모든 기록을 모델에게 넘기면 context 가 다시 커지고, 지나치게 요약하면 관계와 실행의 근거가 사라진다.

좋은 context 는 더 긴 대화나 더 많은 요약만으로 만들어지지 않는다. 실행의 변화가 구조화된 event 로 남고, 그 event 에서 현재 상태와 관계가 복원되며, 모델이 목적에 맞는 graph view 를 참조할 수 있어야 한다. ActiveGraph 는 이 방향을 구현하는 하나의 runtime 구조를 보여준다.

참고: