docs: 응답 품질 문서 세트 보강 — OpenClaw 레퍼런스, 확정 원인, 범위 분리

- 리서치: OpenClaw Context Engine/compaction 분석 §7 추가, 리스크 보강
- 계획: OpenClaw 레퍼런스 링크, 비범위에 compaction/Context Engine 명시
- 트러블슈팅: 원인 가설 → 확정 원인으로 갱신 (리서치 §2 기반)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
happybell80 2026-03-19 22:02:49 +09:00
parent 8fc7f421e8
commit 4903acdbee
3 changed files with 83 additions and 10 deletions

View File

@ -6,9 +6,14 @@ tags: [rb8001, prompt, tone, context, quality, plan]
## 관련 문서
- [트러블슈팅: rb8001 프롬프트·의도분석·문맥응답 품질 문제](../troubleshooting/260317_rb8001_prompt_intent_context_response_quality_문제오픈.md)
- [리서치: 톤 과장·문맥 유실 전수 조사](../research/260319_rb8001_응답품질_톤과장_문맥유실_전수조사_리서치.md)
- [리서치: 톤 과장·문맥 유실 전수 조사](../research/260319_rb8001_응답품질_톤과장_문맥유실_전수조사_리서치.md) (§7 OpenClaw 레퍼런스 포함)
- [프롬프트 DB 폐루프 P1 구현 완료](../worklog/260319_프롬프트DB_폐루프_P1_구현_및_검증완료.md)
### 레퍼런스
- OpenClaw 소스: `/home/admin/robeing/reference/openclaw` — Context Engine 패턴, compaction 알고리즘 참고
- 이번 계획에서 가져오는 것: OpenClaw `assemble()`**provider별 형식 변환** 패턴만
- 향후 별도 추적: compaction, session-memory, MEMORY.md → [OpenClaw compaction 아이디어](../ideas/260317_OpenClaw_스타일_컨텍스트_compaction_아이디어.md)
### 상위 원칙
- [0_VALUE Coding Principles](https://github.com/happybell80/0_VALUE/blob/main/02_Governance/coding-principles.md) — #1 원인 직접 수정, #4 폴백 절제, #9 질문별 특례 금지
@ -34,6 +39,9 @@ tags: [rb8001, prompt, tone, context, quality, plan]
- Company X RAG 실패 응답 개선 (23에서 작업 중)
- 의도 분류기 구조 변경 (별도 이슈)
- Pydantic JSON 강제 (1~3단계 효과 확인 후 필요 시 추가)
- Context Engine 중간 계층 도입 (리서치 §7-5, 향후 과제)
- Compaction 도입 ([아이디어 문서](../ideas/260317_OpenClaw_스타일_컨텍스트_compaction_아이디어.md)에서 별도 추적)
- Session Memory / MEMORY.md 도입 (향후 과제)
## 구현 단계

View File

@ -197,7 +197,69 @@ class ChatResponse(BaseModel):
- `length_instruction` 기본값을 "중간" → "짧게"로 변경
- 호칭 지시를 조건부로 (첫 대화에만 사용, 이후 생략)
## 7. 리스크
## 7. OpenClaw 레퍼런스 분석
**소스:** `/home/admin/robeing/reference/openclaw` (commit `7f86be1037`)
### 7-1. OpenClaw의 컨텍스트 처리 구조
```
User Message
→ SessionManager.open() — JSONL에서 전체 대화 이력 로드
→ sanitizeSessionHistory() — provider별 메시지 형식 정규화
→ contextEngine.assemble(messages, tokenBudget) — 토큰 예산 내로 조립
→ LLM 호출
→ afterTurn() — 배경에서 auto-compaction
```
핵심 파일:
- 컨텍스트 엔진 인터페이스: `src/context-engine/types.ts`
- Compaction 알고리즘: `src/agents/compaction.ts`
- 세션 이력 정규화: `src/agents/pi-embedded-runner/google.ts:520+`
- 컨텍스트 조립: `src/agents/pi-embedded-runner/run/attempt.ts:2130-2189`
### 7-2. OpenClaw Context Engine의 핵심 메서드
| 메서드 | 역할 |
|--------|------|
| `assemble(messages, tokenBudget)` | 메시지를 토큰 예산 내로 조립, **provider별 형식 변환 포함** |
| `compact(sessionFile, tokenBudget)` | 오래된 메시지를 요약 압축 |
| `ingest(message)` | 새 메시지 추가 |
| `afterTurn()` | 턴 완료 후 배경 compaction |
**플러그인 방식:** `registerContextEngine()`으로 엔진 교체 가능.
### 7-3. OpenClaw의 provider 정규화 (`sanitizeSessionHistory`)
OpenClaw는 OpenAI/Gemini/Anthropic 각각 다른 메시지 형식을 **하나의 정규화 단계**에서 통일한 후 handler에 전달. 구체적으로:
- 이미지 해상도/형식 정규화
- thinking block 제거 (정책에 따라)
- tool_use/tool_result 페어링 수리
- OpenAI용 function call 다운그레이드
- 세션 간 메시지 경계 주석
**로빙과의 차이:** 로빙은 이 정규화 계층이 없음. OpenAI handler는 `previous_messages`, Gemini handler는 `recent_conversations`를 각자 읽어서 provider별 키 불일치가 발생.
### 7-4. OpenClaw의 Compaction 알고리즘
1. 메시지를 토큰 기준으로 청크 분할 (SAFETY_MARGIN 1.2, 적응적 청크 비율)
2. 청크별 LLM 요약 생성
3. 재귀적 병합 (부분 요약 → 통합 요약)
4. 식별자(UUID, URL, 파일명) 보존 강제
5. 실패 시 점진적 fallback: 전체 요약 → 부분 요약 → 개수만 기록
### 7-5. 로빙에 적용할 것 (범위 분리)
| 적용 대상 | 이번 계획 범위 | 향후 (별도 문서) |
|----------|-------------|---------------|
| provider별 형식 변환 | ✅ `recent_conversations``previous_messages` 변환 | Context Engine 중간 계층 도입 |
| 토큰 예산 관리 | ❌ | `assemble()` 패턴 도입 |
| Compaction | ❌ | [OpenClaw compaction 아이디어](../ideas/260317_OpenClaw_스타일_컨텍스트_compaction_아이디어.md)에서 추적 |
| 세션 메모리 | ❌ | daily log + MEMORY.md 도입 |
**이번 수정의 위치:** OpenClaw의 `assemble()` 중 "provider별 형식 변환" 부분만 가져옴. `llm_service.py``process_request()`에서 `recent_conversations`를 OpenAI `messages[]` 형식으로 변환.
## 8. 리스크
| 리스크 | 대응 |
|--------|------|
@ -205,8 +267,9 @@ class ChatResponse(BaseModel):
| JSON 강제 시 자연스러움 저하 | 1~2단계 선적용 후 필요 시에만 3단계 |
| 문맥 이력 5개 추가 시 토큰 증가 | 이미 Gemini에서 3개 사용 중, OpenAI 5개는 허용 범위 |
| 감정 constraints 제거 시 감정 대응 약화 | neutral 외 감정에서는 유지 |
| Context Engine 없이 키 변환만 하면 또 다른 handler 추가 시 같은 문제 재발 | 향후 Context Engine 도입 시 해소. 이번은 최소 수정 원칙 (SSOT #2 Minimal Change) |
## 8. 구현 순서
## 9. 구현 순서
| 순서 | 내용 | 종결 효과 |
|------|------|----------|
@ -218,7 +281,7 @@ class ChatResponse(BaseModel):
1~4 완료 시 트러블슈팅 종결 가능.
## 9. 결론
## 10. 결론
- 톤 과장의 원인: 시스템 프롬프트의 "도움이 되는 방향으로 조언", "액션 아이템 제시" 문구 + 감정 constraints의 무조건 주입
- 문맥 유실의 원인: OpenAI handler가 `previous_messages` 키를 읽는데, 이 키를 아무도 채우지 않음

View File

@ -39,13 +39,15 @@ tags: [robeing, rb8001, prompt, intent, context, rag, troubleshooting]
6. 실제 응답 톤은 기본 시스템 프롬프트보다 감정 분석, 호칭(`이사님`), task별 `system_instruction` 누적의 영향을 크게 받습니다.
7. Company X 질문은 `try_companyx_grounding()`이 먼저 가로채며, 근거 부족 시 실패 응답으로 조기 종료될 수 있습니다.
## 원인 가설
## 원인 (2026-03-19 리서치에서 확정)
1. 일반 대화와 RAG 질문이 지나치게 보수적인 실패 경로로 수렴합니다.
2. 짧은 후속 질문 컨텍스트 유지 로직이 실제 Slack 실사용 문장(`난 지금 서울이야``내가 지금 어디라고?`)에 안정적으로 작동하지 않습니다.
3. 호칭/감정 주입 프롬프트가 기본 답변 스타일보다 앞서 작동해 과한 의전형 문체를 만듭니다.
4. `weather`, `location`, `followup` 같은 생활형 질의를 별도 정책 없이 `general_chat` 또는 fallback 대화로 흘려보냅니다.
5. `companyx_rag` 경로는 근거 부족 시 실패 응답 보호는 되지만, 질문 적합도 재시도나 대체 응답 전략이 약합니다.
상세는 [리서치 §2](../research/260319_rb8001_응답품질_톤과장_문맥유실_전수조사_리서치.md) 참조.
1. **톤 과장**: 시스템 프롬프트의 "항상 도움이 되는 방향으로 조언", "액션 아이템 제시" 문구가 매 응답마다 과잉 서비스를 유도. (`openai_handler.py:206~226`)
2. **문맥 유실**: OpenAI handler가 `previous_messages` 키를 읽는데, 이 키를 아무도 채우지 않음. `recent_conversations`는 로드되지만 OpenAI 형식으로 변환 안 됨. (`llm_service.py`, `openai_handler.py:64`)
3. **과잉 제안**: "묻지 않은 것은 제안하지 마라" 지시 부재 + 응답 길이 제한 없음.
4. **감정 constraints 무조건 주입**: neutral 감정에서도 "자연스럽게 중간 응답하세요"가 붙어 응답을 늘림. (`llm_service.py:262~268`)
5. `companyx_rag` 경로의 실패 응답 전략은 23에서 별도 작업 중 (이번 범위 제외).
## 코드 기준 구조 요약