Як отримувати інсайти з PDF файлів за допомогою Apryse та GPT

pic

Отримуйте інсайти з PDF-файлів за допомогою Apryse та GPT

Фахівці у сферах фінансів, юриспруденції та охорони здоров'я добре знайомі з важкою працею над нескінченними банківськими виписками, юридичними контрактами, медичними картками та іншими документами, намагаючись витягти щось корисне з купи неструктурованих даних.

На щастя, ми живемо в епоху, коли великі мовні моделі (Large Language Models, LLMs) стали доступнішими ніж будь-коли, і сьогодні вже досить просто створити додаток на базі штучного інтелекту, який може виявляти патерни, тренди та зв'язки для того, щоб допомогти — а не замінити — людські команди у прийнятті рішень на основі даних.

Але є одне "але"? Це можливо лише за умови, що ви зможете надійно витягнути структуровані дані з неструктурованих PDF-файлів.

Вузьке місце: Просте витягнення тексту не достатньо

Хоча ми, звісно, можемо спробувати використати одну з бібліотек на базі PDF.js для витягнення сирого тексту з PDF-файлів, ми ніколи не зможемо зберегти дані у табличному форматі — а це критично важливо при обробці банківських виписок, фінансових звітів, страхових документів та інших матеріалів.

Основна проблема полягає в тому, що PDF-файли ніколи не були призначені для зберігання даних.
Насправді, PDF-файли були розроблені для стабільного відображення, щоб зберігати вигляд документів — шрифт, текст, растрові/векторні зображення, таблиці, форми і так далі — на різних пристроях та операційних системах, а не прив'язані до структурованої моделі даних, з якою ви звикли працювати, як, наприклад, JSON або XML.

Цей відсутність вбудованої схеми ускладнює процес витягнення даних, оскільки вміст у PDF-файлах не організований логічно, а візуально.

Шаблони на основі правил частково можуть вирішити це питання, але це крихке рішення, яке не працює на великому масштабі. Наприклад, кожен банк має свій формат виписки, і навіть незначні зміни в дизайні можуть порушити шаблонну обробку даних.
Створювати і постійно оновлювати шаблони для кожного банку вручну — непрактично. Адже шаблон за своєю суттю є статичним, що потребує постійного догляду — поганий вибір на довгу перспективу.

І без цього важливого першого кроку, не отримавши правильно налаштованого шаблону, ви отримаєте лише стіну лінійного, неформатованого тексту, який не зберігає контекст, і це ніколи не дасть вам надійних результатів від недетермінованої LLM (Large Language Model), ані на великому масштабі, ані в реальному часі. Я б не ставив бізнес на таке.

Є щось краще?

Довгий час я шукав альтернативу, яка поєднувала б потужну обробку документів з гнучким і масштабованим API (інтерфейс програмування додатків), що не є окремим десктопним застосунком, і яка б без проблем інтегрувалася в нашу розробницьку лінію — без необхідності в обов'язковій кастомізації чи постійному ручному нагляді.
І оскільки безпека має першочергове значення, я хотів, щоб рішення можна було розгорнути на нашій власній інфраструктурі.

Apryse задовольняє всі вимоги.

pic

Apryse — це універсальний набір інструментів для керування документами з комерційною ліцензією, який надає бібліотеки для використання на веб, мобільних пристроях, клієнтських і серверних додатках. Він охоплює перегляд, анотацію, редагування, створення, генерацію PDF-документів та, що найбільш актуально для моїх потреб: витягування даних через свій серверний SDK, надаючи дані у форматах JSON, XML або навіть XLSX.

З Apryse я нарешті можу зосередитися не на рутинних задачах (витягування даних, підтримка шаблонів), а на розробці аналітики, яка приносить цінність у масштабах. Це надійна основа для операцій з великими обсягами даних.

“Інтелектуальна обробка даних”

Ось що відрізняє його від інших: складна нейронна мережа під капотом, що використовує глибокі навчальні моделі для інтелектуального витягування структурованих даних з PDF-документів.
Фактично, бібліотека Apryse використовує ланцюг моделей, які "навчилися" розпізнавати, як виглядатимуть табличні дані у PDF-документі — сітки, стовпці, рядки — як вони позиціонуються одна відносно одної, і як вони відрізняються від абзаців тексту, растрових/векторних зображень тощо.

