hdsp-jupyter-extension 2.0.11__py3-none-any.whl → 2.0.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_server/langchain/MULTI_AGENT_ARCHITECTURE.md +1114 -0
- agent_server/langchain/__init__.py +2 -2
- agent_server/langchain/agent.py +72 -33
- agent_server/langchain/agent_factory.py +400 -0
- agent_server/langchain/agent_prompts/__init__.py +25 -0
- agent_server/langchain/agent_prompts/athena_query_prompt.py +71 -0
- agent_server/langchain/agent_prompts/planner_prompt.py +85 -0
- agent_server/langchain/agent_prompts/python_developer_prompt.py +123 -0
- agent_server/langchain/agent_prompts/researcher_prompt.py +38 -0
- agent_server/langchain/custom_middleware.py +652 -195
- agent_server/langchain/hitl_config.py +34 -10
- agent_server/langchain/middleware/__init__.py +24 -0
- agent_server/langchain/middleware/code_history_middleware.py +412 -0
- agent_server/langchain/middleware/description_injector.py +150 -0
- agent_server/langchain/middleware/skill_middleware.py +298 -0
- agent_server/langchain/middleware/subagent_events.py +171 -0
- agent_server/langchain/middleware/subagent_middleware.py +329 -0
- agent_server/langchain/prompts.py +96 -101
- agent_server/langchain/skills/data_analysis.md +236 -0
- agent_server/langchain/skills/data_loading.md +158 -0
- agent_server/langchain/skills/inference.md +392 -0
- agent_server/langchain/skills/model_training.md +318 -0
- agent_server/langchain/skills/pyspark.md +352 -0
- agent_server/langchain/subagents/__init__.py +20 -0
- agent_server/langchain/subagents/base.py +173 -0
- agent_server/langchain/tools/__init__.py +3 -0
- agent_server/langchain/tools/jupyter_tools.py +58 -20
- agent_server/langchain/tools/lsp_tools.py +1 -1
- agent_server/langchain/tools/shared/__init__.py +26 -0
- agent_server/langchain/tools/shared/qdrant_search.py +175 -0
- agent_server/langchain/tools/tool_registry.py +219 -0
- agent_server/langchain/tools/workspace_tools.py +197 -0
- agent_server/routers/config.py +40 -1
- agent_server/routers/langchain_agent.py +818 -337
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/build_log.json +1 -1
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +7 -2
- hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js → hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js +1108 -179
- hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js.map +1 -0
- jupyter_ext/labextension/static/lib_index_js.58c1e128ba0b76f41f04.js → hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.5449ba3c7e25177d2987.js +3916 -8128
- hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.5449ba3c7e25177d2987.js.map +1 -0
- hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.9da31d1134a53b0c4af5.js → hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.a8e0b064eb9b1c1ff463.js +17 -17
- hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.a8e0b064eb9b1c1ff463.js.map +1 -0
- {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/METADATA +1 -1
- {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/RECORD +75 -51
- jupyter_ext/_version.py +1 -1
- jupyter_ext/handlers.py +59 -8
- jupyter_ext/labextension/build_log.json +1 -1
- jupyter_ext/labextension/package.json +7 -2
- jupyter_ext/labextension/static/{frontend_styles_index_js.2d9fb488c82498c45c2d.js → frontend_styles_index_js.037b3c8e5d6a92b63b16.js} +1108 -179
- jupyter_ext/labextension/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js.map +1 -0
- hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.58c1e128ba0b76f41f04.js → jupyter_ext/labextension/static/lib_index_js.5449ba3c7e25177d2987.js +3916 -8128
- jupyter_ext/labextension/static/lib_index_js.5449ba3c7e25177d2987.js.map +1 -0
- jupyter_ext/labextension/static/{remoteEntry.9da31d1134a53b0c4af5.js → remoteEntry.a8e0b064eb9b1c1ff463.js} +17 -17
- jupyter_ext/labextension/static/remoteEntry.a8e0b064eb9b1c1ff463.js.map +1 -0
- hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js.map +0 -1
- hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.58c1e128ba0b76f41f04.js.map +0 -1
- hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.9da31d1134a53b0c4af5.js.map +0 -1
- jupyter_ext/labextension/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js.map +0 -1
- jupyter_ext/labextension/static/lib_index_js.58c1e128ba0b76f41f04.js.map +0 -1
- jupyter_ext/labextension/static/remoteEntry.9da31d1134a53b0c4af5.js.map +0 -1
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/style.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js.24edcc52a1c014a8a5f0.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.19ecf6babe00caff6b8a.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js +0 -0
- {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.1f5038488cdfd8b3a85d.js.map +0 -0
- {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/WHEEL +0 -0
- {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
# Multi-Agent Architecture Design
|
|
2
|
+
|
|
3
|
+
## 목차
|
|
4
|
+
1. [개요](#개요)
|
|
5
|
+
2. [AS-IS vs TO-BE 비교](#as-is-vs-to-be-비교)
|
|
6
|
+
3. [TO-BE 아키텍처](#to-be-아키텍처)
|
|
7
|
+
4. [에이전트 정의](#에이전트-정의)
|
|
8
|
+
5. [구현 전략](#구현-전략)
|
|
9
|
+
6. [개발 계획](#개발-계획)
|
|
10
|
+
7. [참고 자료](#참고-자료)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 개요
|
|
15
|
+
|
|
16
|
+
### 배경
|
|
17
|
+
현재 HDSP Agent는 단일 에이전트가 모든 도구를 관리하는 모놀리식 구조입니다. 이는 다음과 같은 한계가 있습니다:
|
|
18
|
+
|
|
19
|
+
- **Context 비대화**: 모든 도구 설명과 대화 기록이 누적되어 context window 압박
|
|
20
|
+
- **프롬프트 복잡도**: 단일 프롬프트에 모든 도구 사용 지침 포함 필요
|
|
21
|
+
- **유지보수 어려움**: 새 기능 추가 시 전체 프롬프트 수정 필요
|
|
22
|
+
- **오류 전파**: 한 작업의 오류가 전체 세션에 영향
|
|
23
|
+
|
|
24
|
+
### 목표
|
|
25
|
+
LangChain의 **Subagent 패턴**과 **Deep Agents 라이브러리**를 벤치마킹하여 멀티 에이전트 아키텍처로 전환합니다.
|
|
26
|
+
|
|
27
|
+
> **참고**: Deep Agents 라이브러리는 직접 설치하지 않고, 핵심 패턴을 벤치마킹하여 자체 구현합니다.
|
|
28
|
+
> 이를 통해 프로젝트 의존성을 최소화하고 HDSP 특화 기능을 유연하게 확장할 수 있습니다.
|
|
29
|
+
|
|
30
|
+
**핵심 원칙**:
|
|
31
|
+
1. **Supervisor Pattern**: Planner가 메인 에이전트로서 서브에이전트들을 도구처럼 호출
|
|
32
|
+
2. **Context Isolation**: 각 서브에이전트는 독립적인 context에서 실행
|
|
33
|
+
3. **Stateless Subagents**: 서브에이전트는 상태를 유지하지 않음 (메인 에이전트가 관리)
|
|
34
|
+
4. **Human-in-the-Loop 유지**: 코드 실행 승인 등 기존 HITL 기능 유지
|
|
35
|
+
5. **Simple Agent Structure**: 최소한의 에이전트로 복잡도 관리 (Planner + 2개 서브에이전트)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## AS-IS vs TO-BE 비교
|
|
40
|
+
|
|
41
|
+
### AS-IS: 단일 에이전트 구조
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
45
|
+
│ Single Agent │
|
|
46
|
+
│ │
|
|
47
|
+
│ System Prompt (모든 도구 지침 포함) │
|
|
48
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
49
|
+
│ │ - 데이터 분석 가이드 │ │
|
|
50
|
+
│ │ - 파일 작업 지침 │ │
|
|
51
|
+
│ │ - 리소스 확인 규칙 │ │
|
|
52
|
+
│ │ - Todo 관리 규칙 │ │
|
|
53
|
+
│ │ - 출력 형식 지침 │ │
|
|
54
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
55
|
+
│ │
|
|
56
|
+
│ Tools (모든 도구가 직접 연결) │
|
|
57
|
+
│ ┌──────────┬──────────┬──────────┬──────────┬──────────┐ │
|
|
58
|
+
│ │jupyter │file │search │shell │resource │ │
|
|
59
|
+
│ │_cell │_tools │_tools │_tools │_tools │ │
|
|
60
|
+
│ └──────────┴──────────┴──────────┴──────────┴──────────┘ │
|
|
61
|
+
└─────────────────────────────────────────────────────────────┘
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**문제점**:
|
|
65
|
+
- 프롬프트 크기: ~2000+ tokens
|
|
66
|
+
- 도구 선택 복잡도: 8+ 도구 중 선택
|
|
67
|
+
- Context 누적으로 후반부 성능 저하
|
|
68
|
+
|
|
69
|
+
### TO-BE: 멀티 에이전트 구조 (간소화)
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
73
|
+
│ Planner Agent (Supervisor) │
|
|
74
|
+
│ │
|
|
75
|
+
│ System Prompt (계획 및 조율 전문) │
|
|
76
|
+
│ ┌───────────────────────────────────────────────────────────────┐ │
|
|
77
|
+
│ │ - 작업 분해 및 계획 수립 │ │
|
|
78
|
+
│ │ - 서브에이전트 선택 및 위임 │ │
|
|
79
|
+
│ │ - 리소스 확인 (check_resource_tool 직접 호출) │ │
|
|
80
|
+
│ │ - 결과 종합 및 요약 │ │
|
|
81
|
+
│ └───────────────────────────────────────────────────────────────┘ │
|
|
82
|
+
│ │
|
|
83
|
+
│ Tools │
|
|
84
|
+
│ ┌────────────┬────────────┬───────────────────────────────────┐ │
|
|
85
|
+
│ │write_todos │check_ │ task(agent_name, description) │ │
|
|
86
|
+
│ │ │resource │ │ │
|
|
87
|
+
│ └────────────┴────────────┴───────────────────────────────────┘ │
|
|
88
|
+
│ │ │
|
|
89
|
+
│ ┌───────────────┴───────────────┐ │
|
|
90
|
+
│ ▼ ▼ │
|
|
91
|
+
│ ┌───────────────────┐ ┌───────────────────┐ │
|
|
92
|
+
│ │ Python Developer │ │ Researcher Agent │ │
|
|
93
|
+
│ │ │ │ (READ-ONLY 검색) │ │
|
|
94
|
+
│ │ • jupyter_cell │ │ • search_nb │ │
|
|
95
|
+
│ │ • markdown │ │ • diagnostics │ │
|
|
96
|
+
│ │ • write_file │ │ │ │
|
|
97
|
+
│ │ • multiedit_file │ │ ─────────────────── │ │
|
|
98
|
+
│ │ ─────────────────── │ │ [공통 도구] │ │
|
|
99
|
+
│ │ [공통 도구] │ │ • read_file │ │
|
|
100
|
+
│ │ • read_file │ │ • exec_cmd │ │
|
|
101
|
+
│ │ • exec_cmd │ │ • qdrant_search │ │
|
|
102
|
+
│ │ │ └───────────────────┘ │
|
|
103
|
+
│ │ ─────────────────── │ │
|
|
104
|
+
│ │ [Nested Subagent] │ │
|
|
105
|
+
│ │ • task(athena_ │ │
|
|
106
|
+
│ │ query) │ │
|
|
107
|
+
│ └─────────┬─────────┘ │
|
|
108
|
+
│ │ │
|
|
109
|
+
│ ▼ │
|
|
110
|
+
│ ┌───────────────────┐ │
|
|
111
|
+
│ │ Athena Query Agent│ │
|
|
112
|
+
│ │ (SQL 생성 전문) │ │
|
|
113
|
+
│ │ │ │
|
|
114
|
+
│ │ • qdrant_search │ │
|
|
115
|
+
│ │ (DB/테이블 메타) │ │
|
|
116
|
+
│ │ │ │
|
|
117
|
+
│ │ Returns: SQL query│ │
|
|
118
|
+
│ └───────────────────┘ │
|
|
119
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**변경사항 (v1.3)**:
|
|
123
|
+
- ❌ Resource Manager 제거: `check_resource_tool`을 Planner가 직접 사용
|
|
124
|
+
- 🔄 FileSystem Agent → **Researcher Agent**: RAG, 웹 검색 등 확장을 위한 범용 이름
|
|
125
|
+
- ➕ **Athena Query Agent** 추가: Qdrant RAG 기반 Athena SQL 생성
|
|
126
|
+
- 🔗 **Nested Subagent 패턴**: Python Developer → Athena Query Agent 호출 가능
|
|
127
|
+
|
|
128
|
+
**장점**:
|
|
129
|
+
- 프롬프트 크기: Planner ~600 tokens, 각 서브에이전트 ~300 tokens
|
|
130
|
+
- 도구 선택: 각 에이전트는 2-5개 도구만 관리
|
|
131
|
+
- Context 격리로 성능 일관성 유지
|
|
132
|
+
- **자연스러운 워크플로우**: Python Developer가 필요시 Athena Query Agent 직접 호출
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## TO-BE 아키텍처
|
|
137
|
+
|
|
138
|
+
### 전체 구조도
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
142
|
+
│ Client (Jupyter Extension) │
|
|
143
|
+
└─────────────────────────────────┬───────────────────────────────────────┘
|
|
144
|
+
│ SSE
|
|
145
|
+
┌─────────────────────────────────▼───────────────────────────────────────┐
|
|
146
|
+
│ Agent Server (FastAPI) │
|
|
147
|
+
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
|
148
|
+
│ │ Router (langchain_agent.py) │ │
|
|
149
|
+
│ │ • stream_agent() / resume_agent() │ │
|
|
150
|
+
│ │ • Multi-agent orchestration │ │
|
|
151
|
+
│ └─────────────────────────────────┬───────────────────────────────┘ │
|
|
152
|
+
│ │ │
|
|
153
|
+
│ ┌─────────────────────────────────▼───────────────────────────────┐ │
|
|
154
|
+
│ │ Agent Factory (agent_factory.py) │ │
|
|
155
|
+
│ │ • create_planner_agent() │ │
|
|
156
|
+
│ │ • create_subagent(agent_type) │ │
|
|
157
|
+
│ │ • SubAgentMiddleware (deepagents 패턴 벤치마킹) │ │
|
|
158
|
+
│ └─────────────────────────────────┬───────────────────────────────┘ │
|
|
159
|
+
│ │ │
|
|
160
|
+
│ ┌────────────────────────┼────────────────────────┐ │
|
|
161
|
+
│ │ │ │ │
|
|
162
|
+
│ ┌────────▼────────┐ ┌──────────▼──────────┐ ┌────────▼────────┐ │
|
|
163
|
+
│ │ Planner Agent │ │ Subagent Registry │ │ State Manager │ │
|
|
164
|
+
│ │ (Supervisor) │ │ │ │ │ │
|
|
165
|
+
│ │ │ │ • python_developer │ │ • Thread state │ │
|
|
166
|
+
│ │ • Plan creation │ │ • researcher │ │ • Todos │ │
|
|
167
|
+
│ │ • Task dispatch │ │ • (extensible) │ │ • Checkpoints │ │
|
|
168
|
+
│ │ • Resource check│ │ │ │ │ │
|
|
169
|
+
│ │ • Summarization │ │ │ │ │ │
|
|
170
|
+
│ └────────┬────────┘ └─────────────────────┘ └─────────────────┘ │
|
|
171
|
+
│ │ │
|
|
172
|
+
│ │ task(agent_name, description) │
|
|
173
|
+
│ ▼ │
|
|
174
|
+
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
|
175
|
+
│ │ Subagent Execution Layer │ │
|
|
176
|
+
│ │ │ │
|
|
177
|
+
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
|
|
178
|
+
│ │ │ Python Developer │ │ Researcher Agent │ │ │
|
|
179
|
+
│ │ │ │ │ │ │ │
|
|
180
|
+
│ │ │ Tools: │ │ Tools: │ │ │
|
|
181
|
+
│ │ │ • jupyter_cell │ │ • read_file │ │ │
|
|
182
|
+
│ │ │ • markdown │ │ • write_file │ │ │
|
|
183
|
+
│ │ │ │ │ • search_nb │ │ │
|
|
184
|
+
│ │ │ │ │ • exec_cmd │ │ │
|
|
185
|
+
│ │ │ │ │ • (future: RAG) │ │ │
|
|
186
|
+
│ │ └─────────────────────┘ └─────────────────────┘ │ │
|
|
187
|
+
│ └──────────────────────────────────────────────────────────────────┘ │
|
|
188
|
+
│ │
|
|
189
|
+
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
|
190
|
+
│ │ Middleware Layer │ │
|
|
191
|
+
│ │ • SubAgentMiddleware (서브에이전트 호출 처리) │ │
|
|
192
|
+
│ │ • HumanInTheLoopMiddleware (HITL 유지) │ │
|
|
193
|
+
│ │ • TodoListMiddleware (Planner에만 적용) │ │
|
|
194
|
+
│ │ • SummarizationMiddleware (컨텍스트 관리) │ │
|
|
195
|
+
│ └──────────────────────────────────────────────────────────────────┘ │
|
|
196
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 데이터 흐름
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
User: "타이타닉 데이터를 분석하고 생존율 예측 모델을 만들어줘"
|
|
203
|
+
│
|
|
204
|
+
▼
|
|
205
|
+
┌───────────────────────────────────────────────────────────────────┐
|
|
206
|
+
│ Planner Agent │
|
|
207
|
+
│ │
|
|
208
|
+
│ 1. 작업 분해: │
|
|
209
|
+
│ write_todos([ │
|
|
210
|
+
│ {content: "데이터 로드 및 기본 정보 확인", status: "pending"}, │
|
|
211
|
+
│ {content: "데이터 정제 (결측치 처리)", status: "pending"}, │
|
|
212
|
+
│ {content: "EDA 수행", status: "pending"}, │
|
|
213
|
+
│ {content: "예측 모델 학습", status: "pending"}, │
|
|
214
|
+
│ {content: "작업 요약 및 다음단계 제시", status: "pending"} │
|
|
215
|
+
│ ]) │
|
|
216
|
+
│ │
|
|
217
|
+
│ 2. 리소스 확인 (Planner 직접 호출): │
|
|
218
|
+
│ check_resource_tool(files=["titanic.csv"]) │
|
|
219
|
+
│ → "titanic.csv: 60KB, 891 rows" │
|
|
220
|
+
│ │
|
|
221
|
+
│ 3. 서브에이전트 호출: │
|
|
222
|
+
│ task("python_developer", │
|
|
223
|
+
│ "titanic.csv를 로드하고 df.info(), df.head() 출력") │
|
|
224
|
+
│ │ │
|
|
225
|
+
└─────────────────────┼──────────────────────────────────────────────┘
|
|
226
|
+
▼
|
|
227
|
+
┌─────────────────────────┐
|
|
228
|
+
│ Python Developer │
|
|
229
|
+
│ │
|
|
230
|
+
│ jupyter_cell_tool( │
|
|
231
|
+
│ code=""" │
|
|
232
|
+
│ import pandas as pd │
|
|
233
|
+
│ df = pd.read_csv │
|
|
234
|
+
│ ('titanic.csv') │
|
|
235
|
+
│ print(df.info()) │
|
|
236
|
+
│ df.head() │
|
|
237
|
+
│ """ │
|
|
238
|
+
│ ) │
|
|
239
|
+
│ │
|
|
240
|
+
│ → [HITL: 사용자 승인] │
|
|
241
|
+
│ → execution_result │
|
|
242
|
+
└────────────┬────────────┘
|
|
243
|
+
│
|
|
244
|
+
▼
|
|
245
|
+
┌───────────────────────────────────────────────────────────────────┐
|
|
246
|
+
│ Planner Agent │
|
|
247
|
+
│ │
|
|
248
|
+
│ 4. 파일 검색 필요 시: │
|
|
249
|
+
│ task("researcher", "workspace에서 CSV 파일 목록 찾기") │
|
|
250
|
+
│ │ │
|
|
251
|
+
└─────────────────────┼──────────────────────────────────────────────┘
|
|
252
|
+
▼
|
|
253
|
+
┌─────────────────────────┐
|
|
254
|
+
│ Researcher Agent │
|
|
255
|
+
│ │
|
|
256
|
+
│ execute_command_tool( │
|
|
257
|
+
│ command="find . -name│
|
|
258
|
+
│ '*.csv' -type f" │
|
|
259
|
+
│ ) │
|
|
260
|
+
│ │
|
|
261
|
+
│ → "./data/titanic.csv"│
|
|
262
|
+
│ → "./data/test.csv" │
|
|
263
|
+
└────────────┬────────────┘
|
|
264
|
+
│
|
|
265
|
+
▼
|
|
266
|
+
┌───────────────────────────────────────────────────────────────────┐
|
|
267
|
+
│ Planner Agent │
|
|
268
|
+
│ │
|
|
269
|
+
│ 5. 진행 상황 업데이트: │
|
|
270
|
+
│ write_todos([ │
|
|
271
|
+
│ {content: "데이터 로드...", status: "completed"}, │
|
|
272
|
+
│ {content: "데이터 정제...", status: "in_progress"}, │
|
|
273
|
+
│ ... │
|
|
274
|
+
│ ]) │
|
|
275
|
+
│ │
|
|
276
|
+
│ 6. 계속 진행... │
|
|
277
|
+
└───────────────────────────────────────────────────────────────────┘
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 에이전트 정의
|
|
283
|
+
|
|
284
|
+
### 1. Planner Agent (Supervisor)
|
|
285
|
+
|
|
286
|
+
**역할**: 작업 계획, 리소스 확인, 서브에이전트 조율, 결과 종합
|
|
287
|
+
|
|
288
|
+
**System Prompt**:
|
|
289
|
+
```
|
|
290
|
+
You are a data science project planner and coordinator. Respond in Korean.
|
|
291
|
+
|
|
292
|
+
# Your Role
|
|
293
|
+
- Analyze user requests and break them into executable tasks
|
|
294
|
+
- Check resources BEFORE delegating data operations (use check_resource_tool)
|
|
295
|
+
- Delegate tasks to specialized subagents
|
|
296
|
+
- Track progress using write_todos
|
|
297
|
+
- Synthesize results and provide summaries
|
|
298
|
+
|
|
299
|
+
# Available Subagents
|
|
300
|
+
- python_developer: Python code execution in Jupyter notebooks (analysis, visualization, ML)
|
|
301
|
+
- Can internally call athena_query for Athena SQL generation
|
|
302
|
+
- researcher: File/workspace search, RAG queries, information gathering
|
|
303
|
+
|
|
304
|
+
Note: athena_query is NOT directly callable. Use python_developer for Athena data tasks.
|
|
305
|
+
|
|
306
|
+
# Workflow
|
|
307
|
+
1. Understand the request
|
|
308
|
+
2. Create a todo list with write_todos (always end with "작업 요약 및 다음단계 제시")
|
|
309
|
+
3. Check resources with check_resource_tool (for data loading tasks)
|
|
310
|
+
4. Delegate each task to the appropriate subagent using task()
|
|
311
|
+
5. Update todos as tasks complete
|
|
312
|
+
6. Provide final summary with next_items JSON
|
|
313
|
+
|
|
314
|
+
# Rules
|
|
315
|
+
- ALWAYS create todos before delegating
|
|
316
|
+
- Check file sizes with check_resource_tool BEFORE python_developer loads data
|
|
317
|
+
- Delegate ONE task at a time
|
|
318
|
+
- Wait for subagent result before next delegation
|
|
319
|
+
- Mark todos complete only after receiving subagent result
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Tools**:
|
|
323
|
+
| Tool | Description |
|
|
324
|
+
|------|-------------|
|
|
325
|
+
| `write_todos` | 작업 목록 관리 (TodoListMiddleware) |
|
|
326
|
+
| `check_resource_tool` | 파일 크기/메모리 확인 (리소스 체크) |
|
|
327
|
+
| `task` | 서브에이전트 호출 (SubAgentMiddleware) |
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
### 2. Python Developer Agent
|
|
332
|
+
|
|
333
|
+
**역할**: Python 코드 작성, 파일 수정, Jupyter 노트북 실행, **Athena 쿼리 실행**
|
|
334
|
+
|
|
335
|
+
**System Prompt**:
|
|
336
|
+
```
|
|
337
|
+
You are an expert Python developer specializing in data science and Jupyter notebooks.
|
|
338
|
+
|
|
339
|
+
# Your Capabilities
|
|
340
|
+
- Write and execute Python code for data analysis
|
|
341
|
+
- Read and modify Python/config files
|
|
342
|
+
- Create visualizations with matplotlib, seaborn, plotly
|
|
343
|
+
- Build machine learning models
|
|
344
|
+
- Add markdown documentation to notebooks
|
|
345
|
+
- Execute Athena queries (via athena_query subagent)
|
|
346
|
+
|
|
347
|
+
# Athena Query Workflow
|
|
348
|
+
When you need to query AWS Athena data:
|
|
349
|
+
1. Call task("athena_query", "description of what data you need")
|
|
350
|
+
2. Receive the SQL query string from athena_query agent
|
|
351
|
+
3. Execute the query using pyathena/boto3 in jupyter_cell_tool
|
|
352
|
+
|
|
353
|
+
Example:
|
|
354
|
+
sql = task("athena_query", "최근 7일간 일별 활성 사용자 수 조회")
|
|
355
|
+
# Then execute with pyathena:
|
|
356
|
+
jupyter_cell_tool(code="df = pd.read_sql(sql, athena_conn)")
|
|
357
|
+
|
|
358
|
+
# Guidelines
|
|
359
|
+
- Use English labels for plots (required for Korean text rendering issues)
|
|
360
|
+
- Handle large datasets efficiently (use chunking, sampling when needed)
|
|
361
|
+
- Always include error handling for file operations
|
|
362
|
+
- Use multiedit_file_tool for multiple changes in a single file
|
|
363
|
+
- For Athena queries, ALWAYS use task("athena_query", ...) first
|
|
364
|
+
|
|
365
|
+
# Output Format
|
|
366
|
+
Execute code using jupyter_cell_tool, add explanations using markdown_tool.
|
|
367
|
+
Return concise results back to the Planner.
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Tools**:
|
|
371
|
+
| Tool | Description |
|
|
372
|
+
|------|-------------|
|
|
373
|
+
| `jupyter_cell_tool` | Python 코드 실행 (HITL) |
|
|
374
|
+
| `markdown_tool` | 마크다운 셀 추가 |
|
|
375
|
+
| `read_file_tool` | 파일 읽기 (공통) |
|
|
376
|
+
| `write_file_tool` | 파일 쓰기 (HITL) |
|
|
377
|
+
| `multiedit_file_tool` | 다중 편집 (HITL) |
|
|
378
|
+
| `execute_command_tool` | 쉘 명령 실행 (HITL, 공통) |
|
|
379
|
+
| `task` | **Nested subagent 호출** (athena_query만 허용) |
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
### 3. Researcher Agent
|
|
384
|
+
|
|
385
|
+
**역할**: 정보 검색, 파일 탐색, 코드 분석, 향후 RAG 통합
|
|
386
|
+
|
|
387
|
+
> **확장성**: 이 에이전트는 현재 읽기 전용 검색/탐색을 담당하며, 향후 RAG (Retrieval-Augmented Generation),
|
|
388
|
+
> 웹 검색, 문서 검색 등 다양한 검색 기능을 추가할 수 있도록 범용적으로 설계되었습니다.
|
|
389
|
+
|
|
390
|
+
**System Prompt**:
|
|
391
|
+
```
|
|
392
|
+
You are a research and information specialist for data science workspaces.
|
|
393
|
+
|
|
394
|
+
# Your Capabilities
|
|
395
|
+
- Read files and analyze content (READ-ONLY)
|
|
396
|
+
- Search notebook cells for code patterns
|
|
397
|
+
- Execute shell commands for file discovery (find, grep, ls)
|
|
398
|
+
- Navigate workspace structure
|
|
399
|
+
- Analyze code for potential issues using LSP diagnostics
|
|
400
|
+
- (Future) RAG-based document search
|
|
401
|
+
- (Future) Web search for documentation
|
|
402
|
+
|
|
403
|
+
# Guidelines
|
|
404
|
+
- Validate paths to prevent directory traversal
|
|
405
|
+
- Use appropriate encoding for different file types
|
|
406
|
+
- Summarize large file contents instead of full output
|
|
407
|
+
- Use find/grep commands for efficient file search
|
|
408
|
+
- Return concise, actionable results
|
|
409
|
+
|
|
410
|
+
# Important
|
|
411
|
+
- You are READ-ONLY: Do NOT modify files (use python_developer for that)
|
|
412
|
+
- Focus on gathering and synthesizing information
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Tools**:
|
|
416
|
+
| Tool | Description |
|
|
417
|
+
|------|-------------|
|
|
418
|
+
| `read_file_tool` | 파일 읽기 (공통) |
|
|
419
|
+
| `search_notebook_cells_tool` | 노트북 셀 검색 |
|
|
420
|
+
| `execute_command_tool` | 쉘 명령 실행 (HITL) - 검색용 |
|
|
421
|
+
| `diagnostics_tool` | LSP 진단 (코드 오류 확인) |
|
|
422
|
+
| `qdrant_search_tool` | Qdrant RAG 검색 (공통) |
|
|
423
|
+
| *(Future)* `web_search_tool` | 웹 검색 |
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
### 4. Athena Query Agent
|
|
428
|
+
|
|
429
|
+
**역할**: Qdrant RAG를 활용한 Athena SQL 쿼리 생성
|
|
430
|
+
|
|
431
|
+
> **호출 방식**: Planner가 직접 호출하지 않고, **Python Developer가 필요시 호출**합니다.
|
|
432
|
+
> 이는 SQL 생성 → Python 실행이라는 자연스러운 워크플로우를 지원합니다.
|
|
433
|
+
|
|
434
|
+
**System Prompt**:
|
|
435
|
+
```
|
|
436
|
+
You are an AWS Athena SQL expert. Generate optimized Athena queries based on user requirements.
|
|
437
|
+
|
|
438
|
+
# Your Capabilities
|
|
439
|
+
- Search database/table metadata using Qdrant RAG
|
|
440
|
+
- Generate Athena-compatible SQL queries (Presto/Trino syntax)
|
|
441
|
+
- Optimize queries for performance (partitioning, predicate pushdown)
|
|
442
|
+
|
|
443
|
+
# Workflow
|
|
444
|
+
1. Receive query requirement from Python Developer
|
|
445
|
+
2. Search Qdrant for relevant database/table metadata
|
|
446
|
+
3. Generate optimized Athena SQL query
|
|
447
|
+
4. Return ONLY the SQL query string (no explanation needed)
|
|
448
|
+
|
|
449
|
+
# Guidelines
|
|
450
|
+
- Always use fully qualified table names: database.table
|
|
451
|
+
- Include appropriate WHERE clauses for partitioned columns
|
|
452
|
+
- Use LIMIT for exploratory queries
|
|
453
|
+
- Handle NULL values appropriately
|
|
454
|
+
- Use CAST for type conversions
|
|
455
|
+
|
|
456
|
+
# Output Format
|
|
457
|
+
Return ONLY the SQL query as a string. Python Developer will execute it.
|
|
458
|
+
Example: "SELECT * FROM my_database.my_table WHERE partition_date = '2024-01-01' LIMIT 100"
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
**Tools**:
|
|
462
|
+
| Tool | Description |
|
|
463
|
+
|------|-------------|
|
|
464
|
+
| `qdrant_search_tool` | Qdrant에서 DB/테이블 메타데이터 검색 |
|
|
465
|
+
|
|
466
|
+
**Qdrant RAG 데이터 구조** (예시):
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"database": "analytics",
|
|
470
|
+
"table": "user_events",
|
|
471
|
+
"columns": [
|
|
472
|
+
{"name": "user_id", "type": "string", "description": "사용자 고유 ID"},
|
|
473
|
+
{"name": "event_type", "type": "string", "description": "이벤트 유형"},
|
|
474
|
+
{"name": "event_time", "type": "timestamp", "description": "이벤트 발생 시간"},
|
|
475
|
+
{"name": "partition_date", "type": "string", "partition": true, "description": "파티션 날짜 (YYYY-MM-DD)"}
|
|
476
|
+
],
|
|
477
|
+
"description": "사용자 행동 이벤트 로그 테이블",
|
|
478
|
+
"sample_queries": [
|
|
479
|
+
"SELECT user_id, COUNT(*) FROM analytics.user_events GROUP BY user_id"
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
### Nested Subagent 호출 패턴
|
|
487
|
+
|
|
488
|
+
Python Developer가 Athena Query Agent를 호출하는 워크플로우:
|
|
489
|
+
|
|
490
|
+
```
|
|
491
|
+
┌───────────────────────────────────────────────────────────────────┐
|
|
492
|
+
│ Python Developer Agent │
|
|
493
|
+
│ │
|
|
494
|
+
│ User Task: "최근 7일간 일별 활성 사용자 수를 조회해줘" │
|
|
495
|
+
│ │
|
|
496
|
+
│ 1. Athena 쿼리 필요 인식 │
|
|
497
|
+
│ task("athena_query", │
|
|
498
|
+
│ "최근 7일간 일별 활성 사용자 수 조회 쿼리 생성") │
|
|
499
|
+
│ │ │
|
|
500
|
+
└─────────────────────┼─────────────────────────────────────────────┘
|
|
501
|
+
▼
|
|
502
|
+
┌─────────────────────────────┐
|
|
503
|
+
│ Athena Query Agent │
|
|
504
|
+
│ │
|
|
505
|
+
│ 1. qdrant_search_tool( │
|
|
506
|
+
│ "사용자 활성 이벤트" │
|
|
507
|
+
│ ) │
|
|
508
|
+
│ → analytics.user_events│
|
|
509
|
+
│ │
|
|
510
|
+
│ 2. Generate SQL: │
|
|
511
|
+
│ """ │
|
|
512
|
+
│ SELECT │
|
|
513
|
+
│ partition_date, │
|
|
514
|
+
│ COUNT(DISTINCT user_id) │
|
|
515
|
+
│ AS dau │
|
|
516
|
+
│ FROM analytics.user_events│
|
|
517
|
+
│ WHERE partition_date >= │
|
|
518
|
+
│ DATE_FORMAT( │
|
|
519
|
+
│ DATE_ADD('day', -7, │
|
|
520
|
+
│ CURRENT_DATE), '%Y-%m-%d')│
|
|
521
|
+
│ GROUP BY partition_date │
|
|
522
|
+
│ ORDER BY partition_date │
|
|
523
|
+
│ """ │
|
|
524
|
+
│ │
|
|
525
|
+
│ → Return SQL string │
|
|
526
|
+
└────────────┬────────────────┘
|
|
527
|
+
│
|
|
528
|
+
▼
|
|
529
|
+
┌───────────────────────────────────────────────────────────────────┐
|
|
530
|
+
│ Python Developer Agent │
|
|
531
|
+
│ │
|
|
532
|
+
│ 2. SQL 수신 후 Python 코드로 실행 │
|
|
533
|
+
│ jupyter_cell_tool(code=""" │
|
|
534
|
+
│ import boto3 │
|
|
535
|
+
│ import pandas as pd │
|
|
536
|
+
│ from pyathena import connect │
|
|
537
|
+
│ │
|
|
538
|
+
│ conn = connect(s3_staging_dir='s3://my-bucket/athena-results/',│
|
|
539
|
+
│ region_name='ap-northeast-2') │
|
|
540
|
+
│ │
|
|
541
|
+
│ query = ''' │
|
|
542
|
+
│ SELECT partition_date, COUNT(DISTINCT user_id) AS dau │
|
|
543
|
+
│ FROM analytics.user_events │
|
|
544
|
+
│ WHERE partition_date >= DATE_FORMAT(...) │
|
|
545
|
+
│ GROUP BY partition_date │
|
|
546
|
+
│ ORDER BY partition_date │
|
|
547
|
+
│ ''' │
|
|
548
|
+
│ │
|
|
549
|
+
│ df = pd.read_sql(query, conn) │
|
|
550
|
+
│ df │
|
|
551
|
+
│ """) │
|
|
552
|
+
│ │
|
|
553
|
+
│ 3. 결과 반환 to Planner │
|
|
554
|
+
└───────────────────────────────────────────────────────────────────┘
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## 구현 전략
|
|
560
|
+
|
|
561
|
+
### Phase 1: Deep Agents 패턴 벤치마킹 및 프로토타입
|
|
562
|
+
|
|
563
|
+
**목표**: Deep Agents의 SubAgentMiddleware 구현 방식을 벤치마킹하여 자체 구현
|
|
564
|
+
|
|
565
|
+
> **참고**: Deep Agents 라이브러리를 직접 설치하지 않고 핵심 패턴만 차용합니다.
|
|
566
|
+
> GitHub: https://github.com/langchain-ai/deepagents
|
|
567
|
+
|
|
568
|
+
**Deep Agents 핵심 패턴 (벤치마킹)**:
|
|
569
|
+
```python
|
|
570
|
+
# Deep Agents의 SubAgentMiddleware 핵심 패턴:
|
|
571
|
+
# 1. 서브에이전트를 task(name, description) 도구로 호출
|
|
572
|
+
# 2. 각 서브에이전트는 독립적인 context에서 실행
|
|
573
|
+
# 3. 결과는 문자열로 요약되어 메인 에이전트에 반환
|
|
574
|
+
# 4. HITL interrupt는 메인 에이전트로 bubbling up
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**작업**:
|
|
578
|
+
1. Deep Agents GitHub 소스 코드 분석 (SubAgentMiddleware, task tool)
|
|
579
|
+
2. SubAgentMiddleware 핵심 로직을 자체 구현
|
|
580
|
+
3. 프로토타입 구현 (Planner + 2개 서브에이전트)
|
|
581
|
+
|
|
582
|
+
**핵심 코드 구조** (자체 구현):
|
|
583
|
+
|
|
584
|
+
```python
|
|
585
|
+
# agent_factory.py (신규)
|
|
586
|
+
|
|
587
|
+
from typing import Dict, Any, List
|
|
588
|
+
from langchain.agents import create_agent
|
|
589
|
+
from langchain.tools import tool
|
|
590
|
+
|
|
591
|
+
# 공통 도구 정의 (모듈화)
|
|
592
|
+
SHARED_TOOLS = {
|
|
593
|
+
"read_file_tool", # 파일 읽기 - 모든 에이전트 공통
|
|
594
|
+
"execute_command_tool", # 쉘 명령 - Python Developer, Researcher 공통
|
|
595
|
+
"qdrant_search_tool", # Qdrant RAG - Researcher, Athena Query 공통
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
# 서브에이전트 정의 (3-agent 구조 + Nested 호출)
|
|
599
|
+
SUBAGENT_CONFIGS = {
|
|
600
|
+
"python_developer": {
|
|
601
|
+
"name": "python_developer",
|
|
602
|
+
"description": "Execute Python code, read/write files, run shell commands. Can call athena_query agent.",
|
|
603
|
+
"system_prompt": PYTHON_DEVELOPER_PROMPT,
|
|
604
|
+
"tools": ["jupyter_cell_tool", "markdown_tool",
|
|
605
|
+
"read_file_tool", "write_file_tool", "multiedit_file_tool",
|
|
606
|
+
"execute_command_tool"],
|
|
607
|
+
# Nested subagent 호출 허용
|
|
608
|
+
"can_call_subagents": ["athena_query"], # Athena Query Agent만 호출 가능
|
|
609
|
+
},
|
|
610
|
+
"researcher": {
|
|
611
|
+
"name": "researcher",
|
|
612
|
+
"description": "Search files, explore workspace, Qdrant RAG search (READ-ONLY).",
|
|
613
|
+
"system_prompt": RESEARCHER_PROMPT,
|
|
614
|
+
"tools": ["read_file_tool", "search_notebook_cells_tool",
|
|
615
|
+
"execute_command_tool", "diagnostics_tool",
|
|
616
|
+
"qdrant_search_tool"], # Qdrant RAG 검색 가능
|
|
617
|
+
},
|
|
618
|
+
"athena_query": {
|
|
619
|
+
"name": "athena_query",
|
|
620
|
+
"description": "Generate Athena SQL queries using Qdrant RAG for DB/table metadata",
|
|
621
|
+
"system_prompt": ATHENA_QUERY_PROMPT,
|
|
622
|
+
"tools": ["qdrant_search_tool"],
|
|
623
|
+
# Python Developer에 의해서만 호출됨 (Planner 직접 호출 X)
|
|
624
|
+
"callable_by": ["python_developer"],
|
|
625
|
+
},
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
def create_subagent(agent_type: str, llm_config: Dict[str, Any]):
|
|
629
|
+
"""서브에이전트 생성 (deepagents 패턴 벤치마킹)"""
|
|
630
|
+
config = SUBAGENT_CONFIGS[agent_type]
|
|
631
|
+
llm = create_llm(llm_config)
|
|
632
|
+
tools = [get_tool(name) for name in config["tools"]]
|
|
633
|
+
|
|
634
|
+
return create_agent(
|
|
635
|
+
model=llm,
|
|
636
|
+
tools=tools,
|
|
637
|
+
prompt=config["system_prompt"],
|
|
638
|
+
# 서브에이전트는 미들웨어 최소화
|
|
639
|
+
middleware=[HumanInTheLoopMiddleware(...)], # HITL 유지
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
@tool
|
|
643
|
+
def task(agent_name: str, description: str) -> str:
|
|
644
|
+
"""
|
|
645
|
+
Delegate a task to a specialized subagent.
|
|
646
|
+
|
|
647
|
+
Available agents:
|
|
648
|
+
- python_developer: Python code execution for data analysis, visualization, ML
|
|
649
|
+
- researcher: File search, workspace exploration, code pattern search
|
|
650
|
+
|
|
651
|
+
Args:
|
|
652
|
+
agent_name: Name of the subagent to invoke
|
|
653
|
+
description: Detailed task description for the subagent
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
Result from the subagent execution
|
|
657
|
+
"""
|
|
658
|
+
if agent_name not in SUBAGENT_CONFIGS:
|
|
659
|
+
return f"Error: Unknown agent '{agent_name}'. Available: {list(SUBAGENT_CONFIGS.keys())}"
|
|
660
|
+
|
|
661
|
+
# 서브에이전트 생성 (또는 캐시에서 가져오기)
|
|
662
|
+
subagent = create_subagent(agent_name, current_llm_config)
|
|
663
|
+
|
|
664
|
+
# 서브에이전트 실행
|
|
665
|
+
result = subagent.invoke({
|
|
666
|
+
"messages": [{"role": "user", "content": description}]
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
# 결과 추출 및 반환
|
|
670
|
+
return result["messages"][-1].content
|
|
671
|
+
|
|
672
|
+
def create_planner_agent(llm_config: Dict[str, Any], ...):
|
|
673
|
+
"""Planner (Supervisor) 에이전트 생성"""
|
|
674
|
+
llm = create_llm(llm_config)
|
|
675
|
+
|
|
676
|
+
tools = [
|
|
677
|
+
check_resource_tool, # 리소스 확인 (Planner 직접 사용)
|
|
678
|
+
task, # 서브에이전트 호출 도구
|
|
679
|
+
]
|
|
680
|
+
|
|
681
|
+
middleware = [
|
|
682
|
+
TodoListMiddleware(...), # Todo 관리
|
|
683
|
+
SubAgentMiddleware(...), # 서브에이전트 미들웨어
|
|
684
|
+
SummarizationMiddleware(...),
|
|
685
|
+
ModelCallLimitMiddleware(run_limit=50), # 멀티에이전트는 호출 횟수 증가
|
|
686
|
+
]
|
|
687
|
+
|
|
688
|
+
return create_agent(
|
|
689
|
+
model=llm,
|
|
690
|
+
tools=tools,
|
|
691
|
+
prompt=PLANNER_SYSTEM_PROMPT,
|
|
692
|
+
middleware=middleware,
|
|
693
|
+
checkpointer=checkpointer,
|
|
694
|
+
)
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Phase 2: HITL 통합
|
|
698
|
+
|
|
699
|
+
**목표**: 서브에이전트의 HITL 도구 호출이 Planner를 통해 클라이언트까지 전파되도록 구현
|
|
700
|
+
|
|
701
|
+
**과제**:
|
|
702
|
+
- 서브에이전트의 interrupt가 Planner로 bubbling up
|
|
703
|
+
- 클라이언트 UI에서 어떤 서브에이전트의 요청인지 표시
|
|
704
|
+
- resume 시 올바른 서브에이전트로 전달
|
|
705
|
+
|
|
706
|
+
**구현 방안**:
|
|
707
|
+
|
|
708
|
+
```python
|
|
709
|
+
# 서브에이전트 실행 시 interrupt 처리
|
|
710
|
+
@tool
|
|
711
|
+
def task(agent_name: str, description: str, runtime: ToolRuntime) -> Command:
|
|
712
|
+
"""서브에이전트 호출 (interrupt 전파 지원)"""
|
|
713
|
+
subagent = create_subagent(agent_name, current_llm_config)
|
|
714
|
+
|
|
715
|
+
try:
|
|
716
|
+
# 스트리밍 실행으로 interrupt 감지
|
|
717
|
+
for step in subagent.stream({"messages": [{"role": "user", "content": description}]}):
|
|
718
|
+
if "__interrupt__" in step:
|
|
719
|
+
# interrupt를 Planner 레벨로 전파
|
|
720
|
+
return Command(
|
|
721
|
+
interrupt={
|
|
722
|
+
"subagent": agent_name,
|
|
723
|
+
"original_interrupt": step["__interrupt__"],
|
|
724
|
+
}
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
# 정상 완료
|
|
728
|
+
result = step["messages"][-1].content
|
|
729
|
+
return result
|
|
730
|
+
|
|
731
|
+
except Exception as e:
|
|
732
|
+
return f"Error in {agent_name}: {str(e)}"
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Phase 3: UI 업데이트
|
|
736
|
+
|
|
737
|
+
**목표**: 멀티에이전트 상태를 UI에 표시
|
|
738
|
+
|
|
739
|
+
**변경사항**:
|
|
740
|
+
- 현재 활성 에이전트 표시 (Planner / Python Developer / ...)
|
|
741
|
+
- 서브에이전트 실행 상태 표시
|
|
742
|
+
- HITL 요청 시 서브에이전트 컨텍스트 표시
|
|
743
|
+
|
|
744
|
+
```typescript
|
|
745
|
+
// AgentPanel.tsx 변경
|
|
746
|
+
interface AgentStatus {
|
|
747
|
+
activeAgent: 'planner' | 'python_developer' | 'filesystem' | 'resource_manager';
|
|
748
|
+
currentTask?: string;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// SSE 이벤트 추가
|
|
752
|
+
// event: agent_switch
|
|
753
|
+
// data: { agent: "python_developer", task: "Load titanic.csv" }
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
### Phase 4: 최적화 및 확장
|
|
757
|
+
|
|
758
|
+
**목표**: 성능 최적화 및 추가 서브에이전트
|
|
759
|
+
|
|
760
|
+
**작업**:
|
|
761
|
+
1. 서브에이전트 인스턴스 캐싱
|
|
762
|
+
2. 병렬 서브에이전트 실행 지원 (선택적)
|
|
763
|
+
3. 추가 서브에이전트 (필요 시):
|
|
764
|
+
- `code_reviewer`: 코드 리뷰 및 리팩토링 제안
|
|
765
|
+
- `documentation`: 문서화 전문
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
## 개발 계획
|
|
770
|
+
|
|
771
|
+
### Sprint 1: 기반 구축
|
|
772
|
+
|
|
773
|
+
| Task | Description | Priority |
|
|
774
|
+
|------|-------------|----------|
|
|
775
|
+
| 1.1 | Deep Agents GitHub 소스 코드 분석 (벤치마킹) | P0 |
|
|
776
|
+
| 1.2 | SubAgentMiddleware 핵심 패턴 추출 | P0 |
|
|
777
|
+
| 1.3 | `agent_factory.py` 모듈 생성 | P0 |
|
|
778
|
+
| 1.4 | `task` 도구 기본 구현 | P0 |
|
|
779
|
+
| 1.5 | Planner 에이전트 프롬프트 작성 | P1 |
|
|
780
|
+
|
|
781
|
+
**산출물**:
|
|
782
|
+
- `agent_factory.py` with `create_planner_agent()`, `create_subagent()`
|
|
783
|
+
- Basic `task` tool implementation
|
|
784
|
+
- Deep Agents 패턴 분석 문서
|
|
785
|
+
|
|
786
|
+
### Sprint 2: 서브에이전트 구현
|
|
787
|
+
|
|
788
|
+
| Task | Description | Priority |
|
|
789
|
+
|------|-------------|----------|
|
|
790
|
+
| 2.1 | Python Developer 서브에이전트 구현 | P0 |
|
|
791
|
+
| 2.2 | Researcher 서브에이전트 구현 | P0 |
|
|
792
|
+
| 2.3 | 서브에이전트 프롬프트 최적화 | P1 |
|
|
793
|
+
| 2.4 | 단위 테스트 작성 | P1 |
|
|
794
|
+
|
|
795
|
+
**산출물**:
|
|
796
|
+
- 2개 서브에이전트 구현 완료
|
|
797
|
+
- 각 서브에이전트 테스트 케이스
|
|
798
|
+
|
|
799
|
+
### Sprint 3: HITL 통합 (1주)
|
|
800
|
+
|
|
801
|
+
| Task | Description | Priority |
|
|
802
|
+
|------|-------------|----------|
|
|
803
|
+
| 3.1 | 서브에이전트 interrupt 전파 구현 | P0 |
|
|
804
|
+
| 3.2 | Router 수정 (멀티에이전트 지원) | P0 |
|
|
805
|
+
| 3.3 | resume_agent 멀티에이전트 처리 | P0 |
|
|
806
|
+
| 3.4 | 기존 HITL 테스트 케이스 통과 확인 | P1 |
|
|
807
|
+
|
|
808
|
+
**산출물**:
|
|
809
|
+
- HITL이 서브에이전트에서도 정상 작동
|
|
810
|
+
- 기존 기능 회귀 테스트 통과
|
|
811
|
+
|
|
812
|
+
### Sprint 4: UI 및 최적화 (1주)
|
|
813
|
+
|
|
814
|
+
| Task | Description | Priority |
|
|
815
|
+
|------|-------------|----------|
|
|
816
|
+
| 4.1 | UI에 활성 에이전트 표시 | P1 |
|
|
817
|
+
| 4.2 | SSE 이벤트 추가 (agent_switch) | P1 |
|
|
818
|
+
| 4.3 | 서브에이전트 캐싱 구현 | P2 |
|
|
819
|
+
| 4.4 | 성능 벤치마크 | P2 |
|
|
820
|
+
| 4.5 | 문서화 업데이트 | P1 |
|
|
821
|
+
|
|
822
|
+
**산출물**:
|
|
823
|
+
- 멀티에이전트 UI 완성
|
|
824
|
+
- 성능 비교 리포트
|
|
825
|
+
- 업데이트된 ARCHITECTURE.md
|
|
826
|
+
|
|
827
|
+
---
|
|
828
|
+
|
|
829
|
+
## 파일 구조 (TO-BE)
|
|
830
|
+
|
|
831
|
+
```
|
|
832
|
+
agent_server/langchain/
|
|
833
|
+
├── __init__.py
|
|
834
|
+
├── agent.py # 기존 (deprecated, 호환성 유지)
|
|
835
|
+
├── agent_factory.py # [NEW] 에이전트 팩토리
|
|
836
|
+
├── subagents/ # [NEW] 서브에이전트 정의
|
|
837
|
+
│ ├── __init__.py
|
|
838
|
+
│ ├── base.py # 서브에이전트 기본 클래스
|
|
839
|
+
│ ├── python_developer.py # Python/Jupyter 코드 실행 (can call athena_query)
|
|
840
|
+
│ ├── researcher.py # 검색/탐색 (READ-ONLY)
|
|
841
|
+
│ └── athena_query.py # [NEW] Athena SQL 생성 (Qdrant RAG)
|
|
842
|
+
├── middleware/ # [NEW] 미들웨어 리팩토링
|
|
843
|
+
│ ├── __init__.py
|
|
844
|
+
│ ├── subagent_middleware.py # SubAgentMiddleware (deepagents 벤치마킹)
|
|
845
|
+
│ ├── hitl_middleware.py
|
|
846
|
+
│ ├── todo_middleware.py
|
|
847
|
+
│ └── custom_middleware.py # 기존 커스텀 미들웨어
|
|
848
|
+
├── prompts/ # [NEW] 프롬프트 분리
|
|
849
|
+
│ ├── __init__.py
|
|
850
|
+
│ ├── planner_prompt.py # Planner (Supervisor) 프롬프트
|
|
851
|
+
│ ├── python_developer_prompt.py
|
|
852
|
+
│ ├── researcher_prompt.py # Researcher 에이전트 프롬프트
|
|
853
|
+
│ └── athena_query_prompt.py # [NEW] Athena Query 에이전트 프롬프트
|
|
854
|
+
├── tools/ # [REFACTOR] 도구 모듈화
|
|
855
|
+
│ ├── __init__.py
|
|
856
|
+
│ ├── shared/ # [NEW] 공통 도구 (여러 에이전트가 공유)
|
|
857
|
+
│ │ ├── __init__.py
|
|
858
|
+
│ │ ├── read_file.py # read_file_tool
|
|
859
|
+
│ │ ├── execute_command.py # execute_command_tool
|
|
860
|
+
│ │ └── qdrant_search.py # qdrant_search_tool (Researcher, Athena Query 공통)
|
|
861
|
+
│ ├── planner/ # [NEW] Planner 전용 도구
|
|
862
|
+
│ │ ├── __init__.py
|
|
863
|
+
│ │ └── check_resource.py # check_resource_tool
|
|
864
|
+
│ ├── python_developer/ # [NEW] Python Developer 전용 도구
|
|
865
|
+
│ │ ├── __init__.py
|
|
866
|
+
│ │ ├── jupyter_cell.py # jupyter_cell_tool
|
|
867
|
+
│ │ ├── markdown.py # markdown_tool
|
|
868
|
+
│ │ ├── write_file.py # write_file_tool
|
|
869
|
+
│ │ └── multiedit_file.py # multiedit_file_tool
|
|
870
|
+
│ └── researcher/ # [NEW] Researcher 전용 도구
|
|
871
|
+
│ ├── __init__.py
|
|
872
|
+
│ ├── search_notebook.py # search_notebook_cells_tool
|
|
873
|
+
│ └── diagnostics.py # diagnostics_tool
|
|
874
|
+
├── llm_factory.py # 기존 유지
|
|
875
|
+
├── state.py # 기존 유지
|
|
876
|
+
└── logging_utils.py # 기존 유지
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
### 도구 모듈화 설계
|
|
880
|
+
|
|
881
|
+
```python
|
|
882
|
+
# tools/shared/__init__.py
|
|
883
|
+
"""공통 도구 - 여러 에이전트가 공유"""
|
|
884
|
+
from .read_file import read_file_tool
|
|
885
|
+
from .execute_command import execute_command_tool
|
|
886
|
+
|
|
887
|
+
SHARED_TOOLS = [read_file_tool, execute_command_tool]
|
|
888
|
+
|
|
889
|
+
# tools/__init__.py
|
|
890
|
+
"""에이전트별 도구 조합"""
|
|
891
|
+
from .shared import SHARED_TOOLS
|
|
892
|
+
from .planner import check_resource_tool
|
|
893
|
+
from .python_developer import jupyter_cell_tool, markdown_tool, write_file_tool, multiedit_file_tool
|
|
894
|
+
from .researcher import search_notebook_cells_tool, diagnostics_tool
|
|
895
|
+
|
|
896
|
+
def get_tools_for_agent(agent_type: str, include_nested_task: bool = False) -> list:
|
|
897
|
+
"""에이전트 타입별 도구 목록 반환"""
|
|
898
|
+
base_tools = list(SHARED_TOOLS) # 공통 도구 복사
|
|
899
|
+
|
|
900
|
+
if agent_type == "planner":
|
|
901
|
+
return [check_resource_tool] # Planner는 task tool만 사용 (agent_factory에서 추가)
|
|
902
|
+
elif agent_type == "python_developer":
|
|
903
|
+
tools = base_tools + [jupyter_cell_tool, markdown_tool, write_file_tool, multiedit_file_tool]
|
|
904
|
+
if include_nested_task:
|
|
905
|
+
# Python Developer는 athena_query만 호출 가능한 제한된 task tool 추가
|
|
906
|
+
tools.append(create_limited_task_tool(allowed_agents=["athena_query"]))
|
|
907
|
+
return tools
|
|
908
|
+
elif agent_type == "researcher":
|
|
909
|
+
return base_tools + [search_notebook_cells_tool, diagnostics_tool, qdrant_search_tool]
|
|
910
|
+
elif agent_type == "athena_query":
|
|
911
|
+
return [qdrant_search_tool] # Athena Query는 Qdrant 검색만 사용 (공통 도구에서 가져옴)
|
|
912
|
+
else:
|
|
913
|
+
raise ValueError(f"Unknown agent type: {agent_type}")
|
|
914
|
+
|
|
915
|
+
def create_limited_task_tool(allowed_agents: list):
|
|
916
|
+
"""특정 에이전트만 호출할 수 있는 제한된 task tool 생성"""
|
|
917
|
+
@tool
|
|
918
|
+
def task(agent_name: str, description: str) -> str:
|
|
919
|
+
"""Delegate a task to a specialized subagent (limited)."""
|
|
920
|
+
if agent_name not in allowed_agents:
|
|
921
|
+
return f"Error: Cannot call '{agent_name}'. Allowed: {allowed_agents}"
|
|
922
|
+
# ... 기존 task 로직
|
|
923
|
+
return task
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
---
|
|
927
|
+
|
|
928
|
+
## Deep Agents 벤치마킹 상세
|
|
929
|
+
|
|
930
|
+
### 핵심 패턴 분석
|
|
931
|
+
|
|
932
|
+
Deep Agents 라이브러리(https://github.com/langchain-ai/deepagents)의 SubAgentMiddleware를 분석하여 다음 패턴을 벤치마킹합니다:
|
|
933
|
+
|
|
934
|
+
#### 1. Subagent 정의 구조
|
|
935
|
+
```python
|
|
936
|
+
# deepagents의 SubAgent TypedDict 패턴
|
|
937
|
+
SubAgent = TypedDict("SubAgent", {
|
|
938
|
+
"name": str, # 필수: 에이전트 식별자
|
|
939
|
+
"description": str, # 필수: task tool에 표시될 설명
|
|
940
|
+
"system_prompt": str, # 필수: 에이전트 프롬프트
|
|
941
|
+
"tools": List[BaseTool], # 필수: 사용 가능한 도구 목록
|
|
942
|
+
"model": Optional[str], # 선택: 커스텀 LLM 모델
|
|
943
|
+
"middleware": Optional[List], # 선택: 추가 미들웨어
|
|
944
|
+
})
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
#### 2. Task Tool 동작 방식
|
|
948
|
+
```python
|
|
949
|
+
# deepagents의 task tool 핵심 로직
|
|
950
|
+
@tool
|
|
951
|
+
def task(task_description: str, subagent_type: str = "general") -> str:
|
|
952
|
+
"""
|
|
953
|
+
1. subagent_type 유효성 검사
|
|
954
|
+
2. 격리된 상태 준비 (messages, todos 등 제외)
|
|
955
|
+
3. task_description을 HumanMessage로 변환하여 서브에이전트에 전달
|
|
956
|
+
4. 서브에이전트 실행
|
|
957
|
+
5. 최종 메시지를 ToolMessage로 반환
|
|
958
|
+
"""
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
#### 3. Context Isolation (핵심!)
|
|
962
|
+
```python
|
|
963
|
+
# deepagents 패턴: 서브에이전트 호출 시 상태 격리
|
|
964
|
+
def _prepare_isolated_state(parent_state: dict) -> dict:
|
|
965
|
+
"""메인 에이전트 컨텍스트를 오염시키지 않도록 격리"""
|
|
966
|
+
excluded_keys = {"messages", "todos", "structured_response"}
|
|
967
|
+
isolated = {k: v for k, v in parent_state.items() if k not in excluded_keys}
|
|
968
|
+
return isolated
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
#### 4. HITL Interrupt Bubbling
|
|
972
|
+
```python
|
|
973
|
+
# deepagents 패턴: 서브에이전트의 HITL interrupt를 메인 에이전트로 전파
|
|
974
|
+
def _handle_subagent_interrupt(interrupt_info: dict) -> Command:
|
|
975
|
+
"""
|
|
976
|
+
서브에이전트가 HITL interrupt 발생 시:
|
|
977
|
+
1. interrupt 정보를 메인 에이전트로 bubbling up
|
|
978
|
+
2. 클라이언트에서 승인 후 resume_agent 호출 시
|
|
979
|
+
3. 올바른 서브에이전트 context로 라우팅
|
|
980
|
+
"""
|
|
981
|
+
return Command(interrupt={
|
|
982
|
+
"subagent": interrupt_info["agent_name"],
|
|
983
|
+
"tool_call": interrupt_info["tool_call"],
|
|
984
|
+
"checkpoint": interrupt_info["checkpoint_id"],
|
|
985
|
+
})
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
### HDSP 적용 계획
|
|
989
|
+
|
|
990
|
+
| Deep Agents 패턴 | HDSP 적용 |
|
|
991
|
+
|-----------------|----------|
|
|
992
|
+
| `SubAgent` TypedDict | `SUBAGENT_CONFIGS` dict |
|
|
993
|
+
| `task(description, subagent_type)` | `task(agent_name, description)` |
|
|
994
|
+
| Context Isolation | 서브에이전트 호출 시 messages 제외 |
|
|
995
|
+
| HITL Bubbling | 기존 `interrupt_on_approval` 패턴 확장 |
|
|
996
|
+
| `general` default subagent | 제외 (명시적 호출만 허용) |
|
|
997
|
+
|
|
998
|
+
---
|
|
999
|
+
|
|
1000
|
+
## 참고 자료
|
|
1001
|
+
|
|
1002
|
+
### LangChain Documentation
|
|
1003
|
+
- [Subagents Architecture](https://docs.langchain.com/oss/python/langchain/multi-agent/subagents)
|
|
1004
|
+
- [Personal Assistant Tutorial](https://docs.langchain.com/oss/python/langchain/multi-agent/subagents-personal-assistant)
|
|
1005
|
+
- [Router vs Subagents](https://docs.langchain.com/oss/python/langchain/multi-agent/router-knowledge-base)
|
|
1006
|
+
|
|
1007
|
+
### Deep Agents Library (벤치마킹 대상)
|
|
1008
|
+
- [Overview](https://docs.langchain.com/oss/python/deepagents/overview)
|
|
1009
|
+
- [Middleware](https://docs.langchain.com/oss/python/deepagents/middleware)
|
|
1010
|
+
- [Subagents](https://docs.langchain.com/oss/python/deepagents/subagents)
|
|
1011
|
+
- [Task Delegation (Harness)](https://docs.langchain.com/oss/python/deepagents/harness)
|
|
1012
|
+
- [GitHub Repository](https://github.com/langchain-ai/deepagents)
|
|
1013
|
+
|
|
1014
|
+
### 핵심 개념
|
|
1015
|
+
1. **Supervisor Pattern**: 중앙 에이전트가 전문 에이전트들을 도구처럼 호출
|
|
1016
|
+
2. **Context Isolation**: 각 서브에이전트는 깨끗한 context window에서 실행
|
|
1017
|
+
3. **Stateless Subagents**: 서브에이전트는 상태를 유지하지 않음
|
|
1018
|
+
4. **Tool-based Invocation**: 서브에이전트를 `task()` 도구를 통해 호출
|
|
1019
|
+
5. **HITL Bubbling**: 서브에이전트의 human-in-the-loop interrupt가 메인 에이전트로 전파
|
|
1020
|
+
|
|
1021
|
+
---
|
|
1022
|
+
|
|
1023
|
+
## 변경 이력
|
|
1024
|
+
|
|
1025
|
+
| Date | Version | Description |
|
|
1026
|
+
|------|---------|-------------|
|
|
1027
|
+
| 2026-01-22 | 1.0 | 초안 작성 |
|
|
1028
|
+
| 2026-01-22 | 1.1 | 아키텍처 간소화: Resource Manager 제거, FileSystem → Researcher 변경, Deep Agents 벤치마킹 방식으로 전환 |
|
|
1029
|
+
| 2026-01-22 | 1.2 | 도구 재배치: Python Developer에 file tools, execute_command 추가. Researcher는 READ-ONLY로 변경. 공통 도구 모듈화 설계 추가 |
|
|
1030
|
+
| 2026-01-22 | 1.3 | **Athena Query Agent 추가**: Qdrant RAG 기반 SQL 생성. **Nested Subagent 패턴** 도입 (Python Developer → Athena Query 호출 가능) |
|
|
1031
|
+
| 2026-01-22 | 1.4 | **구현 완료**: 디렉토리 구조, 프롬프트, 도구 레지스트리, 서브에이전트 기반, 미들웨어, 라우터 업데이트, 단위 테스트 (22개 통과) |
|
|
1032
|
+
|
|
1033
|
+
---
|
|
1034
|
+
|
|
1035
|
+
## 구현 상태 (Implementation Status)
|
|
1036
|
+
|
|
1037
|
+
### ✅ 완료된 항목
|
|
1038
|
+
|
|
1039
|
+
#### 1. 디렉토리 구조
|
|
1040
|
+
```
|
|
1041
|
+
agent_server/langchain/
|
|
1042
|
+
├── agent_prompts/ # ✅ 에이전트별 프롬프트
|
|
1043
|
+
│ ├── __init__.py
|
|
1044
|
+
│ ├── planner_prompt.py
|
|
1045
|
+
│ ├── python_developer_prompt.py
|
|
1046
|
+
│ ├── researcher_prompt.py
|
|
1047
|
+
│ └── athena_query_prompt.py
|
|
1048
|
+
├── subagents/ # ✅ 서브에이전트 설정
|
|
1049
|
+
│ ├── __init__.py
|
|
1050
|
+
│ └── base.py # SubAgentConfig 정의
|
|
1051
|
+
├── middleware/ # ✅ 서브에이전트 미들웨어
|
|
1052
|
+
│ ├── __init__.py
|
|
1053
|
+
│ └── subagent_middleware.py # task tool, SubAgentMiddleware
|
|
1054
|
+
├── tools/
|
|
1055
|
+
│ ├── shared/ # ✅ 공통 도구
|
|
1056
|
+
│ │ ├── __init__.py
|
|
1057
|
+
│ │ └── qdrant_search.py # Qdrant RAG 검색
|
|
1058
|
+
│ └── tool_registry.py # ✅ 에이전트별 도구 매핑
|
|
1059
|
+
├── agent.py # ✅ create_agent_system 추가
|
|
1060
|
+
└── agent_factory.py # ✅ 멀티에이전트 팩토리
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
#### 2. 핵심 모듈
|
|
1064
|
+
| 모듈 | 상태 | 설명 |
|
|
1065
|
+
|------|------|------|
|
|
1066
|
+
| `agent_factory.py` | ✅ | `create_multi_agent_system`, `create_planner_agent`, `create_subagent` |
|
|
1067
|
+
| `tool_registry.py` | ✅ | 에이전트별 도구 매핑 (`AGENT_TOOLS_CONFIG`, `get_tools_for_agent`) |
|
|
1068
|
+
| `subagent_middleware.py` | ✅ | `task` 도구, `SubAgentMiddleware`, `create_task_tool` |
|
|
1069
|
+
| `subagents/base.py` | ✅ | `SubAgentConfig`, `SUBAGENT_CONFIGS`, `get_subagent_config` |
|
|
1070
|
+
|
|
1071
|
+
#### 3. 라우터 업데이트
|
|
1072
|
+
- ✅ `AgentRequest.agentMode` 필드 추가 ("single" | "multi")
|
|
1073
|
+
- ✅ `ResumeRequest.agentMode` 필드 추가
|
|
1074
|
+
- ✅ `create_agent_system()` 사용으로 전환
|
|
1075
|
+
- ✅ 캐시 키에 `agent_mode` 포함
|
|
1076
|
+
|
|
1077
|
+
#### 4. 단위 테스트
|
|
1078
|
+
- ✅ `tests/test_multi_agent_architecture.py` (22개 테스트 모두 통과)
|
|
1079
|
+
- Tool Registry 테스트 (5개)
|
|
1080
|
+
- SubAgent Config 테스트 (4개)
|
|
1081
|
+
- Agent Prompts 테스트 (3개)
|
|
1082
|
+
- SubAgent Middleware 테스트 (2개)
|
|
1083
|
+
- Agent Factory 테스트 (1개)
|
|
1084
|
+
- Agent System Wrapper 테스트 (2개)
|
|
1085
|
+
- Router Agent Mode 테스트 (3개)
|
|
1086
|
+
- Qdrant Search Tool 테스트 (2개)
|
|
1087
|
+
|
|
1088
|
+
### 🔄 사용 방법
|
|
1089
|
+
|
|
1090
|
+
#### Single Agent Mode (기존 방식)
|
|
1091
|
+
```python
|
|
1092
|
+
# 기본값: agentMode="single"
|
|
1093
|
+
response = await client.post("/langchain/stream", json={
|
|
1094
|
+
"request": "분석해줘",
|
|
1095
|
+
"threadId": "...",
|
|
1096
|
+
})
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
#### Multi-Agent Mode (Planner + Subagents)
|
|
1100
|
+
```python
|
|
1101
|
+
# 멀티에이전트 모드 활성화
|
|
1102
|
+
response = await client.post("/langchain/stream", json={
|
|
1103
|
+
"request": "분석해줘",
|
|
1104
|
+
"threadId": "...",
|
|
1105
|
+
"agentMode": "multi", # ← 멀티에이전트 모드
|
|
1106
|
+
})
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
### 📝 향후 작업 (TODO)
|
|
1110
|
+
|
|
1111
|
+
1. **HITL Interrupt Bubbling**: 서브에이전트의 HITL interrupt가 Planner로 전파되도록 구현
|
|
1112
|
+
2. **UI 업데이트**: 활성 에이전트 표시, agent_switch SSE 이벤트
|
|
1113
|
+
3. **Qdrant 연동 테스트**: 실제 Qdrant 서버와 연동 테스트
|
|
1114
|
+
4. **성능 벤치마크**: 단일 에이전트 vs 멀티에이전트 비교
|