llaa33219 / coreouto

완성된 agent가 아니라,
agent를 만드는 core

openclaw · hermes · Vercel PAT 같은 완제품 agent와 달리, coreouto는 자기 영역에 맞는 맞춤형 agent를 찍어낼 수 있도록 "loop 코어"만 제공하는 플랫폼형 라이브러리다. 아래에서 "도대체 무엇을 확장할 수 있는가"를 도식과 함께 분석한다.

출처: github.com/llaa33219/coreouto

1 핵심 개념 — 왜 다른가

기존 agent들은 이미 특정 용도로 조립된 완제품이다. coreouto는 스스로를 framework가 아니라 library로 규정한다. 제공하는 것은 딱 하나의 loop뿐이다.

"coreouto는 framework가 되려 하지 않는다. CLI도, server mode도, deployment pipeline도, plugin system도 없다. 그것은 library다: import하고, call하면, return한다." — docs/philosophy.md
User message internal loop (agent.py) provider.create() LLM 응답 수집 tool call 있는가? tool 실행 결과를 다시 LLM에 yes 최종 답 text & no tool call no
그림 1. coreouto의 유일한 종료 규칙 — "텍스트 있고 tool call 없음" → 그 텍스트가 최종 답

종료 규칙은 단 하나다:

모델이 텍스트는 있고 tool call은 없는 응답을 내면 → 그 텍스트가 최종 답이고 루프 종료.
그 외 provider가 unrecoverable하게 종료(token 초과 · refusal · content filter · 서버 실패)해도 종료.
while True:
    response = await provider.create(...)
    if not tool_calls:
        if response.content:
            return Response(...)   # ← 유일한 정상 종료
        continue                  # 텍스트·tool call 둘 다 없으면 재프롬프트
    # tool call이 있으면 실행하고 루프 계속

설계를 관통하는 5가지 철학

철학의미코드에서의 발현
Minimalism
최소주의
loop 하나만 잘하면 된다agent.py를 10분이면 다 읽는다. 숨은 상태머신 없음
Extensibility
확장성
모든 주요 컴포넌트가 교체 가능provider/tool/preset/hook 전부 단순 인터페이스
Explicitness
명시성
내가 시키지 않으면 아무 일도 안 일어남auto-discovery 없음. tool·hook 직접 등록
Fragmentation
파편화
각 조각이 독립적으로 동작모듈마다 독립 registry(dict). 서로 모름
Conciseness
간결성
사용 코드가 짧고 읽기 쉽다tool 5줄, preset 3줄, 호출 1줄

2 확장 가능한 5개 지점

public API와 각 registry를 종합하면 확장 축은 정확히 5개다. 각각 독립된 module-level dict registry를 가지며 서로를 모른다.

CORE Agent loop agent.py Provider LLM 백엔드 Tool 함수+타입힌트 Preset 설정=데이터 Hook 이벤트 7개 Multi-agent agent→tool
그림 2. loop 코어를 둘러싼 5개의 opt-in 확장 축 (각기 독립 registry)
providers

① Provider

무엇이든 LLM 백엔드로. 상속 불필요한 duck-typed Protocol.

create() · format_assistant_message() · format_tool_result()
_TOOL_REGISTRY

② Tool

타입힌트→JSON Schema, docstring→설명 자동 추출.

@register_tool / register_tool_class
_PRESETS

③ Preset

직렬화·DB저장 가능한 Pydantic 데이터. to_config()로 찍어냄.

register_agent_preset(...)
_HOOKS

④ Hook

7개 이벤트에 sync/async 콜백. 관측·개입 지점.

register_hook(EVENT, fn)
multi_agent

⑤ Multi-agent

agent를 tool로 감쌈. 정적 위임 / 동적 dispatch.

agent_as_tool / make_delegate_tool

① Provider — 메서드 3개짜리 Protocol

@runtime_checkable
class Provider(Protocol):
    async def create(self, messages, *, model, tools=None, ...) -> LLMResponse: ...
    def format_assistant_message(self, response) -> Message: ...
    def format_tool_result(self, tool_call, result) -> Message: ...

→ OpenAI·Anthropic·Google뿐 아니라 로컬 모델·프록시·human-in-the-loop·mock까지 provider로 감쌀 수 있다. 기본 제공: openai, openai-response, anthropic, google.

④ Hook — 7개 관측·개입 지점

이벤트발화 시점대표 용도
BEFORE_LLM_CALLLLM 호출 직전메시지 검사/수정, 프롬프트 로깅
AFTER_LLM_CALLLLM 응답 직후토큰 집계, 응답 로깅
BEFORE_TOOL_CALLtool 실행 직전인자 검증, 승인 게이트
AFTER_TOOL_CALLtool 실행 직후결과 로깅, 사용량 수집
ON_ITERATION매 루프 반복마다진행 알림, 자동 요약 트리거
ON_FINISH루프 종료 시최종 후처리, 재프롬프트 결정
ON_USER_INJECTION런타임 메시지 주입 시human-in-the-loop 처리

contrib/hooks.py 레시피: token_collection_hook, auto_summarize_hook, token_limit_warning_hook, iteration_notification_hook, tool_usage_collection_hook.

