v0.2.1
릴리스이전 버전
서버 SDK 교체만으로 끝납니다 — .env / main.py / app/*.py 코드 변경 없이 wheel + uv.lock + pyproject.toml 1줄 교체. 단, A2A 클라이언트 측은 가드레일 차단 응답의 형태 변경에 분기 코드 수정이 필요합니다 (아래 Breaking 섹션 참조).
신규 기능 (0.2.1)
섹션 제목: “신규 기능 (0.2.1)”| 영역 | 변경 | 분류 |
|---|---|---|
| 가드레일 차단 응답 v0.3 정합화 | block 정책 위반 시 응답이 state:"failed" + errorCode:"GUARDRAIL_BLOCKED" ApplicationErrorData 로 전환. 위반 메타는 data[0].guardrail sub-object 에 보존, matchedPatterns 는 id 만 노출(rule name 누설 방지), artifacts[] 폐기. A2A 에러 처리 | Breaking — 응답 형태 |
| Streaming buffer-then-validate | output_guardrail 활성 시 stream 이 자동으로 invoke 폴백 → token 미수신, 단일 청크 응답. PII 누설 방지 trade-off. 출력 가드레일 미설정 시 token streaming UX 유지 | API (UX trade-off) |
GuardrailBlockedError 구조화 | direction / result / guardrail_id / display_text 속성 추가 (기본값 있음, 하위 호환). A2A executor 가 자동으로 GUARDRAIL_BLOCKED 응답으로 변환 | 신규 API |
LLMConfig.skip_llm | RuntimeAdapter passthrough 패턴에서 LLM 호출 우회 → latency·토큰 비용 0. 활성 시 agent= 주입 필수. 상세 | 신규 API (DX) |
| InputContracts priority / fallback chain | DataContract/FileContract.priority + select_input_data_priority() / select_input_file_priority() — 우선순위 내림차순 fallback. 직렬화는 바이트 단위로 동일. 상세 | 신규 API (DX) |
MemoryConfig.history_aware (default True) | 멀티턴(2턴+)에서 system_prompt 끝에 “이전 대화 참고” 안내 자동 prepend. 3중 가드로 1턴/stateless 는 기존 동작 보존. 분류기 등은 false 로 옵트아웃. 상세 | 신규 API |
가드레일 정책 (regex_action / prompt_action) | GuardrailConfig 에 Literal["mask","block"] 추가, 기본 block (보안 우선). PII 마스킹 시에만 regex_action="mask". prompt_action="mask" 는 항상 block 으로 처리(LLM 판정에 위치 정보 부재) | 신규 API |
| DataPart 재귀 마스킹 | mask_runtime_output(output, patterns) — 매칭 패턴을 text + data(중첩 dict/list) 모든 string 에 재귀 적용. DataPart 안 PII 가드레일 우회 결함 해소 | 버그 / 보안 |
| 출력 가드레일 합본 검사 | runtime_output_text_for_guardrail() — 가드레일 입력을 text + JSON(data) + JSON(files-meta) 합본으로 사용. invoke·stream 3경로 일괄 적용 | 버그 수정 |
GuardrailResult.matched_patterns | mask 정책에서 매칭된 regex 패턴을 상위 계층에 전달해 DataPart 마스킹 트리거 | 신규 API (내부) |
| factory 케이스 분기 | RuntimeAdapter + 외부 output_guardrail 동시 사용 시 passthrough PII 누수 차단 (input 은 내부, output 은 외부에서 적용) | 버그 수정 |
| 멀티턴 관측 로그 | LLM 호출 직전 multiturn: thread_msgs=N window=W sent_to_llm=K has_prior=true/false INFO 로그 1줄 emit | 관측성 |
Langfuse guardrail_passed Observation-level 부착 | score_guardrail(span=...) 로 가드레일 span 의 observation_id 를 ScoreBody 에 첨부 → Scores 탭 Observation 컬럼 채워짐. trace-level fallback 유지 | 관측성 |
| A2A 메시지 요청 문서 | A2A 0.3.0 message/send 요청 예시 레퍼런스 신설 (A2A 메시지 요청) | 문서 |
| PostgresBackend PgBouncer session 모드 호환 | AsyncPostgresSaver.from_conn_string() → psycopg_pool.AsyncConnectionPool 직접 주입으로 교체. max_lifetime=1800s 로 PgBouncer 보다 먼저 conn 회전 → 1시간 후 죽은 conn 으로 실패하던 P0 버그 해소. asyncpg 풀도 min_size=0/admin timeout/application_name 명시. 다수 에이전트(1 컨테이너 = 1 에이전트, 동시 활성 50+) 전제. 상세 | 버그 / 인프라 |
| 메모리 관리 API 호출별 timeout 분리 | 관리 API 4개(delete_thread / delete_threads_by_agent / list_threads_by_agent / migrate_agent_id) 가 풀 default(10s) 대신 admin timeout(120s) 사용. 대량 삭제가 10초 timeout 으로 ROLLBACK 되던 회귀 방지. 일반 CRUD 는 fail-fast 위해 10s 유지 | 버그 / 안정성 |
의존성 — psycopg-pool>=3.3.0 first-class | transitive 의존을 pyproject.toml 에 명시 추가. SDK 가 직접 from psycopg_pool import AsyncConnectionPool 사용 → silent breakage 방지 | 의존성 |
Changed (0.2.1)
섹션 제목: “Changed (0.2.1)”| 영역 | 변경 |
|---|---|
Langfuse format_guardrail_comment | comment 포맷을 Violation: {type} - {detail} 계약으로 정규화. type 이 4분류(profanity | inappropriate | religious | political) 외이거나 비어 있으면 inappropriate 로 강제 fallback. 빈 violation_type 으로 Violation: detail 만 나가던 케이스 제거 |
trace.name 우선순위 | chatbotName → workflowName → agentName → default_name 순으로 명문화. 식별자 ID(chatbotId / workflowId / agentId)는 metadata 로 그대로 propagate (필터링·통계 기준) |
Fixed (0.2.1)
섹션 제목: “Fixed (0.2.1)”| 영역 | 버그 |
|---|---|
SchemaValidatedRuntimeAdapter 가드레일 차단 메시지 손실 | 하위 LangGraphAgent 의 가드레일 차단 텍스트를 상위 어댑터가 JSON 파싱 실패로 오인 → on_validation_error fallback 이 차단 메시지를 덮어쓰던 결함. _postprocess_pipeline 진입부에 _GUARDRAIL_BLOCK_MARKERS 기반 short-circuit 추가 |
| 가드레일 mask 토큰 불일치 | LangGraphAgent 출력 가드레일 mask 분기에서 mask_replacement 인자를 누락해 [MASKED] default 가 사용되던 문제. bootstrap.py 의 4 callsite 와 일관되게 GuardrailConfig.mask_replacement 단일 source of truth 사용 |
| Prompt mask DataPart PII 누설 | prompt 가드레일은 mask 정책이어도 항상 block 처리되므로 차단 응답이 data=[guardrailResult] / files=[] 로 폐기 → PII 누설 가능성 0 |
LangGraphAgent.stream() 입력 가드레일 차단 무효화 | try/except Exception 이 GuardrailBlockedError 까지 삼켜 invoke() 폴백을 실행하던 회귀. except GuardrailBlockedError: raise 명시 추가 |
| A2A executor stream 경로 가드레일 중복 실행 | _execute_stream 의 broad except Exception 이 GuardrailBlockedError / ComponentInvokeError / asyncio.TimeoutError / GraphRecursionError 까지 잡아 invoke 경로로 폴백 → 입력 가드레일이 한 trace 안에서 두 번 실행돼 Langfuse 에 input-guardrail × 2, guardrail-violation × 2, guardrail_passed=0.0 누적되던 회귀. 4가지 propagate-only 예외를 명시 raise 하도록 분리, broad catch 는 미지의 예외용 안전망으로만 유지 |
Langfuse guardrail_passed observation-level 부착 무효화 | start_langfuse_child_span 이 반환하는 ChildSpanHandle / DirectSpanHandle wrapper dataclass 에 id 속성이 없어 score_guardrail(span=...) 이 observation_id 를 추출하지 못하고 항상 trace-level fallback 으로만 부착되던 결함 (Langfuse Scores 탭의 Observation 컬럼이 비어 있는 증상). 두 dataclass 에 @property id 추가하여 wrapped Langfuse span (LangfuseSpan / LangfuseGuardrail 등) 의 .id (또는 .span_id) 를 노출 |
| Code-First skill dispatch 가드레일 응답 일관성 | Agent.dispatch() → _run_skill_handler() 가 build_blocked_output() 으로 state:"completed" 응답을 만들던 회귀. agent/skills.py:158,171 두 사이트를 raise GuardrailBlockedError(...) 로 변환 → invoke()/stream() 과 동일한 state:"failed" + GUARDRAIL_BLOCKED 응답 |
빈 {} DataPart 처리 변경 | _extract_data() 가 빈 dict 를 더 이상 DataPart 로 승격하지 않음 (isinstance(raw, dict) and raw). RuntimeOutput.data 에 {} 를 명시적으로 넣어 응답 모양을 제어하던 패턴은 동작하지 않음 — 의도가 “빈 객체” 응답 송출이라면 {"_": ""} 같은 비-empty sentinel 사용 |
Performance (0.2.1)
섹션 제목: “Performance (0.2.1)”| 영역 | 변경 |
|---|---|
| Langfuse 메타데이터·스팬 이름 정규화 캐싱 | langfuse_utils._camelize_metadata_key / _normalize_observation_segment / build_observation_name 3개 결정론적 str→str 헬퍼에 functools.lru_cache 적용 (maxsize 256–512). 트레이스 emit 핫패스에서 redundant work 제거 — 마이크로벤치 5–58×, 동일 워크로드 cProfile 기준 1.94× 개선 (요청당 약 77–225 µs 절감). 동작·출력은 바이트 단위로 동일, API 변경 없음, 메모리 점유량 <50KB |