Ініціалізуєте бібліотеку, подаєте їй PDF як вхід, і вона повертає розібрані та структуровані дані таким чином, щоб вони відображали макет на сторінці — визначаючи таблиці, заголовки, підзаголовки, рядки та стовпці, а також витягуючи абзаци/текстовий контент разом з порядком читання та позиційними даними (координати обмежувальних рамок та базові лінії).

Те, що ви отримаєте на виході — це високо структурований результат, який дозволяє подальшим процесам аналізувати або переформатовувати дані для отримання додаткових інсайтів — ідеально підходить для інгестації (ingestion) LLM (Large Language Model) на наступному етапі нашого процесу аналізу.

Давайте подивимося, як це працює.

Створення нашого конвеєра (Pipeline)

Необхідні умови

Почнімо з того, що переконайтесь, що ви використовуєте Node.js версії 18+ та ініціалізуйте новий проєкт.

Ми будемо встановлювати основну бібліотеку Apryse та її модуль для витягування даних (який містить нейронну мережу, про яку ми говорили).
Також ми додамо dotenv для роботи з нашими змінними середовища.

Ви можете використовувати будь-який менеджер пакетів на ваш вибір для встановлення залежностей. Для цього прикладу ми використаємо NPM, оскільки це той менеджер, який за замовчуванням має більшість користувачів Node.js.

npm install @pdftron/pdfnet-node  
npm install @pdftron/data-extraction  
npm install dotenv

Для моїх потреб з LLM (Large Language Model) я буду використовувати той сервіс, на який маю підписку — OpenAI. Але щоб зробити цей туторіал якнайбільш універсальним і щоб кожен, хто читає, міг слідувати за інструкціями, ми використаємо Vercel AI SDK, який є єдиним інтерфейсом для роботи з різними моделями — OpenAI, Anthropic, Gemini, та іншими, до яких ви маєте доступ, навіть для кастомних моделей.

npm install ai @ai-sdk/openai

І наостанок, API-ключі.

Помістіть їх у файл .env у папці вашого проєкту. Ось як виглядає мій файл.

OPENAI\_API\_KEY = openai\_api\_key\_here  
APRYSE\_API\_KEY = apryse\_trial\_api\_key\_here

Триальна версія підходить для необмеженого використання в не виробничих умовах — якщо вам не заважає водяний знак на всіх згенерованих PDF-документах.

Крок 1: Витягування даних

Точка входу для нашого скрипта насправді дуже проста.
Ви імпортуєте бібліотеку, використовуєте addResourceSearchPath, щоб вказати шлях до додатку для витягування даних (що є, технічно, зовнішнім ресурсом), і чекаєте витягування табличних даних з вхідного PDF-документа (bank-statement.pdf в тій самій директорії) у вигляді рядка JSON.

require("dotenv").config();  
const { PDFNet } = require('@pdftron/pdfnet-node')  

async function main() {  
 await PDFNet.addResourceSearchPath("./node\_modules/@pdftron/data-extraction/lib")  
 try {  
 const json = await PDFNet.DataExtractionModule.extractDataAsString('./bank-statement.pdf',   
PDFNet.DataExtractionModule.DataExtractionEngine.e\_Tabular);  
 console.log('-----Витягнутий текст------');  
 console.log(json);  
 } catch (error) {  
 console.error("Помилка:", error);  
 }  
}

Якщо ваш PDF захищений паролем, просто встановіть пароль у об'єкті DataExtractionOptions, ось так:

/\* якщо захищений паролем \*/  
const options = new PDFNet.DataExtractionModule.DataExtractionOptions();  
options.setPDFPassword("пароль")  

const json = await PDFNet.DataExtractionModule.extractDataAsString('./bank-statement.pdf',   
PDFNet.DataExtractionModule.DataExtractionEngine.e\_Tabular, options);  

// решта коду

І ще, щоб переконатися, що Apryse SDK коректно очищає всі об'єкти в пам'яті після виконання процесу, вам слід виконати main(), ініціалізуючи його через PDFNet.runWithCleanup(), що зробить наш код наприкінці етапу витягування таким:

require("dotenv").config();  
const { PDFNet } = require('@pdftron/pdfnet-node')  

