본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 24. 09:15

Acrobat이 개인정보를 지워준다! SDK와 로컬 LLM으로 만든 자동 마스킹 확장 [전편]

요약

Adobe Acrobat SDK와 로컬 LLM을 결합하여 PDF 내 개인정보를 자동으로 탐지하고 마스킹하는 시스템의 아키텍처를 소개합니다. C++ 플러그인과 JavaScript를 활용한 확장 방식과 Ollama/OpenAI를 지원하는 PII 탐지 서비스 구현 과정을 다룹니다.

핵심 포인트

  • Acrobat SDK를 활용한 C++ 플러그인 및 JavaScript 확장 메커니즘 활용
  • 로컬 LLM(Ollama)과 OpenAI를 모두 지원하는 파사드 패턴 설계
  • 메모리 효율과 에러 처리를 위해 페이지 단위 텍스트 추출 방식 채택
  • PII 탐지 정확도 검증을 위한 Next.js 기반의 별도 평가 툴 구축

갑작스럽지만, Adobe Acrobat에 SDK가 있다는 사실을 알고 계시나요?

Acrobat을 PDF 뷰어(PDF Viewer)나 PDF 작성용으로 사용하는 분들이 대부분이겠지만, 사실 Acrobat은 C++ 플러그인과 JavaScript를 통한 확장 기능 메커니즘을 가지고 있습니다. 자신의 코드를 Acrobat 메뉴에 추가하거나, PDF 조작 API를 직접 호출할 수 있습니다.

이러한 확장성은 Acrobat의 큰 강점 중 하나입니다. 사용자가 직접 최신 기술을 Acrobat에 결합하여 업무에 활용할 수 있습니다. 예를 들어, AI를 이용한 문서 분석이나 자동 처리를 Acrobat 상에서 직접 실행할 수 있다면, PDF 중심의 업무 흐름을 그대로 유지하면서 AI의 혜택을 도입할 수 있습니다.

"그것을 이용하면, 개인정보 자동 마스킹(Redaction)도 만들 수 있지 않을까?"

그런 생각이 들어, 로컬 LLM(Large Language Model)과 Acrobat SDK를 결합한 자동 마스킹 시스템을 만들어 보았습니다. 이 전후 2부 구성의 기사에서 그 구현에 대해 쓰겠습니다.

전편에서는 시스템 전체의 아키텍처(Architecture)와 로컬 LLM을 이용한 개인정보(PII, Personally Identifiable Information) 탐지 서비스의 구현에 대해 설명합니다.

먼저 전체상을 보여드립니다.

PII.api 플러그인에서 직접 Ollama를 호출하는 구성도 고려했지만, 우선 로컬 LLM이 PDF의 개인정보를 얼마나 정확하게 탐지할 수 있는지 확인하고 싶었던 점과, OpenAI로도 전환할 수 있는 파사드(Facade)로서 설계하고 싶었기 때문에 이 구성으로 결정했습니다. 본 기사에서는 PII Detection Service의 구현을 중심으로 설명합니다.

시스템은 3개 계층으로 나뉘어 있습니다:

레이어기술역할
Acrobat DC층C++ 플러그인 + JavaScriptPDF 조작·UI의 입구
...

Acrobat은 C++ 플러그인과 JavaScript를 통한 확장 메커니즘을 가지고 있으며, SDK를 사용함으로써 Acrobat 본체와 동등한 권한으로 PDF를 조작하는 코드를 작성할 수 있습니다. SDK의 상세한 메커니즘이나 확장 방법의 종류, 이번에 C++ 플러그인을 선택한 이유에 대해서는 후편에서 설명하겠습니다.

플러그인 구현 전에, 애초에 로컬 LLM이 PDF 내의 PII를 얼마나 정확하게 탐지할 수 있는지 확인해 둘 필요가 있었습니다. 로컬 LLM은 파라미터(Parameter) 수가 적어 정밀도 면에서 불안한 점이 있었고, 모델의 선택지도 많기 때문에 나중에 교체할 수 있는 설계로 해두고 싶다고 생각했습니다. 그래서 우선 LLM 단독의 정밀도를 확인하기 위한 평가용 Web 툴로서, Next.js를 이용한 PII Detection Service를 만들었습니다. PDF를 드래그 앤 드롭하고, 프로바이더(Provider: Ollama / OpenAI)와 모델을 선택하여 탐지 결과를 확인할 수 있는 간단한 Web 앱입니다. 충분한 정밀도를 확인한 후, 동일한 서비스를 플러그인의 백엔드(Backend)로 재사용하는 순서로 진행했습니다.

PII Detection Service의 UI - PDF 드래그 앤 드롭 화면

PDF는 그대로 LLM에 전달할 수 없으므로, 먼저 텍스트를 추출합니다. pdf-parse 라이브러리를 사용하여 페이지 단위로 텍스트를 추출하고, 1페이지씩 LLM에 보내는 설계로 했습니다.

페이지 단위로 한 이유:

  • 큰 PDF라도 메모리(Memory)를 너무 많이 사용하지 않음
  • 페이지 번호와 연결하여 PII 후보를 반환할 수 있음 (나중에 PDF 상의 위치 특정에 사용)
  • 특정 페이지에서 에러가 발생해도 다른 페이지의 처리를 계속할 수 있음

Ollama 호출 시, Reasoning Model과 관련하여 두 가지 문제에 봉착했습니다.

문제 1: 타임아웃(Timeout) stream: false로 호출하면, qwen3.5와 같은 Reasoning Model은 추론이 완료될 때까지 HTTP 응답 헤더조차 반환하지 않습니다. 결과적으로 클라이언트 측에서 타임아웃이 발생합니다.

문제 2: 컨텍스트(Context) 소비 Reasoning Model은 기본 컨텍스트 윈도우(Context Window)가 60,000 토큰으로 매우 커서, 초기화 시 KV 캐시(KV Cache) 확보로 인해 PC의 메모리를 대량으로 소비합니다. 리소스가 제한된 환경에서는 응답이 전혀 돌아오지 않거나 극도로 느려집니다.

두 문제에 공통된 대책은 think: false입니다. 이번과 같은 PII 추출은 태스크(Task)로서 단순하기 때문에, thinking 프로세스 자체를 오프(Off)함으로써 긴 추론 체인(Reasoning Chain)의 생성을 억제하여 타임아웃과 컨텍스트 소비를 모두 완화할 수 있습니다.

각각의 추가 대책으로서, 타임아웃에는 stream: true

でNDJSON形式のチャンクを逐次読み取ることでヘッダーを即時受信できるようにし、コンテキスト消費には num_ctx
num_predict
をReasoning Modelのみ4096トークンに制限しています:

...(isThinkingModel && { num_ctx: 4096, num_predict: 4096 }),

実装全体はこうなっています:

const response = await fetch(`${OLLAMA_BASE_URL}/api/chat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
...

format
パラメーターにJSONスキーマを渡すことで、LLMの出力を定義したスキーマに沿ったJSONへ強制しています。ページ単位でテキストを送る設計のため、num_ctx
4096で十分機能します。

なお、モデルによってはJSONをMarkdownのコードフェンスで囲んで返すことがあります。パース前に正規化する処理を入れています:

function extractJsonCandidate(value: string): string {
// thinking タグを除去してからコードフェンスを剥がす
const trimmed = stripMarkdownCodeFence(
...

LLMへのシステムプロンプトの設計が検出精度の鍵です。検出対象のカテゴリを明示的に定義しています:

FULL_NAME, FIRST_NAME, LAST_NAME, EMAIL_ADDRESS, PHONE_NUMBER,
PHYSICAL_ADDRESS, DATE_OF_BIRTH, SOCIAL_SECURITY_NUMBER,
NATIONAL_ID, PASSPORT_NUMBER, CREDIT_CARD_NUMBER,
...

さらに、日本語の人名検出ルールも設定しました。日本語の人名は文脈なしには判断が難しく、「山田」が人名なのか地名なのかは周囲のテキストを見ないとわかりません。

プロンプトにはこのような文脈判断のルールを詳細に記述しています:

Name detection rules:
- Detect LAST_NAME even when only the family name appears, if the
surrounding text indicates it refers to a specific person.
...

各検出結果には信頼度スコア(HIGH / MEDIUM / LOW)も付けています。

PIIの検出にパブリックなLLMを使う場合、PDFの内容がクラウドへ送信されることになります。用途や契約よってはそれで問題ありませんが、機密性の高い文書を扱う場面では、情報をどこに送るかをコントロールしたいというニーズがあります。

ローカルLLMであれば、データは一切外部に出ません。精度よりも情報管理を優先したい、あるいはネットワーク的に閉じた環境で動かしたいといったケースで有効な選択肢です。

今回のシステムをOpenAIとローカルLLM(Ollama)の両方に対応した設計としたのも、この考えからです。用途や環境に応じてプロバイダーを選べるようにしておくことで、より幅広いシーンで使えるシステムになります。

前編では以下をカバーしました:

PII Detection Service:Next.jsで作ったLLM評価プラットフォーム -
Ollamaクライアント:ストリーミング必須・Reasoning Model対応・JSONスキーマ強制 -
プロンプト設計:日本語のPII検出の工夫と信頼度スコア -
ローカルLLMに対応した理由:情報管理の観点から、プロバイダーを選べる設計に

後編では、Acrobat SDK C++プラグインの実装に踏み込みます。SDKの仕組みと拡張方法を押さえた上で、PII Detection Serviceを呼び出すプラグインを実装し、Action WizardからPDFに墨消しアノテーションを付与するところまでを説明します.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0