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로 재구현할 때 이 레포도 참고 가치 있음.
관련
- overview — pi-sdk 전체 지도
- research/tool-call-llm/why-tool-call-loop-is-needed — 루프가 필요한 이유