async function main() {  
 await PDFNet.addResourceSearchPath("./node\_modules/@pdftron/data-extraction/lib")  
 try {  
 const json = await PDFNet.DataExtractionModule.extractDataAsString_Tabular);  
 console.log('-----Витягнутий текст------');  
 console.log(json);  
 } catch (error) {  
 console.error("Помилка:", error);  
 }  
}  

PDFNet.runWithCleanup(main, process.env.APRYSE\_API\_KEY)  
 .catch(error => console.error("Не вдалося ініціалізувати бібліотеку Apryse:", error))  
 .then(function () {  
 PDFNet.shutdown();  
 });

Знову ж таки, переконайтесь, що ваш API-ключ Apryse вказаний у файлі .env і переданий як другий аргумент функції runWithCleanup().

Коли ви запустите цей скрипт, він має вивести витягнуті, глибоко структуровані дані JSON, про які ми говорили раніше.

Наступним кроком буде передати ці витягнуті дані JSON до LLM з підготовленим запитом для отримання інсайтів.

Крок 2: Передача структурованих вихідних даних LLM для генерування інсайтів

Почнімо з інтеграції Vercel AI SDK для обробки запитів до вашого постачальника LLM.
Напишемо просту функцію, яка прийматиме JSON-дані з попереднього кроку та передаватиме їх LLM з конкретним запитом, який ми розробимо для отримання практичних інсайтів.

Наш сценарій — аналіз банківської виписки для внутрішнього використання в бізнесі (тобто ми хочемо отримати стратегічні та фінансові інсайти для зацікавлених сторін), тому ось чудовий запит для використання, чи не так?

const prompt = `Проаналізуйте наступний текст і згенеруйте список основних   
фінансових та стратегічних інсайтів для внутрішнього використання в бізнесі.`

Майже готово, але ще не зовсім. LLM інтерпретують запити як текст, без чіткого розмежування між інструкціями користувача та командами, прихованими у підлягаючому тексті. Уявіть собі шкідливий текст у PDF, який непомітно перенаправляє ваш запит так, щоб надмірно підкреслював певні дані, що може спотворити висновки вашої компанії на користь упереджених чи навіть шкідливих рішень. Неприємна думка.

Якби це був традиційний застосунок, ми б просто обробляли введення користувача за допомогою суворої санітарної обробки/перевірки.
Запити до LLM, проте, складніші для захисту через відсутність у них чіткої структури кодування.

Але є деякі засоби захисту, які ми можемо застосувати. Оскільки ми вже маємо структуровані JSON-дані на вхід, готові до використання, ми можемо просто помістити їх в додаткове поле JSON і сказати LLM обробляти тільки те, що знаходиться в цьому конкретному парі ключ-значення.

const prompt = `Проаналізуйте дані транзакцій, на_analyze', і більше нічого. Кожна транзакція структурована з деталями,   
як опис транзакції, сума, дата та інші метадані.   
Згенеруйте список основних фінансових та стратегічних інсайтів   
для внутрішнього використання в бізнесі. Зосередьтесь на визначенні шаблонів грошових потоків,   
категорій високих витрат, повторюваних платежів, великих або незвичайних транзакцій,   
та будь-яких боргових зобов'язань. Надайте інсайти щодо можливих напрямків для заощадження коштів,   
кредитного ризику, операційної ефективності та потенційних фінансових ризиків.`
Кожен інсайт має пропонувати дії або стратегічні міркування для   
покращення стабільності грошових потоків, оптимізації розподілу ресурсів, або виявлення   
потенційних фінансових ризиків.
Розширте технічні терміни за потреби, щоб пояснити   
їх для бізнес-стейкхолдерів.

Невизначеність LLM означає, що ви можете витратити безліч часу, тонко налаштовуючи цей запит під свої потреби, але це має стати хорошою відправною точкою.

Після цього ми можемо імпортувати необхідні бібліотеки і просто передати нашому LLM необхідні дані ось так.

const { openai } = require('@ai-sdk/openai');
const { generateText } = require('ai')

