refactor(agent): MCP 기반 에이전트 아키텍처 재설계 — unified.md 분류기 제거, Gemini CLI + MCP 자율 도구 호출로 전환

This commit is contained in:
2026-03-12 16:52:20 +09:00
parent acc8533ef2
commit 246d2a26c4
10 changed files with 592 additions and 128 deletions

View File

@@ -14,12 +14,14 @@ from pathlib import Path
logger = logging.getLogger("variet.gemini")
ROLE_PROMPTS_DIR = Path(__file__).parent.parent / "prompts"
PROJECT_ROOT = Path(__file__).parent.parent
GEMINI_MODEL = "gemini-3-flash-preview"
# 역할별 thinkingBudget (토큰 단위)
# 512=가벼운 분류/요약, 4096=계획/검수, 8192=구현/비평
ROLE_THINKING: dict[str, int] = {
"unified": 512,
"agent": 4096,
"summarizer": 512,
"planner": 4096,
"coder": 8192,
@@ -57,28 +59,50 @@ class GeminiCaller:
"--approval-mode", "yolo"]
return ["gemini", "--model", GEMINI_MODEL, "--approval-mode", "yolo"]
# MCP 서버 설정 (홈 레벨에 등록)
_MCP_SERVERS = {
"anime": {
"command": str(Path(sys.executable)),
"args": [str(PROJECT_ROOT / "mcp_servers" / "anime_server.py")],
"cwd": str(PROJECT_ROOT),
"trust": True,
},
"infra": {
"command": str(Path(sys.executable)),
"args": [str(PROJECT_ROOT / "mcp_servers" / "infra_server.py")],
"cwd": str(PROJECT_ROOT),
"trust": True,
},
}
def _set_thinking_budget(self, role: str):
"""역할별 thinkingBudget을 settings.json에 반영."""
"""역할별 thinkingBudget + MCP 서버 설정을 settings.json에 반영."""
budget = ROLE_THINKING.get(role, DEFAULT_THINKING)
try:
if _SETTINGS_PATH.exists():
settings = json.loads(_SETTINGS_PATH.read_text(encoding="utf-8"))
else:
_SETTINGS_PATH.parent.mkdir(parents=True, exist_ok=True)
settings = {}
# modelConfigs.default.thinkingConfig.thinkingBudget 설정
# thinkingBudget
configs = settings.setdefault("modelConfigs", {})
default = configs.setdefault("default", {})
thinking = default.setdefault("thinkingConfig", {})
thinking["thinkingBudget"] = budget
# MCP 서버 (홈 레벨에 등록 — cwd와 무관하게 항상 사용 가능)
mcp_servers = settings.setdefault("mcpServers", {})
for name, config in self._MCP_SERVERS.items():
mcp_servers[name] = config
_SETTINGS_PATH.write_text(
json.dumps(settings, indent=2, ensure_ascii=False),
encoding="utf-8",
)
logger.debug(f"thinkingBudget={budget} for role={role}")
except Exception as e:
logger.warning(f"thinkingBudget 설정 실패 (role={role}): {e}")
logger.warning(f"settings.json 업데이트 실패 (role={role}): {e}")
def _clean_output(self, raw: str) -> str:
"""Gemini 출력에서 노이즈 라인 제거."""
@@ -190,16 +214,30 @@ class GeminiCaller:
else:
system_prompt = f"You are a {role}. Respond in Korean."
# 역할에 따라 다른 지시
if role == "agent":
# 범용 에이전트: 도구 사용 여부를 자율 판단
footer = (
f"프로젝트 루트: {cwd}\n"
f"모든 응답은 한국어로 작성하세요.\n"
f"파일 작업이 필요하면 직접 수행하고, 대화만 필요하면 바로 답변하세요."
)
else:
# coder 등 파일 작업 전용 역할
footer = (
f"프로젝트 루트: {cwd}\n"
f"파일을 직접 생성/수정하세요. 코드블록으로 출력하지 말고, 실제 파일로 저장하세요.\n"
f"작업 완료 후 변경한 파일 목록을 간단히 출력하세요.\n"
f"모든 응답, 주석, 문서는 반드시 한국어로 작성하세요."
)
full_input = (
f"=== SYSTEM INSTRUCTIONS ===\n"
f"{system_prompt}\n\n"
f"=== TASK ===\n"
f"{context}\n\n"
f"=== IMPORTANT ===\n"
f"프로젝트 루트: {cwd}\n"
f"파일을 직접 생성/수정하세요. 코드블록으로 출력하지 말고, 실제 파일로 저장하세요.\n"
f"작업 완료 후 변경한 파일 목록을 간단히 출력하세요.\n"
f"모든 응답, 주석, 문서는 반드시 한국어로 작성하세요."
f"{footer}"
)
try: