Skip to main content

Bounded agentic Korean ASR query rewriting with Microsoft Foundry and Ollama

Project description

Pronunciation Mapper V2

PyPI Python CI Downloads License

English · 사용 매뉴얼 · PyPI

한국어 ASR 결과를 DB의 고유명사·기술 용어로 안전하게 정규화하는 Query Rewriting 라이브러리입니다.

V2는 기존 자모/편집거리 휴리스틱을 버리지 않습니다. 로컬에서 exact mapping과 발음 top‑K 후보를 만든 뒤, Microsoft Foundry를 기본 decision agent로 사용해 문맥상 올바른 후보 ID만 선택합니다. Ollama는 로컬 옵션이며 OpenAI·Claude provider는 reference-only입니다.

실행 방식 Provider 인증 용도
Deterministic core 로컬 휴리스틱 없음 exact alias, 숫자 정규화, V1 호환 API/CLI
기본 decision agent Microsoft Foundry Microsoft Entra ID 문맥이 필요한 bounded candidate 선택
선택형 local agent Ollama 없음 명시적으로 선택하는 로컬 모델 실행
Reference only OpenAI·Claude 해당 없음 V2 adapter를 제공하지 않는 확장 계약 참고

Python 3.10–3.13을 CI에서 검증하며 기존 PronunciationMapper API와 CLI를 유지합니다.

다운로드 배지는 PSF의 공개 PyPI 통계를 사용하는 pypistats의 최근 월간 집계이며, 알려진 mirror를 제외하고 하루 한 번 갱신됩니다. 신규 package는 첫 집계 전까지 색인 대기 상태로 보일 수 있습니다.

왜 하이브리드인가

LLM에게 문장 전체를 자유롭게 다시 쓰게 하면 DB vocabulary 밖의 단어 생성, 대소문자·ID 훼손, 비용 증가, 재현성 저하가 생깁니다. V2는 모델의 권한을 의도적으로 줄입니다.

ASR text → 로컬 숫자·alias 정규화 → 자모/편집거리 top‑K → Foundry 또는 Ollama의 candidate ID 선택 → 로컬 계약 검증 → RewriteResult

Exact alias는 provider 호출 없이 적용합니다. 모델이 keep/abstain을 선택하면 원문을 보존하고, provider가 실패하거나 계약 밖 응답을 내면 명시한 deterministic fallback을 적용합니다.

핵심 불변식은 다음과 같습니다.

  • 모델은 로컬에서 생성된 candidate ID만 선택할 수 있습니다.
  • 최종 canonical term은 항상 db_terms 안에 있어야 합니다.
  • 허용된 span 밖의 공백·구두점·텍스트는 보존합니다.
  • keepabstain을 정상 결과로 취급합니다.
  • provider 장애나 잘못된 JSON은 설정된 fallback으로 처리합니다.
  • provider 간 자동 전환은 하지 않습니다. Azure에서 Ollama로 데이터를 보내는 정책은 사용자가 명시해야 합니다.

설치

기존 로컬 매퍼만 사용할 때:

python -m pip install pronunciation-mapper

Microsoft Foundry 기본 구성:

python -m pip install 'pronunciation-mapper[foundry]'
az login

export FOUNDRY_PROJECT_ENDPOINT='https://<account>.services.ai.azure.com/api/projects/<project>'
export FOUNDRY_MODEL='<deployment-name>'

FOUNDRY_MODEL은 catalog model ID가 아니라 Foundry의 배포 이름이며 Responses API structured output을 지원하는 배포여야 합니다. 로컬에서는 DefaultAzureCredentialaz login 세션을 사용하고, Azure 운영 환경에서는 Managed Identity credential 주입을 권장합니다.

foundry extra에 openai 패키지가 포함되지만 이는 AIProjectClient.get_openai_client()가 사용하는 Azure Responses 전송 클라이언트입니다. OPENAI_API_KEY나 OpenAI 계정은 필요하지 않습니다.

Ollama 옵션:

python -m pip install 'pronunciation-mapper[ollama]'
ollama pull qwen3.5:4b

export OLLAMA_HOST='http://localhost:11434'
export OLLAMA_MODEL='qwen3.5:4b'

Ollama 모델은 자동으로 pull하지 않습니다. structured output 품질과 한국어 고유명사 정확도는 모델마다 다르므로 이 저장소의 eval set으로 확인해야 합니다.

저장소를 수정하거나 전체 테스트를 실행하는 개발 환경은 source checkout에서 설치합니다.

python -m pip install -e '.[dev,foundry,ollama]'

빠른 시작

네트워크 없이 — V1 호환 API

from pronunciation_mapper import PronunciationMapper

mapper = PronunciationMapper(
    ["customer", "server", "데이터베이스"],
    custom_mappings={"서버": "server"},
)

query = "커스터머,  서버에서 조회"
result = mapper.map_sentence(query)

print(f"입력: {query}")
print(f"출력: {result}")

실행 결과:

입력: 커스터머,  서버에서 조회
출력: customer,  server에서 조회

Microsoft Foundry — 기본

import asyncio

from pronunciation_mapper import AgenticPronunciationMapper


async def main():
    async with AgenticPronunciationMapper(
        db_terms=["XPN36", "account_no", "transaction", "server", "log"],
        custom_mappings={
            "엑스피엔36": "XPN36",
            "어카운트넘버": "account_no",
            "서버": "server",
            "로그": "log",
        },
        # provider를 생략하면 azure가 기본입니다.
    ) as mapper:
        query = "엑스피엔36 서버에서 어카운트넘버 사삼삼오삼칠의 트랜잭숑 로그"
        result = await mapper.rewrite(query)

        print(f"입력: {query}")
        print(f"출력: {result.rewritten_text}")
        print(f"provider: {result.provider}")
        print(f"fallback: {result.fallback_used}")


asyncio.run(main())

실행 결과:

입력: 엑스피엔36 서버에서 어카운트넘버 사삼삼오삼칠의 트랜잭숑 로그
출력: XPN36 server에서 account_no 433537의 transaction log
provider: azure-foundry
fallback: False

이 예제에서 exact mapping과 숫자 변환은 로컬에서 처리되고, 발음 후보인 트랜잭숑 → transaction은 Foundry가 후보 ID를 선택합니다. 모델이 keep 또는 abstain을 선택하면 해당 입력은 그대로 보존될 수 있습니다.

Ollama — 명시적 선택

with AgenticPronunciationMapper(
    db_terms=["customer", "transaction", "server", "log"],
    custom_mappings={
        "서버": "server",
        "로그": "log",
    },
    provider="ollama",
    provider_options={"model": "qwen3.5:4b"},
) as mapper:
    query = "트랜잭숑 서버 로그"
    result = mapper.rewrite_sync(query)

    print(f"입력: {query}")
    print(f"출력: {result.rewritten_text}")
    print(f"provider: {result.provider}")

실행 결과 예시:

입력: 트랜잭숑 서버 로그
출력: transaction server log
provider: ollama

모델의 confidence, 판단 근거, latency와 token usage는 실행마다 달라질 수 있습니다. 상세 정보가 필요하면 result.to_dict()를 출력하세요.

이미 event loop가 실행 중이면 rewrite_sync() 대신 await rewrite()를 사용해야 합니다. factory가 만든 provider는 mapper context manager가 종료합니다. 직접 주입한 custom provider/client의 수명은 호출자가 관리합니다.

결과 계약

rewrite()는 문자열뿐 아니라 판단 근거와 운영 metadata를 포함한 RewriteResult를 반환합니다.

result.rewritten_text   # 최종 검색 질의
result.provider         # local-deterministic / azure-foundry / ollama
result.model            # 실제 deployment/model 이름
result.fallback_used    # provider 실패 fallback 여부
result.decisions        # span별 candidate, action, confidence, reason, distance
result.latency_ms
result.usage             # provider가 제공한 token/시간 정보
result.diagnostics

confidence는 모델의 bounded decision confidence입니다. V1 tuple의 두 번째 값은 반대로 0이 가장 좋은 편집 거리이므로 서로 같은 값으로 취급하면 안 됩니다.

실패 정책

