research

pi-agent-core — Agent Loop 핵심 구조

pi-agent-core — Agent Loop 핵심 구조

핵심 루프 (의도적으로 미니멀)

async function* agentLoop(
  model: Model,
  context: Context,
  tools: Tool[],
  options: AgentOptions
): AsyncGenerator<AgentEvent> {
  while (true) {
    // 1. LLM 호출 (스트리밍)
    const response = await streamCompletion(model, { ...context, tools });
    yield { type: 'assistant_message', content: response.content };

    // 2. tool call 없으면 종료 — 모델이 결정
    if (!response.toolCalls?.length) break;

    // 3. tool 순차 실행
    const results: ToolResult[] = [];
    for (const call of response.toolCalls) {
      yield { type: 'tool_start', name: call.name, input: call.input };
      const result = await executeToolCall(call, context);
      results.push(result);
      yield { type: 'tool_end', name: call.name, result };
    }

    // 4. 결과를 context에 추가하고 다시 루프
    context.messages.push({ role: 'assistant', content: response.content, toolCalls: response.toolCalls });
    context.messages.push({ role: 'user', toolResults: results });
  }
}

구조적 특징

max_steps 없음: 루프 횟수를 제한하는 knob이 없다. 모델이 tool call을 내지 않을 때까지 계속 돌린다. "왜 추가하지 않았냐"는 질문에 Mario의 답변: "쓸 일이 없었다."

이벤트 스트림 기반: AsyncGenerator<AgentEvent>로 루프 내 모든 단계를 이벤트로 방출. UI나 외부 시스템이 쉽게 구독할 수 있다.

tool 순차 실행: 병렬 실행 없이 순서대로 하나씩. 단순하고 예측 가능.

tool 인자 검증: TypeBox 스키마로 tool 인자를 자동 검증. 실패 시 에러를 tool result로 모델에게 돌려줘서 재시도하도록 유도.

위에 올라오는 것들 (pi-coding-agent)

pi-agent-core는 순수한 루프만 담당하고, 그 위에 pi-coding-agent가 실용적인 기능을 추가:

  • JSONL 기반 세션 영속화 (브랜칭 지원)
  • context compaction (토큰 오버플로우 대응)
  • Extension 시스템 (이벤트 훅, 커스텀 도구)
  • 빌트인 도구: read, write, edit, bash

Python 구현과의 대응

지금 tool_calls.py가 짜고 있는 구조가 이 루프와 동일:

| pi-agent-core | tool_calls.py | |---|---| | streamCompletion() | POST /v1/chat/completions | | response.toolCalls?.length | finish_reason == "tool_calls" 체크 | | executeToolCall() | search_web() 실행 | | context.messages.push(...) | messages 리스트에 결과 추가 | | while (true) | 구현 예정인 루프 |

Rust 포팅 레퍼런스

pi_agent_rust (Dicklesworthstone)라는 Rust 포팅이 이미 존재함. 원본 TypeScript 대비 장점으로 꼽는 것들:

  • 루프 경계에서 명시적으로 취소/타임아웃 체크
  • 리소스 수명이 런타임 타입으로 명확히 표현됨
  • 메인 루프가 AgentStart → TurnStart → TurnEnd → AgentEnd 순서를 보장

Rust로 재구현할 때 이 레포도 참고 가치 있음.

관련