xsoft_comply Python SDK
xsoft comply 공식 Python SDK · PyPI 배포. AI 기본법 대응 AI 거버넌스 플랫폼의 감사기록을 남기는 클라이언트 라이브러리입니다.
설치
0) 가상환경(venv) 먼저 — externally-managed-environment 에러 예방
Ubuntu 23.04+ · Debian 12+ · Fedora 38+ 등 최신 배포판의 시스템 파이썬은 PEP 668 로 잠겨 있어
pip install 이 다음 메시지로 멈춥니다.
error: externally-managed-environment
× This environment is externally managed
시스템 파이썬을 건드리지 말고 가상환경에 설치하세요(권장).
# 1) venv 만들기 (python3-venv 가 없다면: sudo apt install python3-venv)
python3 -m venv ~/venv
# 2) venv 의 pip 으로 설치 — activate 없이 경로로 직접 불러도 됩니다
~/venv/bin/pip install --upgrade pip
# 3) 실행도 같은 venv 로
~/venv/bin/python -c "import xsoft_comply; print(xsoft_comply.__version__)"
셸에 붙여 쓰려면 source ~/venv/bin/activate 후 pip·python 을 그대로 쓰면 됩니다
(빠져나올 때는 deactivate).
venv 를 만들 수 없는 환경(컨테이너 베이스 이미지 등)에서만 마지막 수단으로
pip install --break-system-packages ...를 씁니다. 시스템 패키지와 충돌할 수 있어 권장하지 않습니다.
1) 설치 방법 — PyPI 정식 배포 (표준)
xsoft-comply 는 PyPI 에 정식 배포되어 있습니다. 아래가 표준 설치 경로입니다.
이 배포본 =
1.0.0(LICENSE 동봉).pip install xsoft-comply는 최신 게시본을 설치합니다. 배포 파일의 무결성 확인값(sha256)은 버전별로 릴리스 노트(RELEASES.md)에 기재합니다. README 는 배포 파일 안에 포함되므로 자기 버전의 확인값을 여기에 적지 않습니다.
~/venv/bin/pip install xsoft-comply
소스에서 직접(개발·수정 목적) 설치하려면:
# 저장소 루트에서
~/venv/bin/pip install -e sdk/python
★ 안전하게 설치하기 (권장)
이 SDK 는 고객사 서버 안에서 실행됩니다. 공급망 공격(타이포스쿼팅·계정 탈취)을 막으려면 이름을 정확히 쓰고 해시를 고정하시기 바랍니다.
# ① 정확한 배포 이름 = xsoft-comply (import 이름은 xsoft_comply)
# 비슷한 이름(xsoftcomply · xsoft-compliance 등)은 당사 배포본이 아닙니다.
# ② 해시 고정 = 아래 예시대로 requirements.txt 파일을 직접 만들어 적어두고
# --require-hashes 로 설치합니다 (이 파일은 패키지에 동봉되지 않습니다).
~/venv/bin/pip install --require-hashes -r requirements.txt
아래 내용을 담은 requirements.txt 를 직접 생성합니다(확인값은 릴리스 노트의 값을 그대로 복사):
# <확인값> 자리에는 릴리스 노트(RELEASES.md)에 기재된 해당 버전의 sha256 값을 넣습니다.
xsoft-comply==1.0.0 \
--hash=sha256:<whl 파일의 확인값> \
--hash=sha256:<tar.gz 파일의 확인값>
각 버전의 배포 파일 sha256 은 RELEASES.md 에 적어 둡니다.
이 README 자체가 배포 파일 안에 들어가므로 여기에 자기 확인값을 적으면
적는 순간 그 값이 틀려집니다 — 그래서 확인값 기록은 패키지 밖 파일에 둡니다.
참고로, 이벤트의 occurred_at(발생 시각)과 event_id(이벤트 식별자)는
언제나 SDK 가 스스로 만들며, 이를 바깥에서 덮어쓰는 파라미터는 배포 패키지에 없습니다.
설치 후 확인:
~/venv/bin/python -c "import xsoft_comply; print(xsoft_comply.__version__)"
당사가 지키는 것 — 외부 의존성 0(표준 라이브러리만), 설치 시 실행되는 코드 없음
(setup.py·.pth 없음). import 만으로는 네트워크를 열지 않습니다
(init() 을 호출해야 백그라운드 전송 작업이 시작됩니다).
외부 의존성 없음(표준 라이브러리만). Python 3.9 이상.
빠른 시작
import xsoft_comply as xc
xc.init(
api_key="xsk_live_...", # API 키 (대시보드에서 발급)
endpoint="https://your-domain.com/api/v1",
log_llm_content=False, # 기본값 · 원문 미전송
)
with xc.session(agent_id="support-bot") as s:
s.llm_call(
model="gpt-4o-mini",
provider="openai",
tokens_in=512,
tokens_out=128,
latency_ms=820,
)
s.tool_call(tool="crm.lookup", status="ok", latency_ms=12)
s.data_access(resource="customer_profile", scope="read", record_count=1)
s.decision(decision="escalate", options=3, confidence=0.72)
s.human_review(human_review="approved", reviewer_role="manager")
s.action(action="ticket.create", status="ok")
세션 컨텍스트를 벗어나면 session_end 이벤트가 자동으로 기록 대기열에 추가됩니다.
저절로 남는 것과 직접 적는 것
| 구분 | 저절로 남습니다 | 직접 적으셔야 합니다 |
|---|---|---|
| 세션 | session_start · session_end (세션 컨텍스트가 처리) |
— |
| 이벤트 | — | LLM 호출 · 도구 호출 · 데이터 조회 · 업무 처리 · 판단 · 사람 검토 |
| 프레임워크 연동 | LangChain · OpenAI Agents SDK 의 주요 실행 | 판단 · 사람 검토 · 업무별 metadata |
이벤트 6종
| 이벤트 | 언제 기록됩니까 | 메서드 |
|---|---|---|
session_start |
세션이 시작될 때 | 세션 컨텍스트가 자동 처리 |
llm_call |
LLM 을 호출했을 때 | s.llm_call(...) |
tool_call |
외부 도구·API 를 호출했을 때 | s.tool_call(...) |
data_access |
데이터를 조회했을 때 | s.data_access(...) |
action |
실제 처리·업무를 실행했을 때 | s.action(...) · s.decision(...) · s.human_review(...) |
session_end |
세션이 끝날 때 | 세션 컨텍스트가 자동 처리 |
조항 증적 (제31~36조)
event_type 은 6종입니다 — session_start · llm_call · tool_call · data_access ·
action · session_end.
decision() · human_review() 는 메서드로 그대로 사용합니다. 내부에서
action + 조항 증적(제34조 고영향 책무)으로 조립되어 전송됩니다 → 기존 코드는 그대로 동작합니다.
달라지는 것은 «당사 서버가 이 기록을 어떤 조항으로 분류해 보관하는가» 뿐입니다.
with xc.session(agent_id="loan-bot") as s:
# 한 이벤트에 여러 조항 증적을 담을 수 있습니다(배열)
s.action("loan.approve", status="ok", evidence=[
xc.evidence_block(34, supervisor_id="sup-1", decision_basis="심사 기준표 v3"),
xc.evidence_block(31, ai_generated_label="labeled", label_method="watermark",
output_id="out-99"),
])
# 운영 중 기록이 아닌 «선언» 성격의 값도 같은 경로로 보냅니다.
# 선언 정보도 일반 이벤트 경로로 기록하여 변경 이력을 함께 관리합니다.
s.evidence(32, source="declared", evidence_key="model-2026",
model_id="m-100", model_version="1.2",
risk_management_policy_ref="RM-2026-01")
evidence_key는 같은 선언을 반복 전송해도 하나의 증적으로 관리하기 위한 중복 방지 키입니다. 생략하면 이벤트마다 새 키가 되어 매번 새 증적이 됩니다.- 수집 항목은 조항별 허용 항목명만 받습니다. 오타·정의되지 않은 이름은 경고 후 제외(drop) 됩니다(이벤트 자체는 거부되지 않습니다).
- 값에 원문(prompt/response)이나 판정어("준수/위반")를 넣지 마십시오.
값 규칙·형식 제약 — 타입이 틀리면 해당 필드가 저장되지 않습니다 (★ 중요)
각 수집 항목은 정해진 타입이 있습니다. 타입이 맞지 않는 값은 서버가 해당 항목만 제외합니다(drop). 이벤트는 202(접수)로 응답되고 제외 사유는 응답에 나타나지 않으므로, 접수에 성공해도 감사 시점에 해당 항목이 비어 있을 수 있습니다. 전송 전 dry_run=True·validate() 로 확인하시기 바랍니다.
| 타입 | 허용 값 | 맞지 않으면 | 예시 |
|---|---|---|---|
| 텍스트(text) | 아무 문자열(500자 초과 시 잘림) | 숫자·객체도 문자열로 변환 | supervisor_id="sup-1" |
| 숫자(numeric) | 숫자·숫자 문자열 | 그 밖의 문자 → 제외 | confidence=0.68 |
| 정수(bigint) | 정수·정수 문자열 | 소수는 내림 · 숫자 아님 → 제외 | evidence_document_id=123 |
| 참/거짓(boolean) | true/false·"true"/"false" |
1·"yes" → 제외 |
control_applied=true |
| 날짜(date) | 해석 가능한 날짜(YYYY-MM-DD 저장) |
형식 불명 → 제외 | submission_date="2026-02-10" |
null·빈 문자열 = 제외. 값이 없으면 넣지 마십시오.- 산업별(로봇·음성·자율주행·의료·채용·금융) 계측 예시·전송 흐름은 설치 및 사용 설명서(PDF) 참조.
수집 항목 (조항별)
evidence_block(조항, **항목) 또는 s.evidence(조항, ...) 에 넣는 수집 항목입니다.
조항마다 허용된 항목명이 정해져 있으며, 여기에 없는 이름은 경고 후 저장되지 않습니다(이벤트 자체는 거부하지 않습니다).
본 SDK 는 항목의 필수 여부를 판정하지 않습니다. 어떤 항목을 언제 전송할 것인지는 이용사업자가 정합니다. 전송되지 않은 항목은 비어 있는 상태로 보관되며, 이를 미준수로 해석하지 않습니다.
본 목록은 「인공지능 발전과 신뢰 기반 조성 등에 관한 기본법」 제31조부터 제36조까지 및 같은 법 시행령 제23조부터 제29조까지가 요구하는 사실을 수집하기 위한 항목입니다.
제31조 인공지능 투명성 확보 의무 — 15개
| 수집 항목 | 타입 | 의미 | 근거 |
|---|---|---|---|
| prior_notice_provided | boolean | 사전 고지 이행 여부 | 법 §31① |
| disclosure_method | text | 고지 방법 | 영 §23①1~4 |
| disclosure_version | text | 적용한 고지 문구의 버전 | 영 §23① |
| ai_generated_label | text | 생성 결과물 표시 이행 여부 | 법 §31② |
| label_method | text | 표시 방법 | 영 §23②1~2 |
| machine_readable_label | text | 기계판독 표시 시 안내 제공 여부 | 영 §23②2 후단 |
| synthetic_media | text | 실제와 구분이 어려운 결과물 해당 여부 | 법 §31③ |
| label_visibility | text | 이용자가 명확히 인식할 수 있는 방식 여부 | 법 §31③ |
| accessibility_method | text | 쉽게 확인할 수 있는 방법 | 영 §23③1 |
| audience_consideration | text | 주된 이용자의 조건 고려 | 영 §23③2 |
| artistic_relaxation | boolean | 예술적·창의적 표현물 완화 적용 여부 | 법 §31③ 후단 |
| exemption_type | text | 적용 예외 유형 | 영 §23④1~3 |
| exemption_basis | text | 예외로 본 근거 | 영 §23④ |
| output_id | text | 대상 결과물 식별 | 법 §31②③ |
| media_type | text | 결과물 매체 종류 | 법 §31③ |
제32조 인공지능 안전성 확보 의무 — 24개
| 수집 항목 | 타입 | 의미 | 근거 |
|---|---|---|---|
| training_compute | numeric | 학습 누적 연산량 | 영 §24①1 |
| frontier_technology_status | text | 최첨단 기술 적용 여부 | 영 §24①2 |
| frontier_technology_basis | text | 그 판단의 근거 | 영 §24①2 |
| broad_serious_impact_status | text | 광범위·중대한 영향 우려 여부 | 영 §24①3 |
| broad_serious_impact_basis | text | 그 판단의 근거 | 영 §24①3 |
| model_id | text | 대상 모델 식별 | 법 §32① |
| model_version | text | 모델 버전 | 법 §32① |
| risk_management_policy_ref | text | 위험관리체계 문서 참조 | 법 §32①2 |
| submission_status | text | 이행 결과 제출 여부 | 법 §32② |
| submission_date | date | 제출일 | 법 §32② |
| risk_id | text | 위험 건 식별 | 법 §32①1 |
| risk_signal_source | text | 위험을 식별한 경로 | 법 §32①1 |
| risk_lifecycle_stage | text | 위험이 발생한 수명주기 단계 | 법 §32①1 |
| risk_type | text | 위험 구분 | 법 §32①1 |
| risk_level | text | 위험 평가 결과 | 법 §32①1 |
| risk_action | text | 위험 완화 조치 | 법 §32①1 |
| risk_status | text | 위험 처리 상태 | 법 §32①1 |
| incident_id | text | 안전사고 건 식별 | 법 §32①2 |
| incident_detection_source | text | 사고 감지 경로 | 법 §32①2 |
| incident_type | text | 사고 구분 | 법 §32①2 |
| incident_severity | text | 사고 심각도 | 법 §32①2 |
| incident_action | text | 사고 대응 조치 | 법 §32①2 |
| incident_status | text | 사고 처리 상태 | 법 §32①2 |
| incident_close_reason | text | 사고 종결 사유 | 법 §32①2 |
제34조 고영향 인공지능과 관련한 사업자의 책무 — 38개
| 수집 항목 | 타입 | 의미 | 근거 |
|---|---|---|---|
| risk_mgmt_ref | text | 위험관리방안 문서 참조 | 법 §34①1 |
| control_applied | boolean | 통제 실행 여부 | 법 §34①1 |
| control_type | text | 통제 구분 | 법 §34①1 |
| control_outcome | text | 통제 결과 | 법 §34①1 |
| control_version | text | 적용된 통제 규칙의 버전 | 법 §34①1 |
| explanation_policy_ref | text | 설명 방안 문서 참조 | 법 §34①2 |
| decision_basis | text | 최종결과 도출에 활용된 주요 기준 | 법 §34①2 |
| training_data_reference | text | 학습용데이터 개요 참조 | 법 §34①2 |
| explanation_available | boolean | 설명 제공 가능 여부 | 법 §34①2 |
| explanation_delivery_status | text | 설명 전달 결과 | 법 §34①2 |
| user_protection_policy_ref | text | 이용자 보호 방안 문서 참조 | 법 §34①3 |
| user_protection_action | text | 이용자 보호 조치 | 법 §34①3 |
| protection_reason | text | 보호 조치 사유 | 법 §34①3 |
| protection_outcome | text | 보호 조치 결과 | 법 §34①3 |
| oversight_policy_ref | text | 사람 관리·감독 체계 문서 참조 | 법 §34①4 |
| supervisor_id | text | 관리·감독자 식별 | 법 §34①4 |
| supervisor_name | text | 관리·감독자 성명 | 영 §27①4 |
| supervisor_contact | text | 관리·감독자 연락처 | 영 §27①4 |
| supervisor_role | text | 관리·감독자 직책 | 법 §34①4 |
| intervention_trigger | text | 개입 조건 | 법 §34①4 |
| intervention_authority | text | 개입 권한 및 범위 | 법 §34①4 |
| oversight_required | boolean | 사람 검토 필요 여부 | 법 §34①4 |
| oversight_request_status | text | 사람 검토 요청 상태 | 법 §34①4 |
| oversight_handover_status | text | 검토 대상 인계 상태 | 법 §34①4 |
| reviewer_id | text | 검토자 식별 | 법 §34①4 |
| reviewer_role | text | 검토자 직책 | 법 §34①4 |
| review_status | text | 검토 진행 상태 | 법 §34①4 |
| review_result | text | 검토 결과 | 법 §34①4 |
| human_override | text | 사람에 의한 변경 여부 | 법 §34①4 |
| override_reason | text | 변경 사유 | 법 §34①4 |
| evidence_document_id | bigint | 조치 근거 문서 참조 | 법 §34①5 |
| document_version | text | 보관 문서의 버전 | 법 §34①5 |
| retention_status | text | 문서 5년 보관 상태 | 영 §27② |
| publication_status | text | 주요 내용 게시 여부 | 영 §27① |
| publication_url | text | 게시 위치 | 영 §27① |
| committee_matter | text | 위원회 심의·의결 사항 | 법 §34①6 |
| alt_compliance_basis | text | 다른 법령에 따른 조치로 갈음하는 근거 | 법 §34③ · 영 §27⑤ |
| downstream_exemption | boolean | 중대한 기능 변경이 없어 이행한 것으로 보는 경우 | 영 §27③ |
제33조 · 제35조 · 제36조 — 수집 항목 없음
| 조항 | 사유 |
|---|---|
| 제33조 고영향 인공지능의 확인 | 제공 전 사전 검토 및 정부 확인 요청은 호출 시점마다 발생하지 않습니다. 관리 화면 및 문서 등록 경로에서 관리합니다. |
| 제35조 고영향 인공지능 영향평가 | 영향평가의 내용은 시행령 제28조제1항이 정하는 바에 따라 영향평가서에 포함됩니다. 문서 등록 경로에서 관리합니다. |
| 제36조 국내대리인 지정 | 국내대리인의 지정 및 신고는 회사 단위의 사실입니다. 회사 설정 화면에서 관리합니다. |
★ 위 세 조항의 항목을 SDK 로 전송하면 허용된 항목명에 없으므로 경고 후 저장되지 않습니다.
전송 전 로컬 확인
xc.init(..., dry_run=True) # ① 네트워크 전송 없음. 전송 데이터·조항 연결·수집 항목·가림 결과를 콘솔에 출력
report = xc.validate(evt) # ② 조항별 수집 항목 전송 «사실» 집계 (판단 아님)
print(xc.preview()) # ③ 이벤트 1건의 전송 내용을 로컬에서 확인
python -m xsoft_comply.preview # 설치 확인용 이벤트 1건 출력
validate() 는 「수집 항목 N개 중 M개가 전송되었다」는 사실만 반환합니다.
준수 또는 위반 여부를 판단하지 않습니다(변호사법 제109조).
블록마다 반환하는 값 =
| 키 | 뜻 |
|---|---|
field_total · field_filled |
그 조항의 수집 항목 수 · 그중 값이 전송된 수 |
field_empty |
전송되지 않은 수집 항목 이름 |
unknown_fields |
허용된 항목명에 없어 저장되지 않을 이름(오타 확인용) |
★ field_empty 에 이름이 많다는 것은 미준수를 뜻하지 않습니다. 어떤 항목을 전송할 것인지는
이용사업자가 자신의 시스템에서 정하며, 해당 사실이 발생하지 않은 항목은 비어 있는 것이 정상입니다.
LangChain 연동
from langchain_openai import ChatOpenAI
handler = xc.langchain_handler()
llm = ChatOpenAI(callbacks=[handler])
with xc.session(agent_id="lc-agent") as s:
response = llm.invoke("안녕하세요")
langchain 또는 langchain-core 가 설치돼 있으면 BaseCallbackHandler 를 상속합니다.
없으면 LangChain 의존성 없이 사용할 수 있는 기본 Python 객체로 동작합니다.
OpenAI Agents SDK 연동
import asyncio
import xsoft_comply as xc
from agents import Agent, Runner, function_tool
@function_tool
def lookup_order(order_id: str) -> str:
return f"주문 {order_id} 상태 = 배송중"
agent = Agent(name="order-bot", model="gpt-4o-mini", tools=[lookup_order])
xc.instrument(agent) # 에이전트 연동 설정(AgentHooks)
async def main():
with xc.session(agent_id="order-bot"):
await Runner.run(
agent, "주문번호 A-1024 상태 알려줘.",
hooks=xc.agents_run_hooks(), # 실행 알림 처리기 등록(작업 인계까지 기록)
)
asyncio.run(main())
기록 항목 = 에이전트 시작·종료, 도구 호출(이름·소요시간), LLM 호출(모델·토큰 수), 작업 인계. 원문(입력·출력)은 전송하지 않고 길이만 남깁니다. 두 처리기를 함께 써도 중복 기록을 방지합니다(이미 연동된 에이전트는 실행 알림 처리기가 건너뜁니다).
이벤트 종류별 로그 토글
xc.init(..., log_events={"tool_call": False, "data_access": False})
비활성화한 이벤트 유형에는 순번(seq)을 부여하지 않으므로, 이벤트 순서 검증의 연속성이 유지됩니다.
session_start · session_end 는 끌 수 없습니다(끄면 실행 흐름 기록 자체가 성립하지 않습니다).
정책 기반 실행 차단
rules = [
{"id": "no-mass-delete", "when": {"tool": "crm.delete_all"},
"effect": "block", "reason": "대량 삭제는 사람 승인 후에만"},
{"id": "export-warn", "when_re": {"tool": r"^crm\.export"}, "effect": "warn"},
]
xc.init(..., policy=rules) # policy_enforce=False 면 기록만 하고 차단하지 않습니다
with xc.session(agent_id="support-bot") as s:
outcome = s.run_tool("crm.delete_all", delete_everything)
if outcome.blocked:
... # 도구를 **실행하지 않은** 상태입니다
차단은 예외를 발생시키지 않습니다. outcome.blocked 로 알려주고, 감사기록에는
policy_result="blocked" · metadata.policy_rule 이 남습니다.
s.check("tool_call", tool="…") 로 평가만 할 수도 있습니다.
전자서명 (ed25519 · 의존성 0)
xc.init(..., sign=True) # 키가 없으면 로컬에 생성해 소유자만 읽고 쓸 수 있는 권한(0600)으로 보관을 시도합니다
print(xc.signing_public_key()) # 감사인에게 전달할 공개키(개인키는 반환 경로 없음)
서명 대상 = trace_id·seq·event_id·type·occurred_at·라벨 필드·metadata 해시·직전 서명. 보관된 기록만 보고 검증할 수 있고, 값이 한 글자만 달라져도 검증에 실패합니다. 서명은 백그라운드 전송 작업에서 수행합니다(호출 스레드의 부담을 늘리지 않습니다).
서명 주체는 SDK(고객 프로세스 안) 입니다 — 서버가 대신 서명하는 「서버 서명」 이 아니라 클라이언트 전자서명(ed25519) 입니다. 개인키는 고객 서버 밖으로 나가지 않고, 당사는 공개키로 검증만 합니다.
원문 보관 (객체스토리지 · 옵션)
xc.init(
...,
log_llm_content=True,
r2={"endpoint": "https://<account>.r2.cloudflarestorage.com",
"bucket": "comply-raw", "access_key_id": "...", "secret_access_key": "..."},
)
s.llm_call(model="gpt-4o-mini", content=prompt_and_response)
원문은 고객이 지정한 객체 저장소로 직접 업로드됩니다(당사 수집 API 를 거치지 않습니다).
이벤트에는 위치(content_uri)와 길이·무결성 확인값만 남습니다. 저장소 장애 시에도 이벤트 전송은
별도로 계속 시도하며, 원문은 로컬 임시 보관 파일에도 기록되지 않습니다.
보관기간(retention)
xc.init(..., retention_days=3650) # 기본 1825일(5년) · 추가 보관 옵션 사용 시 그에 맞춰 설정합니다
이벤트 부가정보(metadata)에 _retain_until(만료일)이 남습니다. 만료분 정리는 운영측 도구가
수행합니다.
_retain_until 은 한국 날짜(KST) 기준입니다. 이 값은 절대시각이 아니라 사람이 읽는
날짜이며, 한국 시각으로 같은 날 기록된 이벤트는 만료일도 같아야 하기 때문입니다.
occurred_at·전자서명 시각 같은 절대시각은 UTC 입니다.
수동 flush / 종료
xc.flush(timeout=5.0) # 대기열이 빌 때까지 최대 5초 대기
xc.shutdown() # 백그라운드 전송 작업 정지
프로세스 종료 시 atexit 처리기가 자동으로 최대 2초 동안 전송 완료를 시도합니다.
서버리스에서는 flush_on_return 을 쓰십시오
서버리스는 응답을 반환한 뒤 컨테이너를 멈춥니다. 백그라운드 전송 작업도 함께 멈추므로, 큐에 남아 있던 이벤트가 전송되지 않을 수 있습니다. 오류도 남지 않습니다.
import xsoft_comply as xc
@xc.flush_on_return
def handler(event, context):
with xc.session(agent_id="my-agent") as s:
...
return {"statusCode": 200}
- 핸들러가 값을 돌려주기 직전에 큐를 비웁니다.
- flush 가 실패해도 핸들러의 반환값과 예외는 그대로 통과합니다.
xc.is_serverless()로 현재 환경이 서버리스로 보이는지 확인할 수 있습니다.
유실 계측 — dropped 와 failed 를 함께 확인하십시오
중요한 운영 위험은 이벤트 누락입니다. 그래서 전송되지 않은 이벤트를 두 갈래로 나눠 셉니다. 원인이 다르면 조치도 다르기 때문입니다.
xc.dropped_events() # 대기열 초과로 누락된 건수
xc.failed_events() # 서버 거부로 전송되지 않은 건수
| 함수 | 언제 늘어납니까 | 무엇을 확인해야 합니까 |
|---|---|---|
dropped_events() |
SDK 내부 대기열이 가득 차 제외했을 때 | max_queue 를 키우거나 flush_interval 을 줄입니다 |
failed_events() |
서버가 400·401·403·413 등으로 거부했을 때 |
API 키 · 요금제 · 전송 본문 크기를 확인합니다 |
- 두 값과 로컬 임시 보관(스풀) 상태를 함께 확인하십시오. 스풀에 보관 중인 건(
403 PLAN_REQUIRED·429·5xx)은 재전송 대상 이라 두 카운터에 잡히지 않습니다. - 세는 단위는 이벤트 건수 입니다(전송 묶음 수가 아닙니다). 한 번의 전송 요청에 여러 건이 실립니다.
if xc.dropped_events() or xc.failed_events():
log.error("감사기록 누락 가능 — 대기열 초과 %d건 · 서버 거부 %d건",
xc.dropped_events(), xc.failed_events())
증상별 확인 순서
| 증상 | 먼저 확인할 것 |
|---|---|
| 패키지 import 실패 | 가상환경 경로 · 설치한 이름(xsoft-comply) · Python 3.9 이상 |
| 운영 화면에 기록이 없음 | API 키 · endpoint · 세션 종료 또는 flush() 호출 · 요금제 권한 |
dropped_events() 증가 |
큐 용량(max_queue)과 flush 주기 · 일시적인 대량 전송 여부 |
failed_events() 증가 |
API 키 · 요금제 · 배치 본문 크기 · 서버 응답 코드 |
| 서버리스에서 마지막 기록이 없음 | 반환 직전 flush() 또는 @xc.flush_on_return 사용 여부 |
애플리케이션 영향 최소화 설계
xsoft comply SDK 는 호출 스레드에서 네트워크 전송을 하지 않도록 설계했습니다. 다음 8가지 동작 원칙을 따릅니다.
1. 모든 공개 함수는 예외를 호출자에게 전달하지 않습니다
init() · session() · s.llm_call() 등 모든 공개 함수는 최상위 try/except BaseException 으로 감쌉니다.
KeyboardInterrupt · SystemExit 만 다시 발생시킵니다(프로세스 종료 신호를 차단하지 않기 위해).
# SDK 내부 구조 (단순화)
def llm_call(self, ...):
try:
evt = build_event(...)
buffer.put(evt) # 대기 없이 즉시 반환
except (KeyboardInterrupt, SystemExit):
raise
except BaseException:
pass # 오류를 호출자에게 전달하지 않습니다
2. 호출 스레드는 네트워크 전송을 하지 않습니다
이벤트 기록은 queue.put_nowait() 만 실행합니다.
대기열이 가득 차면 가장 오래된 항목을 먼저 제외하고 새 항목을 넣습니다. 대기 없음.
고객 스레드 → put_nowait(event) → [대기열] → 백그라운드 전송 작업 → 전송 요청
3. 전송은 백그라운드 스레드(daemon thread) 1개가 담당합니다
threading.Thread(daemon=True) 로 생성됩니다.
이 스레드는 Python 인터프리터가 종료될 때 함께 종료되므로 고객 프로세스 종료를 지연시키지 않습니다.
4. 전송 대기 제한 시간: 연결 2초 / 응답 3초
urllib.request 에 합산 5초 제한 시간을 설정합니다.
requests · httpx 등 외부 라이브러리 의존성이 없습니다.
5. 전송 실패 시 로컬 임시 보관 → 재시도 간격 조정
연결 거부 · 서버 오류 · 시간 초과 등 네트워크 장애 시:
- 이벤트를
~/.xsoft_comply/spool/events.jsonl에 줄 단위로 추가 기록합니다 - 재시도 간격을 점차 늘려 대기합니다 (1초 → 2초 → 4초 → 8초 → 최대 30초)
- 대기 후 재전송을 시도합니다
6. 임시 보관 파일 상한 (기본 32 MB)
임시 보관 폴더의 총 크기가 32 MB 를 초과하면 오래된 파일부터 삭제합니다. 디스크 쓰기 실패도 예외를 호출자에게 전달하지 않습니다.
7. init() 전에 이벤트를 기록해도 중단되지 않습니다
대기열이 준비되지 않은 상태에서 이벤트 기록은 아무 작업 없이 즉시 반환됩니다.
8. 프로세스 종료 시 자동 전송 시도 (최대 2초)
import atexit
atexit.register(_atexit_handler) # init() 최초 호출 시 1회 등록
def _atexit_handler():
flush(timeout=2.0) # 2초 후 반드시 반환
프로세스가 정상 종료될 때 대기열에 남은 이벤트의 전송을 최대 2초 동안 시도합니다. 2초 안에 완료되지 않아도 프로세스 종료를 지연시키지 않습니다.
원문을 당사 수집 API 로 전송하지 않는 기본 설계
content= 인자로 원문(prompt/response)을 넘겨도 원문 자체는 당사 수집 API 로 전송되지 않습니다.
대신 content_len (길이) 과 content_sha256_8 (sha256 앞 8자) 만 부가정보에 포함됩니다.
원문을 보관해야 한다면 위 「원문 보관 (객체스토리지 · 옵션)」 을 사용하시면 되고, 그때도 원문은 고객이 지정한 객체 저장소로 직접 업로드됩니다 — 당사를 경유하지 않습니다.
다음 부가정보(metadata) 키가 있으면 SDK 에서 자동 제거하고, 제거된 키의 개수를 기록합니다:
prompt, response, messages, content, input, output 등 21개.
다만 부가정보의 다른 키에 담긴 값까지 원문인지 판별하지는 못하므로,
부가정보와 조항 증적에는 원문·민감정보를 입력하지 마십시오.
개인정보 처리 원칙
1. 적용 범위
본 SDK 가 당사 수집 API 로 전송하는 값은 ① 부가정보(metadata) 와 ② 조항 증적(evidence) 두 가지이며, 개인정보 처리 방식이 서로 다릅니다.
| 구분 | 무엇인가 | 개인정보 처리 |
|---|---|---|
| 부가정보(metadata) | 이용자가 이벤트에 자유롭게 덧붙이는 값. 항목을 당사가 정하지 않습니다 | ★ 자동으로 가립니다(제2항) |
| 조항 증적(evidence) | 법 제31조부터 제36조까지가 요구하는 값. 항목명과 형식을 당사가 정합니다 | ★ 가리지 않습니다(제3항) |
2. 부가정보(metadata) — 자동 가림 대상
부가정보는 이용자가 무엇이든 담을 수 있는 자리이므로, 개인정보가 의도치 않게 포함될 수 있습니다. 이에 본 SDK 는 전송 전 이용자 서버에서 다음 형식의 값을 자동으로 가립니다.
| 대상 형식 | 처리 결과 |
|---|---|
| 주민등록번호 | ******-******* |
| 휴대전화번호 | 010-****-**** |
| 전자우편주소 | u***@example.com |
| 카드번호(16자리) | 1234-****-****-5678 |
가림이 한 건이라도 발생한 경우 metadata._pii_masked 값이 true 로 기록됩니다.
본 항의 가림은 이용자 보호를 위한 예비적 조치이며, 이용자가 부가정보에 개인정보를 담지 않을 책임을 대신하지 않습니다.
3. 조항 증적(evidence) — ★ 가리지 않습니다
본 SDK 는 조항 증적에 포함된 값을 가리지 않으며, 이용자는 해당 값을 가리지 않은 원문 그대로 전송하여야 합니다.
이는 관계 법령이 해당 항목의 게시를 요구하기 때문입니다.
인공지능 기본법 시행령 제27조제1항제4호 — 인공지능사업자는 해당 고영향 인공지능을 관리·감독하는 사람의 성명 및 연락처를 사무소·사업장 또는 인터넷 홈페이지 등에 게시해야 한다.
가려진 값(예: 홍**)은 위 조항이 요구하는 성명에 해당하지 않습니다. 이용자가 임의로
가려 전송한 경우 해당 값은 감사 대응 자료로서의 효력을 상실할 수 있으며, 이로 인하여
발생하는 불이익에 대하여 당사는 책임을 지지 않습니다.
4. 당사 서비스 화면에서의 표시
당사는 제3항에 따라 수집한 값을 원문으로 보관하되, 이용자 화면에는 다음 항목 일부를 가린 형태로만 표시합니다.
| 항목 | 화면 표시 |
|---|---|
supervisor_id · supervisor_name · supervisor_contact · reviewer_id |
가운데를 가려 표시합니다 (예: 홍**) |
supervisor_role · reviewer_role |
직책·역할에 해당하므로 가리지 않습니다 |
가림은 당사 서버에서 수행되며, 가리기 전의 값은 이용자 화면(브라우저)으로 전달되지 않습니다. 감사 대응 자료로 제공되는 증적 묶음에는 원문 그대로 포함됩니다.
5. 항목의 필수 여부
본 SDK 는 각 항목의 필수 여부를 판정하지 않습니다. 전송되지 않은 항목은 감사 대응 자료에 **「미기록」**으로 사실대로 표기됩니다.
라이선스
MIT
Metadata
Release files for xsoft-comply 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xsoft_comply-1.0.0.tar.gz | 95.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xsoft_comply-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 180.6 kB
Release files / xsoft_comply-1.0.0.tar.gz
| Download URL | xsoft_comply-1.0.0.tar.gz |
|---|---|
| Size | 95.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1703d3be09a28fd089dfd854db1953bcbd0c152f4755a853300000d515be9520
|
|
BLAKE2b-256 checksum How to use checksums |
a75c745ef900575ffad13ce91c4b6bb62743072d28a7ce83ea058abba739584e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / xsoft_comply-1.0.0-py3-none-any.whl
| Download URL | xsoft_comply-1.0.0-py3-none-any.whl |
|---|---|
| Size | 84.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8b13858478800fa5eabc027d7ce10206ffdf2ab2bfb68058806752a21d064041
|
|
BLAKE2b-256 checksum How to use checksums |
c96a9cccb5f0ade12b6619b9debc9cc4d9753c5f2f0820224ed469603d2187c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|