hdsp-jupyter-extension 2.0.0__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/__init__.py +8 -0
- agent_server/core/__init__.py +92 -0
- agent_server/core/api_key_manager.py +427 -0
- agent_server/core/code_validator.py +1238 -0
- agent_server/core/context_condenser.py +308 -0
- agent_server/core/embedding_service.py +254 -0
- agent_server/core/error_classifier.py +577 -0
- agent_server/core/llm_client.py +95 -0
- agent_server/core/llm_service.py +649 -0
- agent_server/core/notebook_generator.py +274 -0
- agent_server/core/prompt_builder.py +35 -0
- agent_server/core/rag_manager.py +742 -0
- agent_server/core/reflection_engine.py +489 -0
- agent_server/core/retriever.py +248 -0
- agent_server/core/state_verifier.py +452 -0
- agent_server/core/summary_generator.py +484 -0
- agent_server/core/task_manager.py +198 -0
- agent_server/knowledge/__init__.py +9 -0
- agent_server/knowledge/watchdog_service.py +352 -0
- agent_server/main.py +160 -0
- agent_server/prompts/__init__.py +60 -0
- agent_server/prompts/file_action_prompts.py +113 -0
- agent_server/routers/__init__.py +9 -0
- agent_server/routers/agent.py +591 -0
- agent_server/routers/chat.py +188 -0
- agent_server/routers/config.py +100 -0
- agent_server/routers/file_resolver.py +293 -0
- agent_server/routers/health.py +42 -0
- agent_server/routers/rag.py +163 -0
- agent_server/schemas/__init__.py +60 -0
- hdsp_agent_core/__init__.py +158 -0
- hdsp_agent_core/factory.py +252 -0
- hdsp_agent_core/interfaces.py +203 -0
- hdsp_agent_core/knowledge/__init__.py +31 -0
- hdsp_agent_core/knowledge/chunking.py +356 -0
- hdsp_agent_core/knowledge/libraries/dask.md +188 -0
- hdsp_agent_core/knowledge/libraries/matplotlib.md +164 -0
- hdsp_agent_core/knowledge/libraries/polars.md +68 -0
- hdsp_agent_core/knowledge/loader.py +337 -0
- hdsp_agent_core/llm/__init__.py +13 -0
- hdsp_agent_core/llm/service.py +556 -0
- hdsp_agent_core/managers/__init__.py +22 -0
- hdsp_agent_core/managers/config_manager.py +133 -0
- hdsp_agent_core/managers/session_manager.py +251 -0
- hdsp_agent_core/models/__init__.py +115 -0
- hdsp_agent_core/models/agent.py +316 -0
- hdsp_agent_core/models/chat.py +41 -0
- hdsp_agent_core/models/common.py +95 -0
- hdsp_agent_core/models/rag.py +368 -0
- hdsp_agent_core/prompts/__init__.py +63 -0
- hdsp_agent_core/prompts/auto_agent_prompts.py +1260 -0
- hdsp_agent_core/prompts/cell_action_prompts.py +98 -0
- hdsp_agent_core/services/__init__.py +18 -0
- hdsp_agent_core/services/agent_service.py +438 -0
- hdsp_agent_core/services/chat_service.py +205 -0
- hdsp_agent_core/services/rag_service.py +262 -0
- hdsp_agent_core/tests/__init__.py +1 -0
- hdsp_agent_core/tests/conftest.py +102 -0
- hdsp_agent_core/tests/test_factory.py +251 -0
- hdsp_agent_core/tests/test_services.py +326 -0
- hdsp_jupyter_extension-2.0.0.data/data/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +7 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/build_log.json +738 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/install.json +5 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/package.json +134 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js +4369 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js +12496 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/lib_index_js.622c1a5918b3aafb2315.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.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 +94 -0
- hdsp_jupyter_extension-2.0.0.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 +1 -0
- hdsp_jupyter_extension-2.0.0.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 +94 -0
- hdsp_jupyter_extension-2.0.0.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 +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.dae97cde171e13b8c834.js +623 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/remoteEntry.dae97cde171e13b8c834.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/style.js +4 -0
- hdsp_jupyter_extension-2.0.0.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 +507 -0
- hdsp_jupyter_extension-2.0.0.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 +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js +2071 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js +1059 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +376 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +60336 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js +7132 -0
- hdsp_jupyter_extension-2.0.0.data/data/share/jupyter/labextensions/hdsp-agent/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +1 -0
- hdsp_jupyter_extension-2.0.0.dist-info/METADATA +152 -0
- hdsp_jupyter_extension-2.0.0.dist-info/RECORD +121 -0
- hdsp_jupyter_extension-2.0.0.dist-info/WHEEL +4 -0
- hdsp_jupyter_extension-2.0.0.dist-info/licenses/LICENSE +21 -0
- jupyter_ext/__init__.py +233 -0
- jupyter_ext/_version.py +4 -0
- jupyter_ext/config.py +111 -0
- jupyter_ext/etc/jupyter/jupyter_server_config.d/hdsp_jupyter_extension.json +7 -0
- jupyter_ext/handlers.py +632 -0
- jupyter_ext/labextension/build_log.json +738 -0
- jupyter_ext/labextension/package.json +134 -0
- jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js +4369 -0
- jupyter_ext/labextension/static/frontend_styles_index_js.2607ff74c74acfa83158.js.map +1 -0
- jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js +12496 -0
- jupyter_ext/labextension/static/lib_index_js.622c1a5918b3aafb2315.js.map +1 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js +94 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b80.c095373419d05e6f141a.js.map +1 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js +94 -0
- jupyter_ext/labextension/static/node_modules_emotion_use-insertion-effect-with-fallbacks_dist_emotion-use-insertion-effect-wi-3ba6b81.61e75fb98ecff46cf836.js.map +1 -0
- jupyter_ext/labextension/static/remoteEntry.dae97cde171e13b8c834.js +623 -0
- jupyter_ext/labextension/static/remoteEntry.dae97cde171e13b8c834.js.map +1 -0
- jupyter_ext/labextension/static/style.js +4 -0
- jupyter_ext/labextension/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js +507 -0
- jupyter_ext/labextension/static/vendors-node_modules_babel_runtime_helpers_esm_extends_js-node_modules_emotion_serialize_dist-051195.e2553aab0c3963b83dd7.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js +2071 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_cache_dist_emotion-cache_browser_development_esm_js-node_modules-782ee5.d9ed8645ef1d311657d8.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js +1059 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_react_dist_emotion-react_browser_development_esm_js.36b49c71871f98d4f549.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js +376 -0
- jupyter_ext/labextension/static/vendors-node_modules_emotion_styled_dist_emotion-styled_browser_development_esm_js.661fb5836f4978a7c6e1.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js +60336 -0
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_index_js.985697e0162d8d088ca2.js.map +1 -0
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js +7132 -0
- jupyter_ext/labextension/static/vendors-node_modules_mui_material_utils_createSvgIcon_js.2e13df4ea61496e95d45.js.map +1 -0
|
@@ -0,0 +1,1260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-Agent Prompts
|
|
3
|
+
HuggingFace Jupyter Agent 패턴 기반 프롬프트 템플릿
|
|
4
|
+
|
|
5
|
+
Tool Calling 구조:
|
|
6
|
+
- jupyter_cell: 코드 셀 생성/수정/실행
|
|
7
|
+
- markdown: 마크다운 셀 생성/수정
|
|
8
|
+
- final_answer: 작업 완료 신호
|
|
9
|
+
- read_file: 파일 읽기 (상대 경로만)
|
|
10
|
+
- write_file: 파일 쓰기 (승인 필요)
|
|
11
|
+
- list_files: 디렉토리 조회
|
|
12
|
+
- execute_command: 셸 명령 실행 (위험 명령만 승인)
|
|
13
|
+
- search_files: 파일 내용 검색
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
19
|
+
# Nexus URL 설정 (보안을 위해 외부 파일에서 읽기)
|
|
20
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_pip_index_option() -> str:
|
|
24
|
+
"""
|
|
25
|
+
pip install 시 사용할 index-url 옵션 반환
|
|
26
|
+
- Sagemaker 환경: nexus-url.txt에서 읽어서 --index-url <url> 반환
|
|
27
|
+
- 로컬 환경: 빈 문자열 반환 (일반 pip install)
|
|
28
|
+
"""
|
|
29
|
+
nexus_url_path = "/home/sagemaker-user/nexus-url.txt"
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
if os.path.exists(nexus_url_path):
|
|
33
|
+
with open(nexus_url_path, "r") as f:
|
|
34
|
+
url = f.read().strip()
|
|
35
|
+
if url:
|
|
36
|
+
return f"--index-url {url}"
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print(
|
|
39
|
+
f"[AutoAgent] Warning: Failed to load nexus URL from {nexus_url_path}: {e}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# 파일이 없거나 읽기 실패 시: 일반 pip install (로컬 환경)
|
|
43
|
+
return ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
PIP_INDEX_OPTION = _get_pip_index_option()
|
|
47
|
+
|
|
48
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
49
|
+
# 실행 계획 생성 프롬프트
|
|
50
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
51
|
+
|
|
52
|
+
PLAN_GENERATION_PROMPT = """Jupyter 노트북 Python 전문가. 단계별 실행 계획을 JSON으로 생성.
|
|
53
|
+
|
|
54
|
+
## 도구
|
|
55
|
+
### 기본 도구 (셀 작업)
|
|
56
|
+
1. **jupyter_cell**: {{"code": "Python코드"}} - 노트북 끝에 새 셀 추가
|
|
57
|
+
2. **markdown**: {{"content": "마크다운"}} - 설명 셀 추가
|
|
58
|
+
3. **final_answer**: {{"answer": "완료메시지"}} - 작업 완료
|
|
59
|
+
|
|
60
|
+
### 확장 도구 (파일/터미널)
|
|
61
|
+
4. **read_file**: {{"path": "상대경로"}} - 파일 읽기 (절대경로/.. 금지)
|
|
62
|
+
5. **write_file**: {{"path": "상대경로", "content": "내용"}} - 파일 쓰기 (승인 필요)
|
|
63
|
+
6. **list_files**: {{"path": ".", "recursive": false, "pattern": "*.py"}} - 디렉토리 조회
|
|
64
|
+
7. **execute_command**: {{"command": "pip list"}} - 셸 명령 (위험 명령만 승인)
|
|
65
|
+
8. **search_files**: {{"pattern": "def func", "path": "src"}} - 파일 내용 검색
|
|
66
|
+
|
|
67
|
+
## 🚨 핵심 원칙 (CRITICAL!)
|
|
68
|
+
1. ⛔ **기존 셀 수정 금지! 항상 새 셀을 노트북 끝에 추가**
|
|
69
|
+
2. ⛔ **기존 변수(df 등)에 의존 금지! 새 셀은 독립적으로 실행 가능해야 함**
|
|
70
|
+
3. ✅ **데이터 로딩/정의를 포함한 완전한 코드 작성** (기존 코드는 참고용)
|
|
71
|
+
|
|
72
|
+
## 노트북 현황 (참고용 - 기존 변수 사용 금지!)
|
|
73
|
+
- 셀: {cell_count}개 | 라이브러리: {imported_libraries} | 변수: {defined_variables}
|
|
74
|
+
- 최근 셀 (참고용):
|
|
75
|
+
{recent_cells}
|
|
76
|
+
|
|
77
|
+
## 환경: {available_libraries}
|
|
78
|
+
|
|
79
|
+
## 요청: {request}
|
|
80
|
+
|
|
81
|
+
## 규칙
|
|
82
|
+
1. 최대 10단계, 마지막은 final_answer
|
|
83
|
+
2. 한글 설명, 한자 금지
|
|
84
|
+
3. 미설치 패키지: `!pip install {PIP_INDEX_OPTION} --timeout 180 패키지`
|
|
85
|
+
4. 시각화 전 데이터 검증 필수
|
|
86
|
+
5. 첫 셀에 warnings 필터링 + 필요한 import + 데이터 로딩 포함
|
|
87
|
+
6. 기존 노트북 코드를 분석/개선할 때도 새 셀에서 처음부터 구현
|
|
88
|
+
7. **시각화 코드에는 반드시 한글 폰트 설정 포함**:
|
|
89
|
+
```python
|
|
90
|
+
import matplotlib.pyplot as plt
|
|
91
|
+
plt.rcParams['font.family'] = 'AppleGothic'
|
|
92
|
+
plt.rcParams['axes.unicode_minus'] = False
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## JSON 출력
|
|
96
|
+
```json
|
|
97
|
+
{{"reasoning":"이유","plan":{{"totalSteps":N,"steps":[{{"stepNumber":1,"description":"설명","toolCalls":[{{"tool":"jupyter_cell","parameters":{{"code":"코드"}}}}],"dependencies":[]}}]}}}}
|
|
98
|
+
```
|
|
99
|
+
JSON만 출력."""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
103
|
+
# 코드 생성 프롬프트 (단일 셀)
|
|
104
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
105
|
+
|
|
106
|
+
CODE_GENERATION_PROMPT = """당신은 Jupyter 노트북을 위한 Python 코드 전문가입니다.
|
|
107
|
+
|
|
108
|
+
## 요청
|
|
109
|
+
|
|
110
|
+
{request}
|
|
111
|
+
|
|
112
|
+
## 컨텍스트
|
|
113
|
+
|
|
114
|
+
- 사용 가능한 라이브러리: {available_libraries}
|
|
115
|
+
- 정의된 변수: {defined_variables}
|
|
116
|
+
- 이전 셀 출력: {previous_output}
|
|
117
|
+
|
|
118
|
+
## 지침
|
|
119
|
+
|
|
120
|
+
1. 실행 가능한 Python 코드만 생성하세요
|
|
121
|
+
2. 필요한 import 문을 포함하세요
|
|
122
|
+
3. 마지막 줄에 결과를 반환/출력하세요
|
|
123
|
+
4. 주석은 간결하게 작성하세요
|
|
124
|
+
5. **코드 내 주석과 문자열은 한글 또는 영어로만 작성하세요 (한자 사용 절대 금지)**
|
|
125
|
+
6. **함수 docstring은 작은따옴표(') 3개만 사용하세요. 절대 백틱(`)을 사용하지 마세요.**
|
|
126
|
+
7. **변수 값 출력**:
|
|
127
|
+
- f-string을 사용하여 변수 값을 출력하세요
|
|
128
|
+
- Markdown cell에 변수를 쓰면 변수명이 그대로 텍스트로 출력됩니다
|
|
129
|
+
- 변수 값을 출력하려면 code cell에서 print 또는 display 사용하세요
|
|
130
|
+
- 마크다운 포맷으로 예쁘게 출력하려면 display(Markdown(...)) 사용 권장
|
|
131
|
+
8. 에러 처리를 적절히 포함하세요
|
|
132
|
+
9. **시각화 시 한글 폰트 설정 필수**:
|
|
133
|
+
```python
|
|
134
|
+
import matplotlib.pyplot as plt
|
|
135
|
+
plt.rcParams['font.family'] = 'AppleGothic'
|
|
136
|
+
plt.rcParams['axes.unicode_minus'] = False
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 출력
|
|
140
|
+
|
|
141
|
+
Python 코드만 출력하세요. 마크다운이나 설명 없이."""
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
145
|
+
# 에러 수정 프롬프트 (Self-Healing)
|
|
146
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
147
|
+
|
|
148
|
+
ERROR_REFINEMENT_PROMPT = """다음 코드가 오류로 실패했습니다. 수정된 코드를 제공하세요.
|
|
149
|
+
|
|
150
|
+
## 원래 코드
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
{original_code}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 오류 정보
|
|
157
|
+
|
|
158
|
+
- 오류 유형: {error_type}
|
|
159
|
+
- 오류 메시지: {error_message}
|
|
160
|
+
- 트레이스백:
|
|
161
|
+
```
|
|
162
|
+
{traceback}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 시도 횟수
|
|
166
|
+
|
|
167
|
+
{attempt}/{max_attempts}
|
|
168
|
+
|
|
169
|
+
## 컨텍스트
|
|
170
|
+
|
|
171
|
+
- 사용 가능한 라이브러리: {available_libraries}
|
|
172
|
+
- 정의된 변수: {defined_variables}
|
|
173
|
+
|
|
174
|
+
## 지침
|
|
175
|
+
|
|
176
|
+
1. 오류의 근본 원인을 분석하세요
|
|
177
|
+
2. 수정된 코드를 제공하세요
|
|
178
|
+
3. 같은 오류가 반복되지 않도록 하세요
|
|
179
|
+
4. **코드 내 주석과 문자열은 한글 또는 영어로만 작성하세요 (한자 사용 절대 금지)**
|
|
180
|
+
5. **함수 docstring은 작은따옴표(') 3개만 사용하세요. 절대 백틱(`)을 사용하지 마세요.**
|
|
181
|
+
6. **변수 값 출력**:
|
|
182
|
+
- f-string을 사용하여 변수 값을 출력하세요
|
|
183
|
+
- Markdown cell에 변수를 쓰면 변수명이 그대로 텍스트로 출력됩니다
|
|
184
|
+
- 변수 값을 출력하려면 code cell에서 print 또는 display 사용하세요
|
|
185
|
+
- 마크다운 포맷으로 예쁘게 출력하려면 display(Markdown(...)) 사용 권장
|
|
186
|
+
|
|
187
|
+
## ⚠️ 중요 규칙 (절대 위반 금지)
|
|
188
|
+
|
|
189
|
+
**ModuleNotFoundError/ImportError 처리**:
|
|
190
|
+
- 모듈이 없는 에러의 경우, **절대로 다른 라이브러리로 대체하지 마세요**
|
|
191
|
+
- 예: `import dask` 실패 시 → `import pandas`로 대체 ❌ 금지!
|
|
192
|
+
- 이런 에러는 시스템이 자동으로 패키지 설치로 해결합니다
|
|
193
|
+
- Self-Healing에서는 **코드 문법/로직 수정만** 수행하세요
|
|
194
|
+
|
|
195
|
+
**수정 가능한 에러 유형**:
|
|
196
|
+
- SyntaxError (문법 오류)
|
|
197
|
+
- TypeError (타입 불일치)
|
|
198
|
+
- ValueError (값 오류)
|
|
199
|
+
- KeyError (잘못된 키)
|
|
200
|
+
- IndexError (인덱스 범위)
|
|
201
|
+
- AttributeError (잘못된 속성)
|
|
202
|
+
- NameError (변수명 오타)
|
|
203
|
+
|
|
204
|
+
**수정 불가 - 원래 코드 그대로 반환해야 하는 에러 유형**:
|
|
205
|
+
- ModuleNotFoundError
|
|
206
|
+
- ImportError
|
|
207
|
+
- FileNotFoundError (경로 문제는 시스템이 처리)
|
|
208
|
+
|
|
209
|
+
## 출력 형식 (JSON)
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{{
|
|
213
|
+
"reasoning": "오류 분석 및 수정 방법 설명",
|
|
214
|
+
"toolCalls": [
|
|
215
|
+
{{
|
|
216
|
+
"tool": "jupyter_cell",
|
|
217
|
+
"parameters": {{
|
|
218
|
+
"code": "수정된 Python 코드"
|
|
219
|
+
}}
|
|
220
|
+
}}
|
|
221
|
+
]
|
|
222
|
+
}}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
JSON만 출력하세요."""
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
229
|
+
# Adaptive Replanning 프롬프트 (계획 수정)
|
|
230
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
231
|
+
|
|
232
|
+
ADAPTIVE_REPLAN_PROMPT = """에러가 발생했습니다. 출력과 에러를 분석하여 계획을 수정하거나 새로운 접근법을 제시하세요.
|
|
233
|
+
|
|
234
|
+
## 원래 요청
|
|
235
|
+
|
|
236
|
+
{original_request}
|
|
237
|
+
|
|
238
|
+
## 현재까지 실행된 단계
|
|
239
|
+
|
|
240
|
+
{executed_steps}
|
|
241
|
+
|
|
242
|
+
## 실패한 단계
|
|
243
|
+
|
|
244
|
+
- 단계 번호: {failed_step_number}
|
|
245
|
+
- 설명: {failed_step_description}
|
|
246
|
+
- 실행된 코드:
|
|
247
|
+
```python
|
|
248
|
+
{failed_code}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 에러 정보
|
|
252
|
+
|
|
253
|
+
- 오류 유형: {error_type}
|
|
254
|
+
- 오류 메시지: {error_message}
|
|
255
|
+
- 트레이스백:
|
|
256
|
+
```
|
|
257
|
+
{traceback}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## 실행 출력 (stdout/stderr)
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
{execution_output}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## 현재 환경 정보
|
|
267
|
+
|
|
268
|
+
- **설치된 패키지**: {available_libraries}
|
|
269
|
+
|
|
270
|
+
## ⚠️ 필수 규칙 (MANDATORY RULES - 반드시 따를 것!)
|
|
271
|
+
|
|
272
|
+
### 🚨🚨🚨 ModuleNotFoundError / ImportError → 무조건 `insert_steps` 사용! 🚨🚨🚨
|
|
273
|
+
|
|
274
|
+
**⛔ 절대적 금지 사항 (이 규칙은 어떤 경우에도 위반 불가)**:
|
|
275
|
+
- `ModuleNotFoundError`나 `ImportError` 발생 시:
|
|
276
|
+
- ❌ `refine` 사용 금지!
|
|
277
|
+
- ❌ `replace_step` 사용 금지!
|
|
278
|
+
- ❌ `replan_remaining` 사용 금지!
|
|
279
|
+
- ✅ 오직 `insert_steps`만 허용!
|
|
280
|
+
|
|
281
|
+
**🔍 간접 의존성 오류 (CRITICAL - 매우 중요!)**:
|
|
282
|
+
- 실행한 코드와 오류 메시지의 패키지가 **달라도** `insert_steps` 사용!
|
|
283
|
+
- 예시 1: `import dask.dataframe as dd` 실행 → `No module named 'pyarrow'` 오류
|
|
284
|
+
→ pyarrow는 dask의 **내부 의존성**
|
|
285
|
+
→ `insert_steps`로 `!pip install {PIP_INDEX_OPTION} --timeout 180 pyarrow` 추가!
|
|
286
|
+
→ ❌ "dask 대신 pandas 사용" 같은 접근법 변경 금지!
|
|
287
|
+
- 예시 2: `import tensorflow` 실행 → `No module named 'keras'` 오류
|
|
288
|
+
→ `insert_steps`로 `!pip install {PIP_INDEX_OPTION} --timeout 180 keras` 추가!
|
|
289
|
+
- 예시 3: `from transformers import AutoModel` 실행 → `No module named 'accelerate'` 오류
|
|
290
|
+
→ `insert_steps`로 `!pip install {PIP_INDEX_OPTION} --timeout 180 accelerate` 추가!
|
|
291
|
+
|
|
292
|
+
**📋 판단 기준**: 에러 메시지에 `No module named` 또는 `ImportError`가 있으면:
|
|
293
|
+
1. **⚠️ 에러 메시지에서 패키지명 추출 (코드가 아님!)** ⚠️
|
|
294
|
+
2. 무조건 `insert_steps` 선택
|
|
295
|
+
3. `!pip install {PIP_INDEX_OPTION} --timeout 180 에러메시지의_패키지명` 단계 추가
|
|
296
|
+
4. **사용자가 요청한 원래 라이브러리(dask 등)는 그대로 유지!**
|
|
297
|
+
|
|
298
|
+
**🚨 URL 축약 절대 금지!**:
|
|
299
|
+
- pip install 명령어에서 URL이 포함된 경우, **반드시 전체 URL을 그대로 사용**해야 합니다
|
|
300
|
+
- ❌ 금지: `https://repository.example.../simple` (... 로 축약)
|
|
301
|
+
- ✅ 필수: `https://repository.example.com/pypi/simple` (전체 URL)
|
|
302
|
+
- 긴 URL이라도 절대 축약하지 마세요! 실행되지 않습니다!
|
|
303
|
+
|
|
304
|
+
**🚨 패키지 설치 전 필수 확인!**:
|
|
305
|
+
- **설치된 패키지** 목록을 반드시 확인하세요
|
|
306
|
+
- 에러 메시지의 패키지가 **이미 설치되어 있다면** 설치 단계를 추가하지 마세요!
|
|
307
|
+
- 예: 에러가 `No module named 'pyarrow'`인데 설치된 패키지에 `pyarrow`가 있으면 → 설치 불필요
|
|
308
|
+
- 예: 에러가 `No module named 'dask'`인데 설치된 패키지에 `dask`가 있으면 → 설치 불필요
|
|
309
|
+
- ⚠️ **주의**: 패키지가 이미 있는데도 설치를 반복하면 무한 루프에 빠집니다!
|
|
310
|
+
- ✅ 패키지가 없을 때만 `insert_steps`로 설치 추가하세요
|
|
311
|
+
|
|
312
|
+
### 🚨🚨🚨 패키지명 추출 - 매우 중요!!! 🚨🚨🚨
|
|
313
|
+
|
|
314
|
+
**반드시 에러 메시지에서 추출하세요! 사용자 코드에서 추출하면 안 됩니다!**
|
|
315
|
+
|
|
316
|
+
**예시 상황**:
|
|
317
|
+
- 사용자 코드: `import dask.dataframe as dd`
|
|
318
|
+
- 에러 메시지: `ModuleNotFoundError: No module named 'pyarrow'`
|
|
319
|
+
|
|
320
|
+
| 추출 방법 | 결과 | 판정 |
|
|
321
|
+
|----------|------|------|
|
|
322
|
+
| 사용자 코드에서 추출 | `!pip install {PIP_INDEX_OPTION} --timeout 180 dask` | ❌ **완전히 틀림!** |
|
|
323
|
+
| 에러 메시지에서 추출 | `!pip install {PIP_INDEX_OPTION} --timeout 180 pyarrow` | ✅ **정답!** |
|
|
324
|
+
|
|
325
|
+
**왜 중요한가?**:
|
|
326
|
+
- dask는 이미 설치되어 있음 (그래서 import dask가 시작됨)
|
|
327
|
+
- 하지만 dask 내부에서 pyarrow를 로드하려다 실패
|
|
328
|
+
- 따라서 설치해야 할 패키지는 pyarrow!
|
|
329
|
+
|
|
330
|
+
### 패키지명 추출 규칙
|
|
331
|
+
- "No module named 'xxx'" → `!pip install {PIP_INDEX_OPTION} --timeout 180 xxx` (에러 메시지의 xxx!)
|
|
332
|
+
- "No module named 'xxx.yyy'" → `!pip install {PIP_INDEX_OPTION} --timeout 180 xxx` (최상위 패키지만)
|
|
333
|
+
- 예외: `sklearn` → `!pip install {PIP_INDEX_OPTION} --timeout 180 scikit-learn`
|
|
334
|
+
- 예외: `cv2` → `!pip install {PIP_INDEX_OPTION} --timeout 180 opencv-python`
|
|
335
|
+
- 예외: `PIL` → `!pip install {PIP_INDEX_OPTION} --timeout 180 pillow`
|
|
336
|
+
|
|
337
|
+
## 분석 지침
|
|
338
|
+
|
|
339
|
+
1. **근본 원인 분석**: 단순 코드 버그인가, 접근법 자체의 문제인가?
|
|
340
|
+
2. **필요한 선행 작업**: 누락된 import, 데이터 변환, 환경 설정이 있는가?
|
|
341
|
+
3. **대안적 접근법**: 다른 라이브러리나 방법을 사용해야 하는가?
|
|
342
|
+
4. **⚠️ 이전 실행된 코드 참고**: 위의 "현재까지 실행된 단계"에 표시된 코드를 반드시 확인하세요!
|
|
343
|
+
- 예: 이전 단계에서 데이터프레임 컬럼명을 소문자로 변환했다면, 현재 단계에서도 소문자로 접근해야 합니다
|
|
344
|
+
- 예: 이전 단계에서 특정 변수를 정의했다면, 그 변수명을 그대로 사용해야 합니다
|
|
345
|
+
- 데이터 전처리, 변수 변환 등 이전 컨텍스트를 유지하세요
|
|
346
|
+
5. **코드 내 주석과 문자열은 한글 또는 영어로만 작성하세요 (한자 사용 절대 금지)**
|
|
347
|
+
6. **함수 docstring은 작은따옴표(') 3개만 사용하세요. 절대 백틱(`)을 사용하지 마세요.**
|
|
348
|
+
7. **변수 값 출력**:
|
|
349
|
+
- f-string을 사용하여 변수 값을 출력하세요
|
|
350
|
+
- Markdown cell에 변수를 쓰면 변수명이 그대로 텍스트로 출력됩니다
|
|
351
|
+
- 변수 값을 출력하려면 code cell에서 print 또는 display 사용하세요
|
|
352
|
+
- 마크다운 포맷으로 예쁘게 출력하려면 display(Markdown(...)) 사용 권장
|
|
353
|
+
|
|
354
|
+
## 에러 유형별 해결 전략
|
|
355
|
+
|
|
356
|
+
### FileNotFoundError
|
|
357
|
+
- 파일 경로 확인 또는 파일 존재 여부 체크 단계 추가
|
|
358
|
+
- 가능하면 `os.path.exists()` 검증 후 적절한 에러 메시지
|
|
359
|
+
|
|
360
|
+
### NameError (변수 미정의)
|
|
361
|
+
**원인을 먼저 파악하세요:**
|
|
362
|
+
1. **이전 MODIFY 단계에서 원본 코드가 손실된 경우**
|
|
363
|
+
- 이전 단계에서 셀을 MODIFY할 때 관련 없는 코드를 삭제했을 가능성
|
|
364
|
+
- **해결책**: `refine`으로 해당 코드에 누락된 변수 정의를 복원
|
|
365
|
+
|
|
366
|
+
2. **단순 오타인 경우**
|
|
367
|
+
- `refine`으로 수정
|
|
368
|
+
|
|
369
|
+
3. **원래 계획에서 변수 정의가 누락된 경우**
|
|
370
|
+
- 필요한 변수 정의를 추가하는 것이 적절
|
|
371
|
+
|
|
372
|
+
### TypeError / ValueError
|
|
373
|
+
- 대부분 `refine`으로 코드 수정
|
|
374
|
+
- 데이터 타입 변환이 필요하면 변환 로직 추가
|
|
375
|
+
|
|
376
|
+
## 결정 옵션
|
|
377
|
+
|
|
378
|
+
1. **refine**: 같은 접근법으로 코드만 수정
|
|
379
|
+
- ✅ 사용 가능: SyntaxError, TypeError, ValueError, KeyError, IndexError, AttributeError, NameError
|
|
380
|
+
- ❌ 사용 금지: ModuleNotFoundError, ImportError
|
|
381
|
+
|
|
382
|
+
2. **insert_steps**: 현재 단계 전에 필요한 단계 추가 (선행 작업 필요)
|
|
383
|
+
- ✅ **ModuleNotFoundError, ImportError 발생 시 유일하게 허용되는 옵션!**
|
|
384
|
+
- 패키지 설치: `!pip install {PIP_INDEX_OPTION} --timeout 180 패키지명` 단계 추가
|
|
385
|
+
- 에러 메시지의 패키지명을 정확히 추출하여 설치
|
|
386
|
+
|
|
387
|
+
3. **replace_step**: 현재 단계를 완전히 다른 접근법으로 교체
|
|
388
|
+
- ❌ ModuleNotFoundError, ImportError 시 사용 금지! (라이브러리 대체 금지)
|
|
389
|
+
|
|
390
|
+
4. **replan_remaining**: 남은 모든 단계를 새로 계획 (final_answer도 새로 작성!)
|
|
391
|
+
- ❌ ModuleNotFoundError, ImportError 시 사용 금지! (접근법 변경 금지)
|
|
392
|
+
|
|
393
|
+
## 중요 규칙
|
|
394
|
+
|
|
395
|
+
- **replan_remaining 또는 replace_step 선택 시**: 접근법이 변경되면 final_answer 메시지도 반드시 실제 사용된 방법을 반영해야 합니다.
|
|
396
|
+
- 예: dask → pandas로 변경 시, final_answer는 "pandas를 사용하여..."로 작성
|
|
397
|
+
- **final_answer는 실제 실행된 코드를 정확히 반영**해야 합니다.
|
|
398
|
+
|
|
399
|
+
## 🚨 import 문 보존 규칙 (CRITICAL!)
|
|
400
|
+
|
|
401
|
+
**코드를 수정할 때 import 문은 절대로 주석 처리하지 마세요!**
|
|
402
|
+
|
|
403
|
+
```python
|
|
404
|
+
# ❌ 잘못된 예시 - import까지 주석 처리 → 후속 Step에서 NameError 발생
|
|
405
|
+
# import matplotlib.pyplot as plt ← 이렇게 하면 안 됨!
|
|
406
|
+
# import matplotlib.font_manager as fm
|
|
407
|
+
|
|
408
|
+
# ✅ 올바른 예시 - import는 유지하고 문제 코드만 수정
|
|
409
|
+
import matplotlib.pyplot as plt # 반드시 유지!
|
|
410
|
+
import matplotlib.font_manager as fm # 반드시 유지!
|
|
411
|
+
|
|
412
|
+
# 문제가 있는 부분만 try-except로 감싸거나 제거
|
|
413
|
+
try:
|
|
414
|
+
# 한글 폰트 설정 등 문제 코드
|
|
415
|
+
pass
|
|
416
|
+
except Exception:
|
|
417
|
+
pass
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**규칙**: matplotlib, pandas, numpy, seaborn 등의 import 문은 항상 유지하세요. 문제가 생기면 import 이후의 코드만 수정하세요.
|
|
421
|
+
|
|
422
|
+
## 🚨 Matplotlib API 금지 규칙 (CRITICAL!)
|
|
423
|
+
|
|
424
|
+
**⛔ tick_params()에서 절대 사용 금지:**
|
|
425
|
+
- ❌ `ax.tick_params(ha='right')` - ValueError 발생!
|
|
426
|
+
- ❌ `ax.tick_params(horizontalalignment='right')` - ValueError 발생!
|
|
427
|
+
- ❌ `ax.tick_params(va='center')` - ValueError 발생!
|
|
428
|
+
|
|
429
|
+
**✅ 레이블 정렬이 필요하면 반드시 이 방법 사용:**
|
|
430
|
+
```python
|
|
431
|
+
# 올바른 방법: plt.setp() 사용
|
|
432
|
+
plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
|
|
433
|
+
|
|
434
|
+
# 또는 plt.xticks() 사용
|
|
435
|
+
plt.xticks(rotation=45, ha='right')
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## 🚨 Dask DataFrame 금지 규칙 (CRITICAL!)
|
|
439
|
+
|
|
440
|
+
**⛔ .head() 결과에 .compute() 절대 사용 금지:**
|
|
441
|
+
- ❌ `df.head().compute()` - AttributeError 발생! head()는 이미 pandas!
|
|
442
|
+
- ❌ `df.head(1000).compute()` - AttributeError 발생!
|
|
443
|
+
- ❌ `df[['col1', 'col2']].head(5000).compute()` - 컬럼 선택 후에도 금지!
|
|
444
|
+
- ❌ `sample_df = df.head(100); sample_df.compute()` - head() 결과는 이미 pandas!
|
|
445
|
+
|
|
446
|
+
**✅ head()는 직접 사용 (compute 불필요):**
|
|
447
|
+
```python
|
|
448
|
+
# 올바른 방법: head()는 이미 pandas DataFrame 반환
|
|
449
|
+
sample_df = df.head(1000) # 이미 pandas!
|
|
450
|
+
sample_df = df[['col1', 'col2']].head(5000) # 이미 pandas!
|
|
451
|
+
# 바로 시각화나 분석에 사용하면 됨
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**⛔ corr() 사용 시 문자열 컬럼 포함 금지:**
|
|
455
|
+
- ❌ `df.corr().compute()` - 문자열 컬럼이 있으면 ValueError 발생!
|
|
456
|
+
|
|
457
|
+
**✅ 반드시 숫자형 컬럼만 선택 후 사용:**
|
|
458
|
+
```python
|
|
459
|
+
# 올바른 방법: 숫자형 컬럼만 선택
|
|
460
|
+
numeric_cols = df.select_dtypes(include=['number']).columns.tolist()
|
|
461
|
+
correlation_matrix = df[numeric_cols].corr().compute()
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**⛔ value_counts().unstack() 사용 금지:**
|
|
465
|
+
- ❌ `df.groupby('Sex')['Survived'].value_counts().unstack().compute()` - Dask Series에는 unstack() 메서드 없음! AttributeError 발생!
|
|
466
|
+
|
|
467
|
+
**✅ 대체 방법: compute 후 unstack 또는 crosstab 사용:**
|
|
468
|
+
```python
|
|
469
|
+
# 방법 1: groupby + size + compute 후 unstack
|
|
470
|
+
cross_tab = df.groupby(['Sex', 'Survived']).size().compute().unstack(fill_value=0)
|
|
471
|
+
|
|
472
|
+
# 방법 2: pandas crosstab (compute 후 적용)
|
|
473
|
+
sample = df[['Sex', 'Survived']].compute()
|
|
474
|
+
cross_tab = pd.crosstab(sample['Sex'], sample['Survived'])
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## 출력 형식 (JSON)
|
|
478
|
+
|
|
479
|
+
**⚠️ 중요: 응답은 간결하게!**
|
|
480
|
+
- `root_cause`: 1-2문장으로 간결하게 작성
|
|
481
|
+
- `reasoning`: 1-2문장으로 간결하게 작성
|
|
482
|
+
- 장황한 설명 금지!
|
|
483
|
+
|
|
484
|
+
```json
|
|
485
|
+
{{
|
|
486
|
+
"analysis": {{
|
|
487
|
+
"root_cause": "근본 원인을 1-2문장으로 간결하게 (한국어)",
|
|
488
|
+
"is_approach_problem": true/false,
|
|
489
|
+
"missing_prerequisites": ["누락된 선행 작업들"]
|
|
490
|
+
}},
|
|
491
|
+
"decision": "refine | insert_steps | replace_step | replan_remaining",
|
|
492
|
+
"reasoning": "결정 이유를 1-2문장으로 간결하게 (한국어)",
|
|
493
|
+
"changes": {{
|
|
494
|
+
// decision이 "refine"인 경우:
|
|
495
|
+
"refined_code": "수정된 코드",
|
|
496
|
+
|
|
497
|
+
// decision이 "insert_steps"인 경우 (예: 패키지 설치):
|
|
498
|
+
// ⚠️ 중요: 에러메시지의 패키지명 사용! (예: pyarrow, 사용자코드의 dask 아님!)
|
|
499
|
+
"new_steps": [
|
|
500
|
+
{{
|
|
501
|
+
"description": "에러메시지에서 확인된 패키지(예: pyarrow) 설치",
|
|
502
|
+
"toolCalls": [{{"tool": "jupyter_cell", "parameters": {{"code": "!pip install {PIP_INDEX_OPTION} --timeout 180 에러메시지의_패키지명"}}}}]
|
|
503
|
+
}}
|
|
504
|
+
],
|
|
505
|
+
|
|
506
|
+
// decision이 "replace_step"인 경우:
|
|
507
|
+
"replacement": {{
|
|
508
|
+
"description": "새 단계 설명",
|
|
509
|
+
"toolCalls": [{{"tool": "jupyter_cell", "parameters": {{"code": "코드"}}}}]
|
|
510
|
+
}},
|
|
511
|
+
|
|
512
|
+
// decision이 "replan_remaining"인 경우 (final_answer 필수 포함!):
|
|
513
|
+
"new_plan": [
|
|
514
|
+
{{
|
|
515
|
+
"description": "단계 설명",
|
|
516
|
+
"toolCalls": [{{"tool": "jupyter_cell", "parameters": {{"code": "코드"}}}}]
|
|
517
|
+
}},
|
|
518
|
+
{{
|
|
519
|
+
"description": "최종 결과 제시",
|
|
520
|
+
"toolCalls": [{{"tool": "final_answer", "parameters": {{"answer": "실제 사용된 방법을 반영한 완료 메시지"}}}}]
|
|
521
|
+
}}
|
|
522
|
+
]
|
|
523
|
+
}}
|
|
524
|
+
}}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## 🚨 출력 형식 필수 규칙 (CRITICAL!)
|
|
528
|
+
|
|
529
|
+
**⛔ 절대 금지:**
|
|
530
|
+
- ❌ 마크다운 형식 (## 분석, **굵은 글씨** 등) 출력 금지!
|
|
531
|
+
- ❌ 설명, 해설, 주석 출력 금지!
|
|
532
|
+
- ❌ "다음은...", "분석 결과..." 같은 서두 금지!
|
|
533
|
+
|
|
534
|
+
**✅ 필수:**
|
|
535
|
+
- JSON 코드 블록만 출력하세요!
|
|
536
|
+
- ```json 으로 시작하고 ``` 으로 끝나야 합니다!
|
|
537
|
+
|
|
538
|
+
**올바른 응답 예시:**
|
|
539
|
+
```json
|
|
540
|
+
{{
|
|
541
|
+
"analysis": {{...}},
|
|
542
|
+
"decision": "refine",
|
|
543
|
+
"reasoning": "...",
|
|
544
|
+
"changes": {{...}}
|
|
545
|
+
}}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
위 형식으로만 응답하세요. 다른 텍스트는 절대 포함하지 마세요!"""
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
552
|
+
# 구조화된 계획 생성 프롬프트 (Enhanced Planning with Checkpoints)
|
|
553
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
554
|
+
|
|
555
|
+
STRUCTURED_PLAN_PROMPT = """당신은 Jupyter 노트북을 위한 Python 코드 전문가입니다.
|
|
556
|
+
사용자의 요청을 체계적으로 분석하고, 검증 가능한 단계별 실행 계획을 생성하세요.
|
|
557
|
+
|
|
558
|
+
## 분석 프레임워크
|
|
559
|
+
|
|
560
|
+
### 1. 문제 분해 (Problem Decomposition)
|
|
561
|
+
- 핵심 목표는 무엇인가?
|
|
562
|
+
- 필수 단계와 선택적 단계는 무엇인가?
|
|
563
|
+
- 각 단계의 입력과 출력은 무엇인가?
|
|
564
|
+
|
|
565
|
+
### 2. 의존성 분석 (Dependency Analysis)
|
|
566
|
+
- 어떤 라이브러리가 필요한가?
|
|
567
|
+
- 단계 간 데이터 흐름은 어떠한가?
|
|
568
|
+
- 어떤 변수/객체가 단계 간에 공유되는가?
|
|
569
|
+
|
|
570
|
+
### 3. 위험도 평가 (Risk Assessment)
|
|
571
|
+
- 실패 가능성이 높은 단계는?
|
|
572
|
+
- 외부 의존성(API, 파일, 네트워크)이 있는 단계는?
|
|
573
|
+
- 실행 시간이 오래 걸릴 수 있는 단계는?
|
|
574
|
+
|
|
575
|
+
### 4. 검증 전략 (Validation Strategy)
|
|
576
|
+
- 각 단계의 성공을 어떻게 확인할 수 있는가?
|
|
577
|
+
- 예상 출력 형태는 무엇인가?
|
|
578
|
+
- 체크포인트 기준은 무엇인가?
|
|
579
|
+
|
|
580
|
+
## 사용 가능한 도구
|
|
581
|
+
|
|
582
|
+
### 기본 도구 (셀 작업)
|
|
583
|
+
1. **jupyter_cell**: Python 코드 셀 생성 (노트북 끝에 추가)
|
|
584
|
+
- parameters: {{"code": "Python 코드"}}
|
|
585
|
+
- **항상 새 셀을 노트북 끝에 추가합니다**
|
|
586
|
+
|
|
587
|
+
2. **markdown**: 마크다운 설명 셀 생성 (노트북 끝에 추가)
|
|
588
|
+
- parameters: {{"content": "마크다운 텍스트"}}
|
|
589
|
+
|
|
590
|
+
3. **final_answer**: 작업 완료 및 최종 답변
|
|
591
|
+
- parameters: {{"answer": "최종 답변 텍스트", "summary": "작업 요약(선택)"}}
|
|
592
|
+
|
|
593
|
+
### 확장 도구 (파일/터미널)
|
|
594
|
+
4. **read_file**: 파일 읽기 (절대경로/.. 금지)
|
|
595
|
+
- parameters: {{"path": "상대경로"}}
|
|
596
|
+
|
|
597
|
+
5. **write_file**: 파일 쓰기 (승인 필요)
|
|
598
|
+
- parameters: {{"path": "상대경로", "content": "내용"}}
|
|
599
|
+
|
|
600
|
+
6. **list_files**: 디렉토리 조회
|
|
601
|
+
- parameters: {{"path": ".", "recursive": false, "pattern": "*.py"}}
|
|
602
|
+
|
|
603
|
+
7. **execute_command**: 셸 명령 (위험 명령만 승인)
|
|
604
|
+
- parameters: {{"command": "pip list"}}
|
|
605
|
+
|
|
606
|
+
8. **search_files**: 파일 내용 검색
|
|
607
|
+
- parameters: {{"pattern": "def func", "path": "src"}}
|
|
608
|
+
|
|
609
|
+
## 🔴 핵심 원칙: 항상 새 셀을 아래에 추가!
|
|
610
|
+
|
|
611
|
+
**⛔ 기존 셀을 수정하지 마세요! 항상 새 셀을 노트북 끝에 추가합니다.**
|
|
612
|
+
|
|
613
|
+
이 방식의 장점:
|
|
614
|
+
- 기존 코드 히스토리가 보존됨
|
|
615
|
+
- 사용자가 이전/이후 코드를 비교할 수 있음
|
|
616
|
+
- 실행 순서가 명확해짐
|
|
617
|
+
- 롤백이 쉬움 (불필요한 셀만 삭제하면 됨)
|
|
618
|
+
|
|
619
|
+
## 노트북 컨텍스트 (참고용 - 기존 코드를 수정하지 마세요!)
|
|
620
|
+
|
|
621
|
+
- 셀 개수: {cell_count}
|
|
622
|
+
- 임포트된 라이브러리: {imported_libraries}
|
|
623
|
+
- 정의된 변수: {defined_variables}
|
|
624
|
+
- 최근 셀 내용 (참고용):
|
|
625
|
+
{recent_cells}
|
|
626
|
+
|
|
627
|
+
**참고**: 위 기존 셀들은 수정하지 않습니다. 필요한 코드는 새 셀로 추가하세요.
|
|
628
|
+
|
|
629
|
+
## 사용자 요청
|
|
630
|
+
|
|
631
|
+
{request}
|
|
632
|
+
|
|
633
|
+
## ⚠️ 초기 설정 (첫 번째 코드 셀에 포함)
|
|
634
|
+
|
|
635
|
+
**먼저 "설치된 패키지" 목록을 확인하세요!**
|
|
636
|
+
- 필요한 라이브러리가 없으면 `!pip install {PIP_INDEX_OPTION} --timeout 180 패키지명` 형식으로 설치 단계를 먼저 추가하세요.
|
|
637
|
+
|
|
638
|
+
첫 번째 코드 셀 예시 (설치된 패키지에 따라 조정):
|
|
639
|
+
```python
|
|
640
|
+
# === 경고 필터링 ===
|
|
641
|
+
import warnings
|
|
642
|
+
warnings.filterwarnings('ignore', category=RuntimeWarning)
|
|
643
|
+
warnings.filterwarnings('ignore', category=FutureWarning)
|
|
644
|
+
|
|
645
|
+
# === 기본 라이브러리 import (pandas, numpy는 대부분 설치되어 있음) ===
|
|
646
|
+
import pandas as pd
|
|
647
|
+
import numpy as np
|
|
648
|
+
|
|
649
|
+
# === 시각화 라이브러리 (설치 확인 후 import) ===
|
|
650
|
+
# matplotlib, seaborn이 설치된 패키지 목록에 있는 경우에만 import
|
|
651
|
+
import matplotlib.pyplot as plt
|
|
652
|
+
import seaborn as sns
|
|
653
|
+
|
|
654
|
+
# === 한글 폰트 설정 (선택적 - matplotlib 설치된 경우) ===
|
|
655
|
+
try:
|
|
656
|
+
import matplotlib.font_manager as fm
|
|
657
|
+
korean_fonts = ['Apple SD Gothic Neo', 'Malgun Gothic', 'NanumGothic', 'Noto Sans CJK KR']
|
|
658
|
+
available = set(f.name for f in fm.fontManager.ttflist)
|
|
659
|
+
for font in korean_fonts:
|
|
660
|
+
if font in available:
|
|
661
|
+
plt.rcParams['font.family'] = font
|
|
662
|
+
break
|
|
663
|
+
plt.rcParams['axes.unicode_minus'] = False
|
|
664
|
+
except Exception:
|
|
665
|
+
pass # 폰트 설정 실패해도 계속 진행
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**🔴 중요**:
|
|
669
|
+
- **설치되지 않은 라이브러리는 import하지 마세요!** 먼저 `!pip install {PIP_INDEX_OPTION} --timeout 180 패키지명` 단계를 추가하세요.
|
|
670
|
+
- import 문은 **절대로** 주석 처리하지 마세요! 문제가 생기면 한글 폰트 설정 블록(try 블록)만 수정하세요.
|
|
671
|
+
|
|
672
|
+
## 🔍 파일 탐색 규칙 (중요!)
|
|
673
|
+
|
|
674
|
+
사용자 요청에 **파일명이 언급된 경우**, 반드시 다음 순서로 처리하세요:
|
|
675
|
+
|
|
676
|
+
1. **로컬 파일 탐색 우선**: 먼저 `os.listdir()`, `glob.glob()` 등으로 현재 디렉토리 및 하위 디렉토리에서 해당 파일을 탐색합니다
|
|
677
|
+
2. **파일 존재 확인**: `os.path.exists()` 또는 유사한 방법으로 파일 존재 여부를 확인합니다
|
|
678
|
+
3. **경로 출력**: 발견된 파일의 전체 경로를 출력하여 사용자에게 알립니다
|
|
679
|
+
4. **파일이 없는 경우**: 파일을 찾을 수 없으면 명확한 에러 메시지를 제공합니다
|
|
680
|
+
|
|
681
|
+
예시:
|
|
682
|
+
- "train.csv 파일을 로드해줘" → 먼저 `glob.glob('**/train.csv', recursive=True)`로 파일 탐색
|
|
683
|
+
- "data.xlsx를 읽어줘" → 먼저 로컬에서 해당 파일 검색 후 로드
|
|
684
|
+
|
|
685
|
+
## 📊 시각화 전 데이터 검증 (중요!)
|
|
686
|
+
|
|
687
|
+
**시각화하기 전에 항상 데이터가 비어있는지 확인하세요!**
|
|
688
|
+
|
|
689
|
+
빈 데이터로 `.plot()` 호출 시 `IndexError`가 발생합니다. 다음 패턴을 사용하세요:
|
|
690
|
+
|
|
691
|
+
```python
|
|
692
|
+
# ❌ 잘못된 예시 - 빈 데이터일 때 에러 발생
|
|
693
|
+
missing_pct[missing_pct > 0].head(20).plot(kind='bar')
|
|
694
|
+
|
|
695
|
+
# ✅ 올바른 예시 - 데이터 존재 여부 확인
|
|
696
|
+
data_to_plot = missing_pct[missing_pct > 0].head(20)
|
|
697
|
+
if len(data_to_plot) > 0:
|
|
698
|
+
data_to_plot.plot(kind='bar')
|
|
699
|
+
plt.title('결측치 비율')
|
|
700
|
+
plt.show()
|
|
701
|
+
else:
|
|
702
|
+
print("시각화할 데이터가 없습니다 (결측치 없음)")
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
## 출력 형식 (JSON)
|
|
706
|
+
|
|
707
|
+
```json
|
|
708
|
+
{{
|
|
709
|
+
"analysis": {{
|
|
710
|
+
"problem_decomposition": {{
|
|
711
|
+
"core_goal": "핵심 목표",
|
|
712
|
+
"essential_steps": ["필수 단계 목록"],
|
|
713
|
+
"optional_steps": ["선택적 단계 목록"]
|
|
714
|
+
}},
|
|
715
|
+
"dependency_analysis": {{
|
|
716
|
+
"required_libraries": ["필요한 라이브러리"],
|
|
717
|
+
"data_flow": "데이터 흐름 설명",
|
|
718
|
+
"shared_variables": ["공유 변수"]
|
|
719
|
+
}},
|
|
720
|
+
"risk_assessment": {{
|
|
721
|
+
"high_risk_steps": [1, 2],
|
|
722
|
+
"external_dependencies": ["외부 의존성"],
|
|
723
|
+
"estimated_complexity": "low | medium | high"
|
|
724
|
+
}}
|
|
725
|
+
}},
|
|
726
|
+
"reasoning": "계획 수립 이유에 대한 설명",
|
|
727
|
+
"plan": {{
|
|
728
|
+
"totalSteps": 단계_수,
|
|
729
|
+
"steps": [
|
|
730
|
+
{{
|
|
731
|
+
"stepNumber": 1,
|
|
732
|
+
"description": "단계 설명 (한국어)",
|
|
733
|
+
"toolCalls": [
|
|
734
|
+
{{
|
|
735
|
+
"tool": "jupyter_cell",
|
|
736
|
+
"parameters": {{
|
|
737
|
+
"code": "Python 코드"
|
|
738
|
+
}}
|
|
739
|
+
}}
|
|
740
|
+
],
|
|
741
|
+
"dependencies": [],
|
|
742
|
+
"checkpoint": {{
|
|
743
|
+
"expectedOutcome": "예상 결과",
|
|
744
|
+
"validationCriteria": ["검증 기준 1", "검증 기준 2"],
|
|
745
|
+
"successIndicators": ["성공 지표"]
|
|
746
|
+
}},
|
|
747
|
+
"riskLevel": "low | medium | high"
|
|
748
|
+
}}
|
|
749
|
+
]
|
|
750
|
+
}}
|
|
751
|
+
}}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
JSON만 출력하세요. 다른 텍스트 없이."""
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
758
|
+
# Reflection 프롬프트 (실행 결과 분석 및 적응적 조정)
|
|
759
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
760
|
+
|
|
761
|
+
REFLECTION_PROMPT = """실행 결과를 분석하고 다음 단계에 대한 조정을 제안하세요.
|
|
762
|
+
|
|
763
|
+
## 실행된 단계
|
|
764
|
+
|
|
765
|
+
- 단계 번호: {step_number}
|
|
766
|
+
- 설명: {step_description}
|
|
767
|
+
- 실행된 코드:
|
|
768
|
+
```python
|
|
769
|
+
{executed_code}
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
## 실행 결과
|
|
773
|
+
|
|
774
|
+
- 상태: {execution_status}
|
|
775
|
+
- 출력:
|
|
776
|
+
```
|
|
777
|
+
{execution_output}
|
|
778
|
+
```
|
|
779
|
+
- 오류 (있는 경우):
|
|
780
|
+
```
|
|
781
|
+
{error_message}
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
## 체크포인트 기준
|
|
785
|
+
|
|
786
|
+
- 예상 결과: {expected_outcome}
|
|
787
|
+
- 검증 기준: {validation_criteria}
|
|
788
|
+
|
|
789
|
+
## 남은 단계
|
|
790
|
+
|
|
791
|
+
{remaining_steps}
|
|
792
|
+
|
|
793
|
+
## 분석 요청
|
|
794
|
+
|
|
795
|
+
1. **결과 평가**: 실행 결과가 예상과 일치하는가?
|
|
796
|
+
2. **성공/실패 요인**: 무엇이 잘 되었고 무엇이 문제인가?
|
|
797
|
+
3. **다음 단계 영향**: 이 결과가 남은 단계에 어떤 영향을 미치는가?
|
|
798
|
+
4. **조정 제안**: 계획을 수정해야 하는가?
|
|
799
|
+
|
|
800
|
+
## 출력 형식 (JSON)
|
|
801
|
+
|
|
802
|
+
```json
|
|
803
|
+
{{
|
|
804
|
+
"evaluation": {{
|
|
805
|
+
"checkpoint_passed": true/false,
|
|
806
|
+
"output_matches_expected": true/false,
|
|
807
|
+
"confidence_score": 0.0-1.0
|
|
808
|
+
}},
|
|
809
|
+
"analysis": {{
|
|
810
|
+
"success_factors": ["성공 요인들"],
|
|
811
|
+
"failure_factors": ["실패 요인들"],
|
|
812
|
+
"unexpected_outcomes": ["예상치 못한 결과들"]
|
|
813
|
+
}},
|
|
814
|
+
"impact_on_remaining": {{
|
|
815
|
+
"affected_steps": [단계_번호들],
|
|
816
|
+
"severity": "none | minor | major | critical",
|
|
817
|
+
"description": "영향 설명"
|
|
818
|
+
}},
|
|
819
|
+
"recommendations": {{
|
|
820
|
+
"action": "continue | adjust | retry | replan",
|
|
821
|
+
"adjustments": [
|
|
822
|
+
{{
|
|
823
|
+
"step_number": 단계_번호,
|
|
824
|
+
"change_type": "modify_code | add_step | remove_step | change_approach",
|
|
825
|
+
"description": "변경 설명",
|
|
826
|
+
"new_content": "새 코드 또는 내용 (필요한 경우)"
|
|
827
|
+
}}
|
|
828
|
+
],
|
|
829
|
+
"reasoning": "조정 이유"
|
|
830
|
+
}}
|
|
831
|
+
}}
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
JSON만 출력하세요."""
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
838
|
+
# 최종 답변 생성 프롬프트
|
|
839
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
840
|
+
|
|
841
|
+
FINAL_ANSWER_PROMPT = """작업이 완료되었습니다. 결과를 요약해주세요.
|
|
842
|
+
|
|
843
|
+
## 원래 요청
|
|
844
|
+
|
|
845
|
+
{original_request}
|
|
846
|
+
|
|
847
|
+
## 실행된 단계
|
|
848
|
+
|
|
849
|
+
{executed_steps}
|
|
850
|
+
|
|
851
|
+
## 생성된 출력
|
|
852
|
+
|
|
853
|
+
{outputs}
|
|
854
|
+
|
|
855
|
+
## 지침
|
|
856
|
+
|
|
857
|
+
1. 작업 결과를 간결하게 요약하세요
|
|
858
|
+
2. 주요 발견사항이나 결과를 강조하세요
|
|
859
|
+
3. 다음 단계에 대한 제안이 있으면 포함하세요
|
|
860
|
+
4. 한국어로 작성하세요
|
|
861
|
+
5. **변수명이 아닌 실제 계산된 값을 사용하세요** (예: "n_rows 행" 대신 "891 행")
|
|
862
|
+
|
|
863
|
+
## 출력
|
|
864
|
+
|
|
865
|
+
간결한 요약 텍스트 (200자 이내)"""
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
869
|
+
# 유틸리티 함수
|
|
870
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
def format_plan_prompt(
|
|
874
|
+
request: str,
|
|
875
|
+
cell_count: int,
|
|
876
|
+
imported_libraries: list,
|
|
877
|
+
defined_variables: list,
|
|
878
|
+
recent_cells: list,
|
|
879
|
+
available_libraries: list = None,
|
|
880
|
+
detected_libraries: list = None, # LibraryDetector로 감지된 라이브러리
|
|
881
|
+
rag_context: str = None, # RAG 검색 결과 컨텍스트 (primary)
|
|
882
|
+
) -> str:
|
|
883
|
+
"""
|
|
884
|
+
실행 계획 생성 프롬프트 포맷팅
|
|
885
|
+
|
|
886
|
+
지식 주입 우선순위:
|
|
887
|
+
1. RAG 컨텍스트가 있으면 RAG 결과 사용 (시맨틱 검색)
|
|
888
|
+
2. RAG가 없으면 KnowledgeBase fallback (전체 API 가이드 로드)
|
|
889
|
+
"""
|
|
890
|
+
# 최근 셀 내용 포맷팅 (참고용으로만 표시) - 최대 5개 셀, 각 150자
|
|
891
|
+
recent_cells_text = ""
|
|
892
|
+
max_cells = min(5, len(recent_cells)) # 최대 5개 셀만
|
|
893
|
+
for i, cell in enumerate(recent_cells[-max_cells:]): # 마지막 5개만
|
|
894
|
+
source = cell.get("source", "")[:150] # 최대 150자
|
|
895
|
+
cell_index = cell.get("index", i)
|
|
896
|
+
recent_cells_text += (
|
|
897
|
+
f"\n[셀 {cell_index}]: {source[:100]}...\n"
|
|
898
|
+
if len(source) > 100
|
|
899
|
+
else f"\n[셀 {cell_index}]: {source}\n"
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
# 기본 프롬프트 생성
|
|
903
|
+
base_prompt = PLAN_GENERATION_PROMPT.format(
|
|
904
|
+
request=request,
|
|
905
|
+
cell_count=cell_count,
|
|
906
|
+
imported_libraries=", ".join(imported_libraries)
|
|
907
|
+
if imported_libraries
|
|
908
|
+
else "없음",
|
|
909
|
+
defined_variables=", ".join(defined_variables) if defined_variables else "없음",
|
|
910
|
+
recent_cells=recent_cells_text if recent_cells_text else "없음",
|
|
911
|
+
available_libraries=", ".join(available_libraries)
|
|
912
|
+
if available_libraries
|
|
913
|
+
else "정보 없음",
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
# 지식 주입: RAG primary, KnowledgeBase fallback
|
|
917
|
+
if rag_context:
|
|
918
|
+
# RAG 결과가 있으면 RAG 사용 (시맨틱 검색 기반)
|
|
919
|
+
print(f"[RAG] 컨텍스트 주입됨: {len(rag_context)} chars")
|
|
920
|
+
base_prompt = base_prompt.replace(
|
|
921
|
+
"## JSON 출력", f"{rag_context}\n\n## JSON 출력"
|
|
922
|
+
)
|
|
923
|
+
elif detected_libraries:
|
|
924
|
+
# RAG가 없으면 KnowledgeBase fallback (전체 API 가이드)
|
|
925
|
+
from hdsp_agent_core.knowledge.loader import get_knowledge_loader
|
|
926
|
+
|
|
927
|
+
knowledge_loader = get_knowledge_loader()
|
|
928
|
+
library_knowledge = knowledge_loader.format_knowledge_section(
|
|
929
|
+
detected_libraries
|
|
930
|
+
)
|
|
931
|
+
|
|
932
|
+
if library_knowledge:
|
|
933
|
+
if len(library_knowledge) > 2000:
|
|
934
|
+
library_knowledge = library_knowledge[:2000] + "\n... (생략)"
|
|
935
|
+
print(
|
|
936
|
+
f"[KnowledgeBase Fallback] 라이브러리 지식 주입됨: {detected_libraries} ({len(library_knowledge)} chars)"
|
|
937
|
+
)
|
|
938
|
+
base_prompt = base_prompt.replace(
|
|
939
|
+
"## JSON 출력", f"{library_knowledge}\n\n## JSON 출력"
|
|
940
|
+
)
|
|
941
|
+
else:
|
|
942
|
+
print(
|
|
943
|
+
f"[KnowledgeBase] 주입할 라이브러리 지식 없음. detected={detected_libraries}"
|
|
944
|
+
)
|
|
945
|
+
else:
|
|
946
|
+
print("[Knowledge] RAG 컨텍스트 없음, 감지된 라이브러리 없음")
|
|
947
|
+
|
|
948
|
+
return base_prompt
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
def format_refine_prompt(
|
|
952
|
+
original_code: str,
|
|
953
|
+
error_type: str,
|
|
954
|
+
error_message: str,
|
|
955
|
+
traceback: str,
|
|
956
|
+
attempt: int,
|
|
957
|
+
max_attempts: int,
|
|
958
|
+
available_libraries: list,
|
|
959
|
+
defined_variables: list,
|
|
960
|
+
) -> str:
|
|
961
|
+
"""에러 수정 프롬프트 포맷팅"""
|
|
962
|
+
return ERROR_REFINEMENT_PROMPT.format(
|
|
963
|
+
original_code=original_code,
|
|
964
|
+
error_type=error_type,
|
|
965
|
+
error_message=error_message,
|
|
966
|
+
traceback=traceback,
|
|
967
|
+
attempt=attempt,
|
|
968
|
+
max_attempts=max_attempts,
|
|
969
|
+
available_libraries=", ".join(available_libraries)
|
|
970
|
+
if available_libraries
|
|
971
|
+
else "pandas, numpy, matplotlib",
|
|
972
|
+
defined_variables=", ".join(defined_variables) if defined_variables else "없음",
|
|
973
|
+
)
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
def format_final_answer_prompt(
|
|
977
|
+
original_request: str, executed_steps: list, outputs: list
|
|
978
|
+
) -> str:
|
|
979
|
+
"""최종 답변 프롬프트 포맷팅"""
|
|
980
|
+
steps_text = "\n".join(
|
|
981
|
+
[
|
|
982
|
+
f"- Step {s.get('stepNumber', i + 1)}: {s.get('description', '완료')}"
|
|
983
|
+
for i, s in enumerate(executed_steps)
|
|
984
|
+
]
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
outputs_text = "\n".join(
|
|
988
|
+
[f"[출력 {i + 1}]: {str(o)[:200]}" for i, o in enumerate(outputs)]
|
|
989
|
+
)
|
|
990
|
+
|
|
991
|
+
return FINAL_ANSWER_PROMPT.format(
|
|
992
|
+
original_request=original_request,
|
|
993
|
+
executed_steps=steps_text if steps_text else "없음",
|
|
994
|
+
outputs=outputs_text if outputs_text else "없음",
|
|
995
|
+
)
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
def format_replan_prompt(
|
|
999
|
+
original_request: str,
|
|
1000
|
+
executed_steps: list,
|
|
1001
|
+
failed_step: dict,
|
|
1002
|
+
error_info: dict,
|
|
1003
|
+
execution_output: str = "",
|
|
1004
|
+
available_libraries: list = None,
|
|
1005
|
+
) -> str:
|
|
1006
|
+
"""Adaptive Replanning 프롬프트 포맷팅"""
|
|
1007
|
+
# 실행된 단계 텍스트 (코드 포함)
|
|
1008
|
+
executed_text_parts = []
|
|
1009
|
+
if executed_steps:
|
|
1010
|
+
for i, s in enumerate(executed_steps):
|
|
1011
|
+
step_num = s.get("stepNumber", i + 1)
|
|
1012
|
+
step_desc = s.get("description", "완료")
|
|
1013
|
+
executed_text_parts.append(f"- Step {step_num}: {step_desc} ✅")
|
|
1014
|
+
|
|
1015
|
+
# 이 스텝에서 실행한 코드 추가
|
|
1016
|
+
tool_calls = s.get("toolCalls", [])
|
|
1017
|
+
for tc in tool_calls:
|
|
1018
|
+
if tc.get("tool") == "jupyter_cell":
|
|
1019
|
+
code = tc.get("parameters", {}).get("code", "")
|
|
1020
|
+
if code:
|
|
1021
|
+
# 코드를 간략하게 표시 (처음 3줄 또는 전체)
|
|
1022
|
+
code_lines = code.split("\n")
|
|
1023
|
+
if len(code_lines) > 5:
|
|
1024
|
+
code_preview = "\n".join(code_lines[:5]) + "\n ...(생략)"
|
|
1025
|
+
else:
|
|
1026
|
+
code_preview = code
|
|
1027
|
+
executed_text_parts.append(
|
|
1028
|
+
f" 코드:\n {code_preview.replace(chr(10), chr(10) + ' ')}"
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
executed_text = "\n".join(executed_text_parts) if executed_text_parts else "없음"
|
|
1032
|
+
|
|
1033
|
+
# 실패한 코드 추출
|
|
1034
|
+
failed_code = ""
|
|
1035
|
+
if failed_step.get("toolCalls"):
|
|
1036
|
+
for tc in failed_step["toolCalls"]:
|
|
1037
|
+
if tc.get("tool") == "jupyter_cell":
|
|
1038
|
+
failed_code = tc.get("parameters", {}).get("code", "")
|
|
1039
|
+
break
|
|
1040
|
+
|
|
1041
|
+
# traceback 처리
|
|
1042
|
+
traceback_data = error_info.get("traceback", [])
|
|
1043
|
+
if isinstance(traceback_data, list):
|
|
1044
|
+
traceback_str = "\n".join(traceback_data)
|
|
1045
|
+
else:
|
|
1046
|
+
traceback_str = str(traceback_data) if traceback_data else ""
|
|
1047
|
+
|
|
1048
|
+
# errorName (Python 예외 이름)이 있으면 우선 사용, 없으면 type 필드 사용
|
|
1049
|
+
# 예: "ModuleNotFoundError", "ImportError", "TypeError" 등
|
|
1050
|
+
error_type = error_info.get("errorName") or error_info.get("type", "runtime")
|
|
1051
|
+
|
|
1052
|
+
return ADAPTIVE_REPLAN_PROMPT.format(
|
|
1053
|
+
original_request=original_request,
|
|
1054
|
+
executed_steps=executed_text,
|
|
1055
|
+
failed_step_number=failed_step.get("stepNumber", "?"),
|
|
1056
|
+
failed_step_description=failed_step.get("description", ""),
|
|
1057
|
+
failed_code=failed_code,
|
|
1058
|
+
error_type=error_type, # Python 예외 이름 (ModuleNotFoundError 등)
|
|
1059
|
+
error_message=error_info.get("message", "Unknown error"),
|
|
1060
|
+
traceback=traceback_str,
|
|
1061
|
+
execution_output=execution_output if execution_output else "없음",
|
|
1062
|
+
available_libraries=", ".join(available_libraries)
|
|
1063
|
+
if available_libraries
|
|
1064
|
+
else "정보 없음",
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def format_structured_plan_prompt(
|
|
1069
|
+
request: str,
|
|
1070
|
+
cell_count: int,
|
|
1071
|
+
imported_libraries: list,
|
|
1072
|
+
defined_variables: list,
|
|
1073
|
+
recent_cells: list,
|
|
1074
|
+
) -> str:
|
|
1075
|
+
"""구조화된 계획 생성 프롬프트 포맷팅 (Enhanced Planning)"""
|
|
1076
|
+
recent_cells_text = ""
|
|
1077
|
+
for i, cell in enumerate(recent_cells):
|
|
1078
|
+
cell_type = cell.get("type", "code")
|
|
1079
|
+
source = cell.get("source", "")[:300]
|
|
1080
|
+
recent_cells_text += (
|
|
1081
|
+
f"\n[셀 {cell.get('index', i)}] ({cell_type}):\n```\n{source}\n```\n"
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
return STRUCTURED_PLAN_PROMPT.format(
|
|
1085
|
+
request=request,
|
|
1086
|
+
cell_count=cell_count,
|
|
1087
|
+
imported_libraries=", ".join(imported_libraries)
|
|
1088
|
+
if imported_libraries
|
|
1089
|
+
else "없음",
|
|
1090
|
+
defined_variables=", ".join(defined_variables) if defined_variables else "없음",
|
|
1091
|
+
recent_cells=recent_cells_text if recent_cells_text else "없음",
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def format_reflection_prompt(
|
|
1096
|
+
step_number: int,
|
|
1097
|
+
step_description: str,
|
|
1098
|
+
executed_code: str,
|
|
1099
|
+
execution_status: str,
|
|
1100
|
+
execution_output: str,
|
|
1101
|
+
error_message: str,
|
|
1102
|
+
expected_outcome: str,
|
|
1103
|
+
validation_criteria: list,
|
|
1104
|
+
remaining_steps: list,
|
|
1105
|
+
) -> str:
|
|
1106
|
+
"""Reflection 프롬프트 포맷팅 (실행 결과 분석)"""
|
|
1107
|
+
# 검증 기준 텍스트
|
|
1108
|
+
criteria_text = (
|
|
1109
|
+
"\n".join([f"- {c}" for c in validation_criteria])
|
|
1110
|
+
if validation_criteria
|
|
1111
|
+
else "없음"
|
|
1112
|
+
)
|
|
1113
|
+
|
|
1114
|
+
# 남은 단계 텍스트
|
|
1115
|
+
remaining_text = (
|
|
1116
|
+
"\n".join(
|
|
1117
|
+
[
|
|
1118
|
+
f"- Step {s.get('stepNumber', i + 1)}: {s.get('description', '')}"
|
|
1119
|
+
for i, s in enumerate(remaining_steps)
|
|
1120
|
+
]
|
|
1121
|
+
)
|
|
1122
|
+
if remaining_steps
|
|
1123
|
+
else "없음"
|
|
1124
|
+
)
|
|
1125
|
+
|
|
1126
|
+
return REFLECTION_PROMPT.format(
|
|
1127
|
+
step_number=step_number,
|
|
1128
|
+
step_description=step_description,
|
|
1129
|
+
executed_code=executed_code,
|
|
1130
|
+
execution_status=execution_status,
|
|
1131
|
+
execution_output=execution_output if execution_output else "없음",
|
|
1132
|
+
error_message=error_message if error_message else "없음",
|
|
1133
|
+
expected_outcome=expected_outcome if expected_outcome else "성공적 실행",
|
|
1134
|
+
validation_criteria=criteria_text,
|
|
1135
|
+
remaining_steps=remaining_text,
|
|
1136
|
+
)
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
1140
|
+
# LLM Fallback 에러 분석 프롬프트 (패턴 매칭 실패 시 사용)
|
|
1141
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
1142
|
+
|
|
1143
|
+
ERROR_ANALYSIS_PROMPT = """에러를 분석하고 복구 전략을 결정하세요.
|
|
1144
|
+
|
|
1145
|
+
## 에러 정보
|
|
1146
|
+
|
|
1147
|
+
- 오류 유형: {error_type}
|
|
1148
|
+
- 오류 메시지: {error_message}
|
|
1149
|
+
- 트레이스백:
|
|
1150
|
+
```
|
|
1151
|
+
{traceback}
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
## 이전 시도 횟수: {previous_attempts}
|
|
1155
|
+
|
|
1156
|
+
## 이전 코드 (있는 경우)
|
|
1157
|
+
{previous_codes}
|
|
1158
|
+
|
|
1159
|
+
## 복구 전략 선택지
|
|
1160
|
+
|
|
1161
|
+
1. **refine**: 코드 수정으로 해결 가능한 에러
|
|
1162
|
+
- SyntaxError, TypeError, ValueError, KeyError 등 단순 코드 버그
|
|
1163
|
+
|
|
1164
|
+
2. **insert_steps**: 선행 작업이 필요한 경우
|
|
1165
|
+
- 패키지 설치가 필요한 경우 (ModuleNotFoundError)
|
|
1166
|
+
- 데이터 전처리가 필요한 경우
|
|
1167
|
+
|
|
1168
|
+
3. **replace_step**: 완전히 다른 접근법이 필요한 경우
|
|
1169
|
+
- 현재 방법이 근본적으로 작동하지 않는 경우
|
|
1170
|
+
- 대안적 라이브러리/알고리즘이 필요한 경우
|
|
1171
|
+
|
|
1172
|
+
4. **replan_remaining**: 남은 모든 단계를 재계획해야 하는 경우
|
|
1173
|
+
- 시스템 레벨 문제 (dlopen 에러 등)
|
|
1174
|
+
- 전체 접근법 변경이 필요한 경우
|
|
1175
|
+
|
|
1176
|
+
## 분석 지침
|
|
1177
|
+
|
|
1178
|
+
1. 에러의 근본 원인을 파악하세요
|
|
1179
|
+
2. 이전 시도 횟수를 고려하세요 (2회 이상 실패 시 다른 전략 고려)
|
|
1180
|
+
3. 에러 메시지와 트레이스백을 면밀히 분석하세요
|
|
1181
|
+
4. 가장 효율적인 복구 전략을 선택하세요
|
|
1182
|
+
|
|
1183
|
+
## 출력 형식 (JSON)
|
|
1184
|
+
|
|
1185
|
+
```json
|
|
1186
|
+
{{
|
|
1187
|
+
"analysis": {{
|
|
1188
|
+
"root_cause": "에러의 근본 원인 (1-2문장)",
|
|
1189
|
+
"is_approach_problem": true/false,
|
|
1190
|
+
"missing_prerequisites": ["누락된 선행 작업들"],
|
|
1191
|
+
"complexity": "simple | moderate | complex"
|
|
1192
|
+
}},
|
|
1193
|
+
"decision": "refine | insert_steps | replace_step | replan_remaining",
|
|
1194
|
+
"reasoning": "결정 이유 (1-2문장)",
|
|
1195
|
+
"confidence": 0.0-1.0,
|
|
1196
|
+
"changes": {{
|
|
1197
|
+
// decision이 "refine"인 경우:
|
|
1198
|
+
"refined_code": null,
|
|
1199
|
+
|
|
1200
|
+
// decision이 "insert_steps"인 경우:
|
|
1201
|
+
"new_steps": [
|
|
1202
|
+
{{
|
|
1203
|
+
"description": "단계 설명",
|
|
1204
|
+
"toolCalls": [{{"tool": "jupyter_cell", "parameters": {{"code": "코드"}}}}]
|
|
1205
|
+
}}
|
|
1206
|
+
],
|
|
1207
|
+
|
|
1208
|
+
// decision이 "replace_step"인 경우:
|
|
1209
|
+
"replacement": {{
|
|
1210
|
+
"description": "새 단계 설명",
|
|
1211
|
+
"toolCalls": [{{"tool": "jupyter_cell", "parameters": {{"code": "코드"}}}}]
|
|
1212
|
+
}},
|
|
1213
|
+
|
|
1214
|
+
// decision이 "replan_remaining"인 경우:
|
|
1215
|
+
"new_plan": []
|
|
1216
|
+
}}
|
|
1217
|
+
}}
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
JSON만 출력하세요."""
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
def format_error_analysis_prompt(
|
|
1224
|
+
error_type: str,
|
|
1225
|
+
error_message: str,
|
|
1226
|
+
traceback: str,
|
|
1227
|
+
previous_attempts: int = 0,
|
|
1228
|
+
previous_codes: list = None,
|
|
1229
|
+
) -> str:
|
|
1230
|
+
"""LLM Fallback 에러 분석 프롬프트 포맷팅"""
|
|
1231
|
+
previous_codes = previous_codes or []
|
|
1232
|
+
codes_text = ""
|
|
1233
|
+
if previous_codes:
|
|
1234
|
+
for i, code in enumerate(previous_codes[-3:], 1): # 최근 3개만
|
|
1235
|
+
codes_text += f"\n### 시도 {i}:\n```python\n{code[:500]}\n```\n"
|
|
1236
|
+
else:
|
|
1237
|
+
codes_text = "없음"
|
|
1238
|
+
|
|
1239
|
+
return ERROR_ANALYSIS_PROMPT.format(
|
|
1240
|
+
error_type=error_type,
|
|
1241
|
+
error_message=error_message[:500] if error_message else "없음",
|
|
1242
|
+
traceback=traceback[:1000] if traceback else "없음",
|
|
1243
|
+
previous_attempts=previous_attempts,
|
|
1244
|
+
previous_codes=codes_text,
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
1249
|
+
# 프롬프트 치환: {PIP_INDEX_OPTION} placeholder를 실제 값으로 교체
|
|
1250
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
1251
|
+
|
|
1252
|
+
# 모든 프롬프트에서 {PIP_INDEX_OPTION}을 실제 값으로 치환
|
|
1253
|
+
# - 로컬 환경: 빈 문자열 → `!pip install --timeout 180 패키지명`
|
|
1254
|
+
# - 내부망: "--index-url <url>" → `!pip install --index-url <url> --timeout 180 패키지명`
|
|
1255
|
+
PLAN_GENERATION_PROMPT = PLAN_GENERATION_PROMPT.replace(
|
|
1256
|
+
"{PIP_INDEX_OPTION}", PIP_INDEX_OPTION
|
|
1257
|
+
)
|
|
1258
|
+
ADAPTIVE_REPLAN_PROMPT = ADAPTIVE_REPLAN_PROMPT.replace(
|
|
1259
|
+
"{PIP_INDEX_OPTION}", PIP_INDEX_OPTION
|
|
1260
|
+
)
|