6a85087b83
전 로컬 LLM 관통 '이드' persona substrate 의 Document Server 측 빌드(W2~W4). 설계 = PKM eid-persona-substrate(r1~r3 수렴) / impl = eid-persona-impl. W2 — compose + 표면 배선: - app/eid/compose.py: persona→rules→overlay→task 단일 system 문자열 + 정적 ROUTE_MAP (런타임 sniffing 아님) + rules 부재 fail-loud · persona 부재 quiet · overflow fail-loud. - 자유-prose 3 표면(react_ask·study_subject_note·study_question_explanation) 중복 정체성· generic 정책 trim + compose 배선(AIClient 에 additive system 파라미터). 도메인 calibration 보존. - STRICT JSON 기계류(briefing_comparative·digest_topic)는 persona-ZERO 동결(불변식 #3). - app/prompts/substrate/: persona(외부 컴파일 산출물 vendor) + rules(생성 가드 서브셋) + overlay 5. W3 — migration + 워커 + study_diagnosis: - migration 301~305: eid_* append-only 원장(약점/복습초안/회고) + approval_requests(가변 큐) + 일정 파생뷰 2. - app/workers/study_weakness.py: study_question_progress.pattern_state 집계로 약점 derived 산출 (LLM 0) + bounded tier(watch/review/focus). nightly cron. - study_diagnosis 표면: 최신 스냅샷을 코치 언어로 번역(약점 판정은 코드, LLM 은 블록 값만 인용). W4-1 — egress 코드층 박탈: - app/eid/ai.py EidAIClient: 이드 표면 = call_primary(내부 MLX) only. 외부 LLM fallback 경로 구조적 봉쇄(call_fallback raise · 자동 fallback 제거 · 외부 endpoint 차단). egress 워커는 분리 유지. load-bearing 정정 3(환경 grounding 강제, 설계 회귀 아님): - rules = 운영 ruleset 전체 → 생성 가드 서브셋(HTML 산출물 룰이 study task 와 충돌). - append-only = REVOKE → CREATE RULE DO INSTEAD NOTHING(단일 owner role 은 REVOKE 무효 + migration 검증기가 plpgsql BEGIN 거부) + actor/source_* NOT NULL 스탬프. - 이드 LLM 봉쇄 = path discipline → EidAIClient 구조화. 검증: eid 순수 단위테스트 30 통과 + py_compile + migration 검증기 모사 + egress 적대감사 COMPLETE. DB/LLM/httpx 의존 테스트(append-only RULE·EidAIClient·E2E)는 staging(Docker) 가동. W4-2 네트워크 belt 은 조건부 보류(코드층 1차 충분, P0-3② 원격 실측 후 hard-gate 시 승격). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42 lines
2.2 KiB
Python
42 lines
2.2 KiB
Python
"""이드 실행 컨텍스트 LLM 클라이언트 — egress 코드층 박탈 (W4-1).
|
|
|
|
설계 0-4 / project_eid_persona_substrate 불변식 #5: 이드 LLM = call_primary(:8801 Mac mini MLX) 만.
|
|
공인 Claude(ai.fallback) 경로를 *구조적으로* 차단 — 같은 fastapi 컨테이너에 합법 egress 워커
|
|
(daily_digest SMTP·law_monitor CalDAV 등)가 import 돼 있어도 이드는 이 클라이언트라 fallback/외부
|
|
endpoint 를 못 부른다(silent fallback 0, rules no-silent-fallback).
|
|
|
|
차단 3중 (코드층 = 1차·확정 가드. 네트워크 default-deny = W4-2 belt, 조건부):
|
|
- call_fallback() → raise (공인 Claude 직접 호출 봉쇄)
|
|
- _call_chat() → 자동 fallback 분기 제거(primary 실패 = re-raise → caller 503)
|
|
- _request() → endpoint 에 anthropic.com 있으면 raise(primary 오결선 방어, 이중보증)
|
|
call_primary / call_triage / embed / rerank 는 그대로(내부 inference·임베딩 허용).
|
|
egress 워커·시스템 경로는 기존 AIClient 유지 — fallback 은 시스템만, 이드만 박탈(분리).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from ai.client import AIClient
|
|
|
|
|
|
class EidEgressBlocked(RuntimeError):
|
|
"""이드 컨텍스트에서 외부 egress(공인 Claude 등) 시도 — 코드층 박탈로 차단."""
|
|
|
|
|
|
class EidAIClient(AIClient):
|
|
"""이드 전용 — call_primary only. fallback/외부 endpoint 구조적 봉쇄. AIClient drop-in."""
|
|
|
|
async def call_fallback(self, prompt: str) -> str:
|
|
raise EidEgressBlocked(
|
|
"이드: 공인 Claude fallback 금지(egress 코드층 박탈). call_primary(:8801) 만 허용."
|
|
)
|
|
|
|
async def _call_chat(self, model_config, prompt: str) -> str:
|
|
# 자동 fallback 분기 제거 — primary 실패는 그대로 raise(caller 가 503 매핑, silent fallback 0).
|
|
return await self._request(model_config, prompt)
|
|
|
|
async def _request(self, model_config, prompt: str, system: str | None = None) -> str:
|
|
endpoint = getattr(model_config, "endpoint", "") or ""
|
|
if "anthropic.com" in endpoint:
|
|
raise EidEgressBlocked(f"이드: 외부 endpoint 차단 ({endpoint}). 내부 inference 만.")
|
|
return await super()._request(model_config, prompt, system=system)
|