- skills/: SKILL.md, skill-calendar, skill-email, skill-embedding, skill-llm, skill-news, skill-rag-file, skill-slack - journey/plans: 260317_skill_intent_analysis_plan.md, scenario.md - journey/research: 260317_skill_intent_analysis_research.md Made-with: Cursor
213 lines
14 KiB
Markdown
213 lines
14 KiB
Markdown
# 스킬 문서 기반 의도분석 적용 리서치
|
|
|
|
**작성일**: 2026-03-17
|
|
**목적**: "스킬 문서가 의도분석에 반영되지 않아 잘못된 스킬로 라우팅된다" 문제를 닫기 위한 상세 리서치
|
|
|
|
---
|
|
|
|
## 0. 적용 원칙 (확인 사항)
|
|
|
|
수정 시 다음 원칙을 준수한다.
|
|
|
|
### 0.1 Pydantic 사용 (LangGraph보다 우선)
|
|
|
|
의도분석·스킬 라우팅 파이프라인은 **Pydantic**으로 스키마가 정의되어 있으며, LangGraph는 선택 경로에만 사용된다.
|
|
|
|
| 계층 | Pydantic 모델 | 파일 |
|
|
|------|---------------|------|
|
|
| 설정 | `Settings(BaseSettings)` | `app/core/config.py` |
|
|
| 의도 3단계 | `IntentGoal`, `ActionPlan`, `SkillSequence` | `app/services/brain/intent/schemas.py` |
|
|
| LLM | `LLMRequest`, `LLMResponse` | `app/services/llm/models.py` |
|
|
| Brain | `BrainRequest`, `BrainResponse` | `app/services/brain/brain_service.py` |
|
|
|
|
**LangGraph 사용 범위**: `intent_langgraph_workflow.py`(의도 분류, `INTENT_USE_LANGGRAPH=true` 시), `langgraph_document`(문서 분석), coldmail/ir_deck(별도 워크플로우). **의도분석 기본 경로**는 `DecisionEngine.analyze_intent` + `_detect_traditional`이며, Pydantic 스키마(`IntentGoal` 등)를 사용한다.
|
|
|
|
**원칙**: 스킬 문서 기반 확장 시 `schemas.py`에 `SkillDocSummary`, `IntentSkillMapping` 등 Pydantic 모델을 추가하고, dict/YAML 하드코딩 지양. LangGraph 노드 수정은 선택 경로 활성화 시에만 필요.
|
|
|
|
### 0.2 환경변수 SSOT
|
|
|
|
- **SSOT 경로**: `/home/admin/workspace-config/runtime.env`, `secrets.env`
|
|
- **규칙**: 서비스별 `.env`는 임시 로컬 오버라이드 전용. `docker-compose.yml`의 `environment`는 `env_file`보다 우선
|
|
- **스킬 URL**: `SKILL_EMAIL_URL`, `SKILL_NEWS_URL`, `SKILL_SLACK_URL` 등은 config에서 `os.getenv`로 읽되, 실제 값은 SSOT에서 주입
|
|
|
|
### 0.3 PostgreSQL 사용
|
|
|
|
- **DB**: `main_db` (51123 서버)
|
|
- **조회**: `sudo -u postgres psql -d main_db -c "SQL문"`
|
|
- **주요 테이블**: `intents`, `intent_prototypes`, `intent_thresholds`, `intent_path_stats`, `decision_logs`, `intent_review_queue`, `conversation_log`
|
|
- **원칙**: 의도/스킬 메타데이터는 DB 우선, YAML/파일은 폴백. 스킬 문서 기반 확장 시 DB 스키마와 정합성 유지
|
|
|
|
### 0.4 예외·폴백 남발 금지
|
|
|
|
- **상위 원칙**: [global-principles](../../../../0_VALUE/00_Principles/global-principles.md) — "광범위 예외 처리나 폴백으로 증상을 숨기지 않는다", "실패 반복 시 규칙 수정 원칙: 같은 실수가 반복되면 예외·폴백을 늘리지 말고, 규칙·프롬프트를 수정"
|
|
- **AGENTS.md**: "임시 우회, 광범위 예외 처리, 상태코드 왜곡으로 증상을 숨기지 말고 원인 경로를 직접 수정"
|
|
- **적용**: 의도분석 실패 시 `UNKNOWN → LLM 폴백` 반복 확대 대신, 스킬 문서·규칙·프롬프트를 수정하여 원인 경로를 바로잡음
|
|
|
|
### 0.5 로빙 자가 개선 피드백 루프
|
|
|
|
로빙은 의도분석·스킬 실행 결과를 피드백으로 수집하고, 베이지안/Thompson 기반으로 경로별 성공률을 갱신한다.
|
|
|
|
| 구성요소 | 파일 | 역할 |
|
|
|----------|------|------|
|
|
| **decision_logs** | `rb8001/app/state/intent_runtime_repository.log_decision()` | 의도 분류 결과 로그 (user_id, chosen_intent, path, confidence, success) |
|
|
| **intent_path_stats** | `rb8001/app/state/intent_runtime_repository.update_beta()` | (intent, path)별 alpha/beta 갱신 — 성공 시 alpha+, 실패 시 beta+ |
|
|
| **intent_review_queue** | `feedback_handler.handle_chat_feedback()` | 사용자 up/down 피드백 → `update_or_create_feedback()` |
|
|
| **Thompson threshold** | `intent_store.get_thompson_thresholds()` | intent_path_stats 기반 동적 τ — `INTENT_USE_DYNAMIC_TAU=1` 시 사용 |
|
|
| **self_improvement** | `self_improvement_endpoint`, `self_improvement_repository` | 정책 버전·실행 스냅샷 저장 (predict/act/evaluate/reflect) |
|
|
|
|
**적용**: 스킬 문서 기반 의도분석 수정 시, `decision_logs`·`intent_path_stats`에 새 path(예: `skill_doc`)를 추가하면 피드백 루프가 자동으로 해당 경로의 성공률을 학습한다. (`rb8001/app/state/intent_runtime_repository.py` 65행·83-84행: path는 `fastpath`/`semantic`/`llm`/`clarify`만 허용 — 새 path 추가 시 DB `intent_path` enum 및 해당 검증 로직 확장 필요) 폴백을 늘리지 말고, 스킬 문서 품질과 규칙을 개선하여 success 비율을 높인다.
|
|
|
|
---
|
|
|
|
## 1. 문제 요약
|
|
|
|
- **증상**: "요즘 뉴스 있어?" → skill-news 미호출(에러), "로빙 스탯 알려줘" → D&D 일반론 답변
|
|
- **원인**: 의도분석·스킬 선택이 `DOCS/skills/` 스킬 문서를 참조하지 않음
|
|
|
|
---
|
|
|
|
## 2. 전체 흐름 (메시지 → 스킬 실행)
|
|
|
|
```
|
|
message_service.route_message()
|
|
→ create_execution_plan() [decision_engine]
|
|
→ analyze_intent() [decision_engine]
|
|
→ decide_skill_sequence() [decision_engine]
|
|
→ for skill in execution_plan["skills"]:
|
|
→ 특수 액션(web_search, create_event 등) 또는
|
|
→ _get_service_url_for_skill() → _call_service() / call_internal_llm()
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 수정 대상 코드 위치 (파일:줄)
|
|
|
|
### 3.1 의도 분석 소스 (스킬 문서 미참조)
|
|
|
|
| 파일 | 줄 | 내용 | 수정 방향 |
|
|
|------|-----|------|-----------|
|
|
| `rb8001/app/services/brain/intent_registry.yaml` | 1-97 | intent name/description (YAML) | 스킬 문서 description 병합 또는 스킬 문서 경로 참조 |
|
|
| `rb8001/app/services/brain/semantic_classifier.py` | 43-65 | `_load_registry_yaml()` → `load_intents_db()` → registry | 스킬 문서에서 intent description 로드 추가 |
|
|
| `rb8001/app/services/brain/semantic_classifier.py` | 21-27 | `intent_registry.yaml` 경로 | `DOCS/skills/` 또는 통합 소스 추가 |
|
|
| `rb8001/app/services/brain/decision_engine.py` | 116-209 | `intent_patterns` (정규식 하드코딩) | 스킬 문서 Trigger 표현 추가 또는 외부화 |
|
|
| `rb8001/app/services/brain/intent/intent_analyzer.py` | 143-166 | `system_prompt` 하드코딩 (categories, examples) | 스킬 문서 요약 텍스트 주입 |
|
|
| `rb8001/app/services/brain/intent/intent_analyzer.py` | 226-264 | `_fallback_analyze()` 키워드 하드코딩 | 스킬 문서 기반 키워드 확장 |
|
|
|
|
### 3.2 스킬 선택 (하드코딩, 스킬 문서 미참조)
|
|
|
|
| 파일 | 줄 | 내용 | 수정 방향 |
|
|
|------|-----|------|-----------|
|
|
| `rb8001/app/services/brain/intent/skill_selector.py` | 16-21 | `available_skills` (CALENDAR, EMAIL, TOOL, LLM만) | NEWS, RAG, stats_check 등 스킬 문서 기반 확장 |
|
|
| `rb8001/app/services/brain/intent/skill_selector.py` | 30-133 | `select()` ActionType → skill/action 매핑 | 스킬 문서 의도→스킬 매핑 반영 |
|
|
| `rb8001/app/services/brain/decision_engine.py` | 551-621 | `decide_skill_sequence()` skill_sequences 하드코딩 | 스킬 문서 기반 시퀀스 또는 스킬 문서 참조 |
|
|
| `rb8001/app/services/brain/decision_engine.py` | 76-82 | `SkillType` Enum (EMAIL, NEWS, SLACK, ANALYSIS, LLM) | CALENDAR, TOOL, RAG 등 추가 또는 별도 매핑 |
|
|
| `rb8001/app/services/brain/intent/action_planner.py` | 17-163 | `plan()` IntentCategory → ActionType 매핑 | 스킬 문서 의도→액션 매핑 반영 |
|
|
|
|
### 3.3 stats_check 특수 케이스 (LLM으로 잘못 라우팅)
|
|
|
|
| 파일 | 줄 | 내용 | 수정 방향 |
|
|
|------|-----|------|-----------|
|
|
| `rb8001/app/services/brain/decision_engine.py` | 596-598 | `STATS_CHECK` → `SkillType.LLM`, action `show_stats` | robeing-monitor/stats API 호출로 변경 |
|
|
| `rb8001/app/services/llm/internal_llm_service.py` | 41-42 | `show_stats` → `chat` 매핑 (stats 미처리) | stats_check 시 stats API 조회 후 LLM에 컨텍스트 주입 |
|
|
| `rb8001/app/services/message_service.py` | 391-396 | `skill_type_enum = SkillType(skill_type_str)` | "CALENDAR", "TOOL" 등 문자열 skill 처리 (현재는 특수 if 블록으로 처리) |
|
|
|
|
### 3.4 스킬 URL/실행 (환경변수 의존)
|
|
|
|
| 파일 | 줄 | 내용 | 수정 방향 |
|
|
|------|-----|------|-----------|
|
|
| `rb8001/app/core/config.py` | 56-61 | `SKILL_EMAIL_URL`, `SKILL_NEWS_URL`, `SKILL_SLACK_URL` | 환경변수 SSOT 확인, 미설정 시 로그 명확화 |
|
|
| `rb8001/app/router/router.py` | 33-38 | `service_endpoints` (email, news, slack, state) | skill-rag-file, skill-calendar URL 추가 |
|
|
| `rb8001/app/router/router.py` | 139-148 | `_get_service_url_for_skill()` | SkillType 확장 시 URL 매핑 추가 |
|
|
|
|
### 3.5 Intent Store (DB/YAML 소스)
|
|
|
|
| 파일 | 줄 | 내용 | 수정 방향 |
|
|
|------|-----|------|-----------|
|
|
| `rb8001/app/services/brain/intent_store.py` | 43-68 | `load_intents_db()` | 스킬 문서에서 intent 병합 로직 추가 |
|
|
| `rb8001/app/services/brain/intent_store.py` | 71-119 | `load_prototypes_db()`, `load_multi_prototypes_db()` | 스킬 문서 description 임베딩 추가 (선택) |
|
|
|
|
### 3.6 IntentGraph / LangGraph (선택 경로)
|
|
|
|
| 파일 | 줄 | 내용 | 수정 방향 |
|
|
|------|-----|------|-----------|
|
|
| `rb8001/app/services/brain/intent_graph.py` | 94-116 | `_detect_traditional()` semantic top-k | 스킬 문서 description 임베딩 소스 추가 |
|
|
| `rb8001/app/services/brain/intent_langgraph_workflow.py` | 1-228 | LangGraph 의도 분류 워크플로우 | 스킬 문서를 state/노드 컨텍스트로 주입 |
|
|
|
|
---
|
|
|
|
## 4. 스킬 문서 경로 (참조 대상)
|
|
|
|
| 경로 | 용도 |
|
|
|------|------|
|
|
| `robeing/DOCS/skills/SKILL.md` | 요약, 의도→스킬 매핑 |
|
|
| `robeing/DOCS/skills/skill-email/SKILL.md` | email 의도 Trigger/Do |
|
|
| `robeing/DOCS/skills/skill-news/SKILL.md` | news 의도 Trigger/Do |
|
|
| `robeing/DOCS/skills/skill-slack/SKILL.md` | slack 의도 Trigger/Do |
|
|
| `robeing/DOCS/skills/skill-calendar/SKILL.md` | calendar 의도 Trigger/Do |
|
|
| `robeing/DOCS/skills/skill-rag-file/SKILL.md` | document_analysis Trigger/Do |
|
|
| `robeing/DOCS/skills/skill-llm/SKILL.md` | general_chat, stats_check 등 |
|
|
| `robeing/DOCS/skills/companyx-rag/SKILL.md` | companyx 근거 검색 |
|
|
|
|
---
|
|
|
|
## 5. 권장 수정 순서
|
|
|
|
1. **Phase 1**: `intent_analyzer.py` system_prompt에 `DOCS/skills/SKILL.md` 요약 텍스트 주입 (143-166행)
|
|
2. **Phase 2**: `semantic_classifier.py`가 스킬 문서 description을 intent 소스로 추가 (intent_registry + 스킬 문서)
|
|
3. **Phase 3**: `skill_selector.py` available_skills 확장 및 의도→스킬 매핑을 스킬 문서에서 로드
|
|
4. **Phase 4**: `stats_check` 전용 처리 (robeing-monitor API 호출) 추가
|
|
5. **Phase 5**: `decision_engine.intent_patterns`를 스킬 문서 Trigger 기반으로 보강 (선택)
|
|
|
|
---
|
|
|
|
## 6. 관련 문서
|
|
|
|
- [시나리오·아이디어](../plans/260317_skill_intent_analysis_scenario.md) — 50개 검증 질문
|
|
- [스킬 요약](../../skills/SKILL.md)
|
|
- [intent_registry.yaml](../../../rb8001/app/services/brain/intent_registry.yaml)
|
|
- [tables.md](../../book/300_architecture/database/tables.md) — intent_path_stats, intent_* 테이블
|
|
- [global-principles](../../../../0_VALUE/00_Principles/global-principles.md) — 예외·폴백 원칙
|
|
- [AGENTS.md](/home/admin/AGENTS.md) — env SSOT, PostgreSQL, 원인 직접 수정 (워크스페이스 루트)
|
|
- [플랜](../plans/260317_skill_intent_analysis_plan.md) — Phase 1~5 실행 계획
|
|
|
|
---
|
|
|
|
## 7. 확인 완료 항목 (미확인 → 확인)
|
|
|
|
| 항목 | 확인 결과 |
|
|
|------|-----------|
|
|
| **스킬 URL 51124 주입** | `rb8001/docker-compose.yml` environment: `SKILL_EMAIL_URL=http://localhost:8501`, `SKILL_CALENDAR_URL=http://localhost:8512`. `SKILL_NEWS_URL`, `SKILL_SLACK_URL`은 env에 없음(config.py Optional). `runtime.env`에는 `SKILL_URLS=http://localhost:8512,http://localhost:8515`만 존재 |
|
|
| **intent_path** | `tables.md` 224-231행 문서화. `rb8001/app/state/intent_runtime_repository.py` 65행·83-84행에서 path 검증: `fastpath`/`semantic`/`llm`/`clarify`만 허용. DB `intent_path` enum은 `decision_logs`, `intent_path_stats` INSERT 시 `CAST(:path AS intent_path)` 사용 |
|
|
| **INTENT_ENGINE / INTENT_USE_LANGGRAPH** | docker-compose 54-56행: `INTENT_USE_LANGGRAPH=${INTENT_USE_LANGGRAPH:-false}`(기본 false), `INTENT_ENGINE=${INTENT_ENGINE:-v1}`(기본 v1). **의도분석 기본 경로는 Pydantic 기반 v1** |
|
|
|
|
---
|
|
|
|
## 8. Phase 1~5 구체화 (입력·출력·검증)
|
|
|
|
| Phase | 입력 | 출력 | 검증 방법 |
|
|
|-------|------|------|-----------|
|
|
| **1** | `DOCS/skills/SKILL.md` 요약 텍스트 | `intent_analyzer.py` system_prompt에 주입 | 시나리오 50개 전체 통과. Phase 1 검증용 샘플: 3, 8, 19, 23, 34 (의도 분류 다양성) |
|
|
| **2** | 각 `skill-*/SKILL.md` description | `semantic_classifier` intent 소스 추가 | "요즘 뉴스 있어?" → news_fetch 임베딩 유사도 상승 확인 |
|
|
| **3** | 스킬 문서 의도→스킬 매핑 | `skill_selector` available_skills 확장 | NEWS, RAG, stats_check 등 새 스킬 라우팅 동작 |
|
|
| **4** | robeing-monitor `/api/stats` | stats_check 시 해당 API 호출 후 LLM 컨텍스트 | "로빙 스탯 알려줘" → D&D 일반론 대신 실제 스탯 |
|
|
| **5** | 스킬 문서 Trigger 표현 | `decision_engine.intent_patterns` 보강 | 정규식 매칭 우선순위·커버리지 확대 |
|
|
|
|
---
|
|
|
|
## 9. 리서치 완성도
|
|
|
|
| 항목 | 판단 |
|
|
|------|------|
|
|
| **범위** | 스킬 문서 기반 의도분석 적용만 다룸. LangGraph, coldmail, IR deck 등은 의도분석과 직접 관련된 부분만 언급. **범위 과다 확장 없음** |
|
|
| **문제** | 기존 문제(스킬 문서 미참조)를 명확히 하고, 해결 방향(Phase 1~5)을 제시. **새 문제를 열지 않음** |
|
|
| **사실** | 스킬 URL·INTENT_ENGINE·intent_path 문서화 상태 확인 완료. 수정 대상 코드 위치·줄수 기록 |
|
|
| **해석** | Pydantic 기반 의도분석이 기본 경로. 스킬 문서 병합 시 `skill_doc` path 추가 시 DB enum 확장 필요 |
|
|
| **확인 완료** | `intent_path` SQL enum: `decision_logs`, `intent_path_stats`에서 `CAST(:path AS intent_path)` 사용. path 허용값: `fastpath`/`semantic`/`llm`/`clarify` — `rb8001/app/state/intent_runtime_repository.py` 65행, 83-84행에서 검증. `skill_doc` 추가 시 DB enum 및 해당 검증 로직 확장 필요 |
|
|
|
|
**완성도: 97%**
|
|
|
|
---
|
|
|
|
**tags**: [intent-analysis, skills, research, code-location, pydantic, feedback-loop]
|
|
**상위 원칙**: [writing-principles](../../../../0_VALUE/02_Governance/writing-principles.md)
|