AgenticPronunciationMapper(..., fallback_strategy="heuristic")  # 기본: V1 후보 적용
AgenticPronunciationMapper(..., fallback_strategy="original")   # 원문/숫자 정규화만 유지
AgenticPronunciationMapper(..., fallback_strategy="raise")      # provider 오류 전파

모델이 정상적으로 keep 또는 abstain을 선택한 경우는 provider 실패가 아니며 heuristic fallback을 적용하지 않습니다. confidence가 minimum_confidence보다 낮아도 원문을 보존합니다. V2의 기본 heuristic fallback 거리는 0.35로 V1 기본값보다 보수적이며, threshold=를 명시하면 그 값을 사용합니다.

안전 한계와 숫자 처리

기본값은 입력 4,096자, lexical span 64개, token 256자까지입니다. max_input_chars, max_spans, max_token_chars로 조절할 수 있으며 초과 입력은 임의로 잘라 보내지 않고 거부합니다. Foundry transport는 timeout 30초, retry 1회, output 2,048 token으로 제한합니다. Ollama도 timeout 30초와 output 2,048 token 상한을 적용하고 thinking mode를 끕니다.

한글 숫자는 명백한 단위·counter(일억 원, 321번) 또는 긴 번호 문맥만 결정적으로 바꿉니다. 일일이, 사이사이, 천만 다행처럼 숫자와 모양이 같은 일반어는 보존합니다. 프로젝트별 짧은 ID나 숫자형 고유명사는 golden set과 명시적 mapping으로 관리하는 것이 안전합니다.

CLI

V1 명령은 그대로 유지됩니다.

pronunciation-mapper map-word 커스터머
pronunciation-mapper map-sentence '그라운드에 있는 데이타베이스 확인'
pronunciation-mapper add-mapping 고객 customer --save

V2:

pronunciation-mapper rewrite \
  '엑스피엔36 서버에서 트랜잭숑 로그' \
  --db-terms examples/db_terms.json \
  --provider azure \
  --json

pronunciation-mapper rewrite '트랜잭숑 로그' \
  --provider ollama \
  --model qwen3.5:4b

DB 용어 파일은 문자열 배열 또는 {"terms": [...]} 형식을 지원합니다.

테스트와 평가

python -m unittest discover -v
python -m pytest -q

# 외부 provider 없이 deterministic fallback baseline
python evals/run_v2.py --provider offline

# 실제 환경에서 opt-in
python evals/run_v2.py --provider azure
python evals/run_v2.py --provider ollama

배포 전에는 최소한 exact accuracy, false rewrite rate, abstention 품질, latency, provider 실패율을 비교하세요. 자동 LLM judge보다 이 도메인의 golden mapping exact match를 1차 release gate로 두는 편이 적합합니다.

문서

라이선스

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pronunciation_mapper-2.0.1.tar.gz (92.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pronunciation_mapper-2.0.1-py3-none-any.whl (39.4 kB view details)

Uploaded Python 3

File details

Details for the file pronunciation_mapper-2.0.1.tar.gz.

File metadata

  • Download URL: pronunciation_mapper-2.0.1.tar.gz
  • Upload date:
  • Size: 92.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pronunciation_mapper-2.0.1.tar.gz
Algorithm Hash digest
SHA256 df5070d76050a4b6f9319f0092057775551499fe984b4052d13aa64f5aaf661b
MD5 e8ad86e094e5b879a25d3b7981c98d48
BLAKE2b-256 97217996d8377d140d2d3671400464c877a0682fb9f42d96835591fce568b454

See more details on using hashes here.

Provenance

The following attestation bundles were made for pronunciation_mapper-2.0.1.tar.gz:

Publisher: pypi-publish.yml on hyeonsangjeon/pronunciation-mapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pronunciation_mapper-2.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pronunciation_mapper-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 180118c126ddff77d2430bc0fb7993a887d8d7c3cb092349c035b2e4eccd6d7c
MD5 31d19288b6b2418c71b90dff716eaf1d
BLAKE2b-256 a4c6f0db5b69782149cb931c840e3f3a7077e16d1e0d441de134dd8233bd15c2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pronunciation_mapper-2.0.1-py3-none-any.whl:

Publisher: pypi-publish.yml on hyeonsangjeon/pronunciation-mapper

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page