VQ Agent Core: THINK, ACT, DECIDE — 감사 가능한 3계층 에이전트 루프
요약
VQ Agent Core는 에이전트의 복잡한 의사결정 과정을 THINK, ACT, DECIDE 세 개의 계층으로 분리하여 구조화했습니다. 이 아키텍처는 문법 제약형 함수 호출(grammar-constrained function calling)과 명확한 감사 추적을 통해 신뢰성과 투명성을 높입니다. 특히, DECIDE 계층은 Jev, Strands Decider 2B, Laya 등 다양한 목적별 결정 모델을 제공하여 에이전트의 판단력을 강화합니다.
핵심 포인트
- 에이전트를 THINK → ACT → DECIDE 세 단계로 분리한 아키텍처 제시
- ACT 계층은 문법 제약형 함수 호출(Needle 2)로 구조적 유효성을 보장
- 모든 이벤트는 JSONL 파일에 기록되어 완전한 감사 추적이 가능함
- DECIDE 계층에서 목적별 전문 결정 모델을 활용하여 판단력 강화
대부분의 에이전트 프레임워크는 하나의 LLM에게 조향 장치(steering wheel), 페달, 그리고 백미러를 동시에 맡깁니다. VQ Agent Core는 이 작업을 잠긴 루프(locked loop)로 세 개의 계층으로 분리합니다: THINK → ACT → DECIDE → THINK. 저는 코드를 통해 설명하고 싶습니다. 왜냐하면 여기서 흥미로운 결정들은 미적인 것이 아니라 아키텍처적인 것이기 때문입니다.
루프 (The loop)
agent.py는 Agent 클래스를 정의합니다. 입력은 THINK (think.py: understand())로 들어가고, 이는 ACT가 라우팅하는 actionable_request를 반환합니다. ACT는 needle_router.py입니다 — Needle 2 (cactus-needle==2.0.15), 문법 제약형 함수 호출(grammar-constrained function calling)을 사용하므로 도구 이름과 인수가 단순히 희망에 의존하는 것이 아니라 구조적으로 유효합니다. 만약 라우팅 신뢰도(routing confidence)가 router.confidence_threshold (config.yaml의 기본값 0.7)보다 낮거나 요청이 모호하면, 에이전트는 DECIDE 계층인 _decide_gate()를 호출합니다. 그런 다음 THINK의 compose()가 최종 답변을 작성합니다. 모든 이벤트는 runs/run-<timestamp>.jsonl에 기록됩니다. 실행당 하나의 파일로 전체 감사 추적(Full audit trail)이 가능합니다.
THINK는 정직하게 성능 저하됨 (THINK degrades honestly)
이것이 think.py에서 제가 가장 좋아하는 부분입니다. 로컬 모델에 접근할 수 없을 때:
understand()는 `via=
도구(Tools)는 tools/ 아래에 위치하며 레지스트리(tools/__init__.py, tools.get(name))를 사용합니다. 트리를 살펴보니 다음 파일들이 있었습니다: local_tools.py (draft_text), system_tools.py (get_time, sysinfo), web_tools.py (web_search, fetch_url), 그리고 notes_tools와 decide_tools. 의도적으로 작게 만든 것이며, 이는 키친 싱크(kitchen sink)가 아니라 확장되도록 설계된 레지스트리입니다. Needle 2는 데스크톱에서는 ctypes를 통해 인-프로세스(in-process)로 실행하거나, Termux/Android 경로인 경우 serve를 통해 needle CLI 바이너리로 HTTP를 통해 실행됩니다. 따라서 동일한 엔진이 휴대폰에서도 작동합니다.
DECIDE: 네 명의 심사위원, 하나의 인터페이스
deciders.py는 config.yaml에서 decider: 키에 따라 팩토리(factory)가 구성됩니다 (jev | local | auto):
- Jev (TypeSafe 클라우드) — 목적에 맞게 설계된 결정 모델로, 한 번의 호출로
choice / score / noul을 제공합니다. - Strands Decider 2B (로컬) — Qwen3.5-2B + 포인터 헤드(pointer head) + LoRA를 사용하며, Apache-2.0 라이선스입니다. 약 4.5GB의 가중치 크기를 가지며, 단일 순전파(forward pass) 과정에서 보정된 타입형 답변을 제공합니다.
- Laya (로컬) — 약 1GB 규모의 목적에 맞게 설계된 결정 모델입니다.
- 프롬프트 기반 로컬 LLM — 최후의 수단으로, 엄격한 JSON 프롬프팅을 사용하며, 보정되지는 않았지만 무료이고 오프라인에서 작동합니다.
`LocalDecider._backends = (
git clone https://github.com/Shivay00001/vq-agent-core.git
pip install -r requirements.txt
point config.yaml -> local_model.base_url at an OpenAI-compatible endpoint
...
fetch_model.sh는 모델 다운로드를 돕고, phone-setup.md는 Termux 경로를 다룹니다 (torch가 설치되지 않으므로 Needle serve 백엔드 + 프롬프트 기반 로컬 디사이더 필요). 이 저장소에는 또한 test_agent.py, test_strands_backend.py, 그리고 실제 E2E 실행에서 가져온 날짜별 엔지니어링 노트도 포함되어 있습니다.
솔직한 평가
작동하는 기반 구조이지, 제품은 아닙니다: UI가 없고, 관리되는 서비스도 없으며, THINK의 멀티모달 능력은 엔드포인트 뒤에 있는 모델에 의존하고, 로컬 DECIDE 백엔드는 PC급 머신을 원합니다. 하지만 중요한 속성들 — 정직한 폴백(fallback), 교체 가능한 보정된 판단 계층(calibrated judgment layer), 전체 실행 추적(full run tracing) — 은 README가 아니라 코드 안에 있습니다. 감사하거나 오프라인으로 실행해야 하는 에이전트를 구축하는 중이라면 읽어볼 가치가 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기