콘텐츠로 이동

v0.2.1

릴리스이전 버전

서버 SDK 교체만으로 끝납니다.env / main.py / app/*.py 코드 변경 없이 wheel + uv.lock + pyproject.toml 1줄 교체. 단, A2A 클라이언트 측은 가드레일 차단 응답의 형태 변경에 분기 코드 수정이 필요합니다 (아래 Breaking 섹션 참조).

영역변경분류
가드레일 차단 응답 v0.3 정합화block 정책 위반 시 응답이 state:"failed" + errorCode:"GUARDRAIL_BLOCKED" ApplicationErrorData 로 전환. 위반 메타는 data[0].guardrail sub-object 에 보존, matchedPatterns 는 id 만 노출(rule name 누설 방지), artifacts[] 폐기. A2A 에러 처리Breaking — 응답 형태
Streaming buffer-then-validateoutput_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_llmRuntimeAdapter passthrough 패턴에서 LLM 호출 우회 → latency·토큰 비용 0. 활성 시 agent= 주입 필수. 상세신규 API (DX)
InputContracts priority / fallback chainDataContract/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)GuardrailConfigLiteral["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_patternsmask 정책에서 매칭된 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-classtransitive 의존을 pyproject.toml 에 명시 추가. SDK 가 직접 from psycopg_pool import AsyncConnectionPool 사용 → silent breakage 방지의존성
영역변경
Langfuse format_guardrail_commentcomment 포맷을 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 (필터링·통계 기준)
영역버그
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 ExceptionGuardrailBlockedError 까지 삼켜 invoke() 폴백을 실행하던 회귀. except GuardrailBlockedError: raise 명시 추가
A2A executor stream 경로 가드레일 중복 실행_execute_stream 의 broad except ExceptionGuardrailBlockedError / 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 사용
영역변경
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