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.
Files changed (81) hide show
  1. agent_server/langchain/MULTI_AGENT_ARCHITECTURE.md +1114 -0
  2. agent_server/langchain/__init__.py +2 -2
  3. agent_server/langchain/agent.py +72 -33
  4. agent_server/langchain/agent_factory.py +400 -0
  5. agent_server/langchain/agent_prompts/__init__.py +25 -0
  6. agent_server/langchain/agent_prompts/athena_query_prompt.py +71 -0
  7. agent_server/langchain/agent_prompts/planner_prompt.py +85 -0
  8. agent_server/langchain/agent_prompts/python_developer_prompt.py +123 -0
  9. agent_server/langchain/agent_prompts/researcher_prompt.py +38 -0
  10. agent_server/langchain/custom_middleware.py +652 -195
  11. agent_server/langchain/hitl_config.py +34 -10
  12. agent_server/langchain/middleware/__init__.py +24 -0
  13. agent_server/langchain/middleware/code_history_middleware.py +412 -0
  14. agent_server/langchain/middleware/description_injector.py +150 -0
  15. agent_server/langchain/middleware/skill_middleware.py +298 -0
  16. agent_server/langchain/middleware/subagent_events.py +171 -0
  17. agent_server/langchain/middleware/subagent_middleware.py +329 -0
  18. agent_server/langchain/prompts.py +96 -101
  19. agent_server/langchain/skills/data_analysis.md +236 -0
  20. agent_server/langchain/skills/data_loading.md +158 -0
  21. agent_server/langchain/skills/inference.md +392 -0
  22. agent_server/langchain/skills/model_training.md +318 -0
  23. agent_server/langchain/skills/pyspark.md +352 -0
  24. agent_server/langchain/subagents/__init__.py +20 -0
  25. agent_server/langchain/subagents/base.py +173 -0
  26. agent_server/langchain/tools/__init__.py +3 -0
  27. agent_server/langchain/tools/jupyter_tools.py +58 -20
  28. agent_server/langchain/tools/lsp_tools.py +1 -1
  29. agent_server/langchain/tools/shared/__init__.py +26 -0
  30. agent_server/langchain/tools/shared/qdrant_search.py +175 -0
  31. agent_server/langchain/tools/tool_registry.py +219 -0
  32. agent_server/langchain/tools/workspace_tools.py +197 -0
  33. agent_server/routers/config.py +40 -1
  34. agent_server/routers/langchain_agent.py +818 -337
  35. {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
  36. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/package.json +7 -2
  37. 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
  38. hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js.map +1 -0
  39. 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
  40. hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.5449ba3c7e25177d2987.js.map +1 -0
  41. 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
  42. hdsp_jupyter_extension-2.0.13.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.a8e0b064eb9b1c1ff463.js.map +1 -0
  43. {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/METADATA +1 -1
  44. {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/RECORD +75 -51
  45. jupyter_ext/_version.py +1 -1
  46. jupyter_ext/handlers.py +59 -8
  47. jupyter_ext/labextension/build_log.json +1 -1
  48. jupyter_ext/labextension/package.json +7 -2
  49. jupyter_ext/labextension/static/{frontend_styles_index_js.2d9fb488c82498c45c2d.js → frontend_styles_index_js.037b3c8e5d6a92b63b16.js} +1108 -179
  50. jupyter_ext/labextension/static/frontend_styles_index_js.037b3c8e5d6a92b63b16.js.map +1 -0
  51. 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
  52. jupyter_ext/labextension/static/lib_index_js.5449ba3c7e25177d2987.js.map +1 -0
  53. jupyter_ext/labextension/static/{remoteEntry.9da31d1134a53b0c4af5.js → remoteEntry.a8e0b064eb9b1c1ff463.js} +17 -17
  54. jupyter_ext/labextension/static/remoteEntry.a8e0b064eb9b1c1ff463.js.map +1 -0
  55. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js.map +0 -1
  56. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.58c1e128ba0b76f41f04.js.map +0 -1
  57. hdsp_jupyter_extension-2.0.11.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.9da31d1134a53b0c4af5.js.map +0 -1
  58. jupyter_ext/labextension/static/frontend_styles_index_js.2d9fb488c82498c45c2d.js.map +0 -1
  59. jupyter_ext/labextension/static/lib_index_js.58c1e128ba0b76f41f04.js.map +0 -1
  60. jupyter_ext/labextension/static/remoteEntry.9da31d1134a53b0c4af5.js.map +0 -1
  61. {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
  62. {hdsp_jupyter_extension-2.0.11.data → hdsp_jupyter_extension-2.0.13.data}/data/share/jupyter/labextensions/hdsp-agent/install.json +0 -0
  63. {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
  64. {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
  65. {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
  66. {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
  67. {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
  68. {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
  69. {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
  70. {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
  71. {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
  72. {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
  73. {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
  74. {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
  75. {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
  76. {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
  77. {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
  78. {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
  79. {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
  80. {hdsp_jupyter_extension-2.0.11.dist-info → hdsp_jupyter_extension-2.0.13.dist-info}/WHEEL +0 -0
  81. {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 멀티에이전트 비교