async function analyze(input) {
try {
const { text } = await generateText({
model: openai("gpt-4o"),
/* структуровані вхідні дані для захисту від ін’єкцій запиту */
prompt: ${prompt}\n{"text_to_analyze": ${input}}
});

if (!text) {
throw new Error("Не отримано тексту відповіді від функції generateText.");
}

return text;
} catch (error) {
console.error("Помилка у функції analyze:", error);
return null; // повертає null, якщо сталася помилка
}
}
```

Як ви можете бачити, Vercel AI SDK дуже зручно дозволяє змінювати модель, яку ви хочете використовувати.
Усе інше залишиться тим самим, окрім значення для властивості model.

Підсумовуючи все це та трохи очистивши код, ось що ми маємо.

Для ясності я обрав записувати вихідні дані на обох етапах (як з PDF, так і згенеровані LLM інсайти) у файли на диску. Якщо щось піде не так, сподіваюся, це допоможе вам діагностувати помилки та налаштувати все як треба.

Ваші інсайти будуть виглядати десь так. Дуже зручно!

// excerpt-of-insights.md  
//...  
### Значні або незвичайні транзакції  
1. **Закупівля обладнання**: За витратами у розмірі **$9,000.00** на комп’ютери 21 квітня 2023 року,  
я рекомендую перевірити, чи відповідають технічні характеристики апаратного забезпечення   
поточним операційним вимогам і потенційним майбутнім потребам, щоб уникнути частих оновлень.  
Також пропоную розглянути можливість оптових знижок при закупівлі або пакованих гарантій, щоб   
знизити витрати на довгострокове обслуговування.

Крім того, розгляньте можливість оренди замість покупки,  
якщо ви хочете знизити початкові витрати.

2. **Витрати на бізнес-подорожі**: Ви витратили **$36,500** на поїздки в місяці   
квітні. Це значна сума на подорожі, і я рекомендую двічі перевірити, щоб переконатися,   
що цифри точні, і що є чітке узгодження між цілями подорожей та очікуваними результатами.  
Також пропоную розробити політику подорожей, яка визначатиме дозволені витрати,   
порядок відшкодування та практики економії (наприклад, попереднє бронювання, обмеження на проживання, добові).   
Регулярно переглядайте цю політику та заохочуйте віртуальні зустрічі, коли це можливо.

//...(більше)
```

Зрештою, це виходить за межі цього посібника, але я не можу не згадати ще одну річ: банківська виписка зазвичай є досить маленьким PDF-файлом, тому структуровані дані, які витягує Apryse, ймовірно, не перевищать ліміти використання або контексту вашого LLM.
Безкоштовний тариф OpenAI, наприклад, має ліміт контексту на 8K токенів, що має з легкістю обробити більшість банківських виписок. Але якщо вам потрібно обробляти більші PDF-файли, як:

  • річні фінансові звіти,
  • багатосторінкові юридичні контракти,
  • або комплексні медичні записи,

Тоді JSON-результат може легко перевищити ліміти токенів для багатьох LLM, особливо якщо він містить багато метаданих щодо розташування. Для таких випадків розгляньте можливість використання підходу RAG (Retrieval-Augmented Generation), щоб розбити контент на менші, більш релевантні частини. Таким чином, ви зможете індексувати ці частини та отримувати лише ті, що найбільш релевантні для кожного запиту, знижуючи витрати на токени та залишаючись в межах контекстного вікна вашої моделі.

Висновки

Витягування даних з PDF — це справжня лотерея, часто вимагає поєднання кількох бібліотек і боротьби з непослідовними результатами.
Apryse на базі глибокого навчання була приємно простим рішенням.

Здатність SDK зберігати структурну цілісність при конвертації PDF в JSON — зберігаючи все, від відносин таблиць до просторового розташування — забезпечує ідеальну основу для аналізу з використанням LLM. Більше ніякого ручного парсингу, більше ніяких вправ з регулярними виразами, і більше не потрібно гадати щодо ієрархії документа.

Досвід розробника також був надзвичайним. Кришталево зрозуміла документація, яка відповідає на питання і дозволяє глибоко зануритися в API, щоб з’ясувати потенційні варіанти використання, безліч прикладів коду, що реально відображають випадки з реального життя, і, звісно, вона швидка.

Я використовував її для банківських виписок, але якщо ви створюєте інструменти для фінансового аналізу, конвеєри обробки документів або будь-які програми, які потребують витягування значущих даних з PDF, Apryse заслуговує на серйозну увагу.
Це перетворило зазвичай болісний процес розробки в просту реалізацію, яка дозволяє зосередитись на створенні функціоналу, а не на боротьбі з парсингом документів.

Перекладено з: How to Generate Insights from PDF Files with Apryse and GPT

Leave a Reply

Your email address will not be published. Required fields are marked *