feat: 채널 기반 비즈니스 용어 사전 (Semantic Federation) 구현#235
Conversation
- SemanticFederationTool(/term_custom): 채널(팀)/전사/개인 3계층 용어 등록·조회·삭제 - OrgSetupTool(/org_setup): DB 스캔 + LLM으로 팀별 용어 자동 추출 → 전사(guild) 레이어 저장 - Discord UI: /term_custom 호출 시 범위 selectbox → 용어 입력 modal (2단계 UX) - 채널이 팀 경계 역할 → 팀 멤버십 관리 불필요, 충돌 설계상 없음 - lookup: 개인 > 채널(팀) > 전사(guild) (narrow→wide, git branch 방식) - kv_list_prefix() 추가 + LIKE 와일드카드 이스케이프 보안 수정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
seyoung4503
left a comment
There was a problem hiding this comment.
기능 방향성 좋습니다 👍 테스트 113개 통과, 충돌 없음 확인했고, Claude + Codex 두 관점으로 교차검토했어요. 머지 전에 하나만 짚고 가면 좋겠고, 하나는 결정 사항입니다. 나머지는 후속 가능한 제안이에요. 🙏
꼭 짚고 싶은 것 (blocking 🚨)
/org_setup자동 추출 용어가 전사(guild) 레이어로 저장돼 팀별 격리가 안 됩니다 — PR의 핵심 목적과 충돌
결정 필요 (🟠)
- 전사 용어 등록·
/org_setup·clear에 권한 체크가 없어 누구나 전사 프롬프트를 바꿀 수 있음
논의했으면 (question/nit) — 인라인 코멘트 참고
| ) | ||
| ctx.store.kv_set( | ||
| scope, | ||
| f"{_SEMFED_PREFIX}:{term.lower()}:guild", |
There was a problem hiding this comment.
🚨 blocking — /org_setup이 추출 용어를 전부 cterm:{term}:guild(전사 공통)로 저장하는데, 그러면 팀별 격리가 안 됩니다. 같은 guild에서 A팀·B팀이 각각 돌리면 동명 용어가 서로 덮어써요 (예: 재무팀 "활성고객"을 마케팅팀이 덮어씀). PR의 목적이 "채널=팀 경계"인데 자동 추출 경로만 이걸 우회합니다. docstring(:11)도 cterm:{term}:team:{org}라고 적혀 있어서, 채널/org 레이어로 저장하는 게 의도였던 것 같아요. 저장 키를 바꾸는 게 어떨까요? 🙏
| }, | ||
| ) | ||
|
|
||
| async def run(self, args: dict[str, Any], ctx: "HarnessContext") -> ToolResult: |
There was a problem hiding this comment.
🟠 결정 필요 — /org_setup·/org_setup clear와 전사 용어 등록에 is_admin 체크가 없어 보여요. Identity.is_admin은 이미 있는데 신규 툴에서 안 쓰는 것 같습니다. 전사 용어는 모든 멤버의 SQL 생성에 영향을 줘서, 아무나 등록/clear 가능하면 실수나 장난으로 전체가 영향받을 수 있어요. 위 scope 파라미터를 넣는다면 "전사 선택 시 관리자만" 가드를 같이 두면 좋겠습니다. (LLM이 term_custom(layer=guild)로도 부를 수 있어서 가드는 툴 run() 안에 둬야 실효가 있어요.)
이건 필요한 작업이라고 보는데, 이번 PR에서 같이 갈지 / 후속 PR로 뺄지 어떻게 생각하세요? 🔐
|
|
||
| scope = ctx.identity.guild_id or f"dm:{ctx.identity.user_id}" | ||
| user_id = ctx.identity.user_id or "unknown" | ||
| channel_id = ctx.identity.thread_id or ctx.identity.channel_id or "" |
There was a problem hiding this comment.
❓ question — channel_id = thread_id or channel_id라 스레드 안에서는 채널이 아니라 스레드 ID로 조회되는데요. 부모 채널에서 등록한 channel 용어가 스레드 질문엔 안 잡히고, 반대도 마찬가지일 것 같아요. "채널=팀" 의도와 어긋날 수 있는데, 스레드를 부모 채널로 정규화하는 게 맞지 않을까요? 🤔
| # cterm:{term}:guild 형식으로 저장 → 전사 공통 레이어 (narrow→wide 최하단) | ||
| saved_terms: list[str] = [] | ||
| for t in terms: | ||
| term = str(t.get("term", "")).strip() |
There was a problem hiding this comment.
🟡 nit — term_custom은 : 포함 term을 거부하는데(semantic_federation.py:146) org_setup은 검증이 없네요. LLM이 : 든 term을 뱉으면 cterm:{term}:guild 키가 깨져서 _load_all의 split(":",3)이 레이어를 오판 → 조회에서 조용히 누락될 수 있어요. 동일한 : 검증을 추가하면 좋겠습니다.
| handlers.enrich(to_identity(_interaction_context(interaction)), table=table, clear=clear), | ||
| ) | ||
|
|
||
| @tree.command(name="term_custom", description="비즈니스 용어 등록·조회·삭제 (조직/팀/개인 범위)") |
There was a problem hiding this comment.
🟡 nit — /term_custom remove에 layer 인자가 없어서 기본값 member만 삭제되는 것 같아요. wizard로 guild/channel 용어를 만들 수 있는데 슬래시로는 그 레이어를 못 지우는 비대칭이 있습니다.
| self._ctx_factory = ctx_factory | ||
|
|
||
| async def on_submit(self, interaction: discord.Interaction) -> None: | ||
| await interaction.response.defer(ephemeral=True, thinking=True) |
There was a problem hiding this comment.
🟡 nit — defer()가 try 밖에 있어서, defer 자체나 except 안의 followup이 실패하면(webhook 만료 등) 여전히 처리가 안 됩니다. 일반 케이스 무한로딩은 잘 막았어요 👍 — 엣지만 참고로요.
|
237에서 일부 해결된 이슈들이 있어 237 기준으로 일괄 작업 예정 |
#️⃣ Issue Number
📝 요약(Summary)
/org_setup으로 DB 스캔 + LLM 자동 추출,/term_custom으로 Discord modal 기반 수동 등록이 가능합니다.[주요 커멘드 목록]
/setup : DB 연결 (guided form)
/org_setup : 테이블 스캔 + LLM 용어 자동 추출 및 저장
/term_custom : 비즈니스 용어 등록·조회·삭제
/semantic_show : 현재 채널에 적용 중인 정의 보기
/enrich : DB 컬럼 메타데이터 자동 보강
💬 To Reviewers (선택)
org/team/member + entity명 직접 입력방식이었으나, 팀 멤버십 관리 문제를 피하기 위해 채널 기반으로 전환했습니다.)kv_list_prefix의 LIKE 이스케이프 처리 방식(ESCAPE '!')이 SQLite에서 안전한지 검토 부탁드립니다.org_setup clear시inferred=True인 항목만 삭제하는 정책이 적절한지 의견 부탁드립니다.PR Checklist
pytest -q113개 전체 통과SemanticFederationTool(/term_custom): 채널/전사/개인 계층 용어 CRUDOrgSetupTool(/org_setup): DB 스캔 + LLM 용어 자동 추출kv_list_prefix()추가 + LIKE 와일드카드 이스케이프 (ESCAPE '!')channel_idfallback 불일치 수정 (DM에서 채널 용어 누락 방지)org_setup clear범위 제한 (수동 등록 용어 보존)term_wizard.on_submit예외 처리 (무한 로딩 방지)