3 examples 정밀 분석 (핵심)

examples는 단순 데모가 아니라 "각 확장 축을 어떻게 꽂는가"를 난이도 순으로 배치한 커리큘럼이다. 번호에서 08·11이 빠져 있는 것도 특징적(의도적 파편화 — 각 예제가 독립적).

01최소실행 02–04단일 축 06자작 provider 05 · 09오케스트레이션 07·10·12–15운영 디테일
그림 3. 예제 번호 순서 = 확장 난이도 곡선
#파일확장하는 것핵심 학습 포인트
0101_simple.py(없음)최소 실행. AgentConfig 직접 생성 → agent.call()
0202_tools.pyTool@register_tool로 함수 노출. 스키마=타입힌트, 설명=docstring
0303_presets.pyPreset이름 붙은 config 번들. OpenAI·Anthropic·Google 3개 동시 등록 → 축의 독립성
0404_hooks.pyHook3가지 부착법: inline hook / token_collection_hook(sink 공유) / iteration_notification_hook
0505_multi_agent.pyMulti(정적)agent_as_tool("researcher")로 coordinator 위임. MiniMax를 OpenAI 호환 base_url
0606_custom_provider.pyProvider(자작)API키·네트워크·SDK 없이 메서드 3개 MyEchoProvider. Protocol이 duck-typed임을 증명
0707_provider_settings.py설정 정규화canonical 8키(temperature/max_tokens…)를 provider 네이티브 kwarg로 번역. 비표준은 provider_passthrough
0909_agent_delegation.pyMulti(동적)make_delegate_tool() → 런타임에 researcher/writer/critic 선택 dispatch. reasoning_effort
1010_custom_endpoints.pyProvider 라우팅같은 OpenAIProvider로 local(ollama)/프록시/MiniMax/Zhipu/Moonshot 등 7개 엔드포인트
1212_history.py대화 상태(앱 책임)상태 자동관리 안 함. call(history=...)로 ①누적 ②가짜 주입 ③빈 리스트 3패턴
1313_inject.py런타임 개입inject_user_message()루프 도중 메시지 큐잉. hook/동시task 주입. HITL·websocket
1414_anthropic_reasoning.py고급 설정reasoning_effort: none~max를 Anthropic adaptive thinking으로 매핑. deep vs fast 대비
1515_multimodal_tool_results.pyTool 반환 확장tool이 ImageBlock/DocumentBlock 반환 → 모델이 이미지·PDF를 실제로 "본다"

examples에서 읽어낼 수 있는 설계 의도

1. 06·12·13이 API 키 없이 Mock/Echo provider로 동작 → provider 추상화가 진짜로 loose하다는 코드 증명. 이게 "core"라는 증거.

2. 05·09·10이 전부 OpenAI 호환 base_url 트릭 → MiniMax·Moonshot·Zhipu·Ollama 등 어떤 벤더든 OpenAIProvider(base_url=...) 한 줄로 편입.

3. 12·13이 "coreouto는 대화 상태·요약·개입을 자동으로 안 한다"를 반복 강조 → 바로 이 부분이 사용자가 자기 영역에 맞게 채워 넣어야 하는 빈칸(RAG·memory·요약·HITL).

4. 번호 순서 = 확장 난이도 곡선: 최소실행 → 단일 축 → 자작 provider → 오케스트레이션 → 운영 디테일.

멀티모달 provider 지원 매트릭스 (15_multimodal_tool_results.py)

providerimagesdocumentsvideoaudio
anthropic
openai-response
google (new SDK)
openai (chat)✘ → ValueError

4 결론 — 어디를 채워 "내 agent"로 만드는가

coreouto CORE loop + 5 확장 슬롯 도메인 행동→ Tool LLM 백엔드/게이트웨이→ Provider 역할별 agent 위계→ Preset + Multi-agent 관측·정책·개입→ Hook 대화·기억 관리→ history + RAG tool
그림 4. 사용자가 자기 영역에 맞게 채우는 확장 슬롯 매핑

docs/philosophy.md의 "우리가 일부러 넣지 않은 것" 목록이 곧 사용자 영역의 확장 슬롯 목록이다:

일부러 뺀 기능coreouto가 대신 주는 것사용자가 채우는 방법
Auto-summarizationtiming만 (ON_ITERATION)auto_summarize_hook에 요약 함수 주입
Agent-to-agent 통신agent_as_tool 위임 primitive원하는 토폴로지 직접 조립
자동 retrytool 에러를 LLM에 결과로 전달hook 또는 provider 래핑
RAG / memory / vector storetool 시스템검색·기억 tool 직접 작성
Streaming없음(전체 응답 수집)provider 레벨 또는 hook
Config 파일(YAML)Python 코드코드로 register
관측성(로깅/트레이싱)hook 시스템OpenTelemetry 등 hook 연결
핵심. coreouto는 각 슬롯에 대해 "timing(언제)"만 hook/primitive로 제공하고, "behavior(무엇을)"는 사용자에게 위임한다. 이것이 이 라이브러리가 완제품 agent가 아니라 맞춤형 agent를 찍어내는 core인 이유다.