Создание испанского налогового поиска на основе GPT: революция в доступе к налоговой информации
Прошли те времена, когда нужно было ориентироваться в сложном лабиринте налоговой информации на государственных веб-сайтах. В марте 2023 года Грег Брокман (соучредитель и президент OpenAI) представил пример использования GPT-4 для уплаты налогов, и я решил создать специальную версию для Renta — годовой декларации по подоходному налогу для резидентов Испании. Имя? РентаГПТ 😅
RentaGPT — это простое решение для упрощения налогового поиска Renta для граждан Испании, основанное на информации на веб-сайте AEAT, сочетающее в себе мощь технологии OpenAI GPT-3 с веб-скрапингом и передовыми лингвистическими инструментами, такими как Chroma и LangChain. ».
В этом сообщении в блоге я расскажу вам о создании этого приложения и о том, как этот инструмент может революционизировать способ доступа пользователей к (налоговой) информации.
Шаг 1. Сбор данных с веб-сайта AEAT.
Первым шагом в создании приложения для налогового поиска был сбор соответствующей информации с веб-сайта AEAT (Agencia Estatal de Administración Tributaria). Используя методы парсинга, я систематически извлекал данные из определенного раздела о Ренте 2022.
def crawl_aeat() -> List[str]: starting_url = "https://sede.agenciatributaria.gob.es/Sede/Ayuda/22Manual/100.html" url_prefix = "https://sede.agenciatributaria.gob.es/Sede/ayuda/manuales-videos-folletos/manuales-practicos/irpf-2022" links = get_all_links(starting_url, url_prefix) leafs_strings = delete_substrings([link.replace(".html", "") for link in links]) no_duplicated_links = [link + ".html" for link in leafs_strings] links_to_skip = ( "presentacion.html", "normativa.html", ) final_list = list( filter( lambda x: not x.endswith(links_to_skip), no_duplicated_links ) ) return final_list
Результатом этого процесса стал всеобъемлющий набор данных, содержащий всю необходимую налоговую информацию, такую как законы, налоговые ставки, сроки, формы и часто задаваемые вопросы.
def load_links(links: List[str]) -> List[Document]: """Get documents from web pages.""" pickle_docs_file = f"{cfg.data_directory}/documents.pkl" if os.path.exists(pickle_docs_file): print("Pickle version of docs found. Loading it") with open(pickle_docs_file, "rb") as fp: documents = pickle.load(fp) else: print("No pickle version of docs found. Downloading links") loader = UnstructuredURLLoader(urls=links) downloaded_docs = loader.load() print("Downloaded docs", len(downloaded_docs)) documents = [fix_document(doc) for doc in downloaded_docs] with open(pickle_docs_file, "wb") as fp: pickle.dump(documents, fp) return documents
Шаг 2: Создание базы данных векторного поиска встраивания с помощью Chroma
Чтобы обеспечить эффективный поиск в приложении, я использовал Chroma, мощный движок базы данных на основе DuckDB, который создает базы данных векторного поиска встраивания.
Преобразовывая извлеченную налоговую информацию в многомерное векторное пространство, Chroma позволяет пользователям быстро и эффективно выполнять поиск в наборе данных.
Такой подход не только гарантирует быстрое получение релевантных результатов, но и помогает уточнить поисковые запросы, чтобы предоставить пользователям самую точную и актуальную налоговую информацию.
def create_embeddings(documents: List[Document]) -> List[Document]: split_documents = [] embeddings_file = f"{cfg.chroma.persist_directory}/chroma-embeddings.parquet" embeddings = OpenAIEmbeddings( openai_api_key=cfg.providers.openai.api_key, model="text-embedding-ada-002", ) if not os.path.exists(embeddings_file): print("No embeddings file found. Calculating embeddings.") text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=50, length_function=len, ) split_documents = text_splitter.split_documents(documents) client = Chroma.from_documents( documents=split_documents, embedding=embeddings, collection_name=cfg.chroma.collection_name, persist_directory=cfg.chroma.persist_directory, ) client.persist() return split_documents
Шаг 3: Склеивание шагов LLM с помощью LangChain
Как только набор данных был преобразован в базу данных векторного поиска встраивания, мне понадобился способ подключить его к OpenAI GPT-3 API, механизму обработки естественного языка.
LangChain послужил идеальным инструментом для этой цели, поскольку он специализируется на связывании шагов языковой модели и оптимизации потока информации между различными компонентами.
Интегрировав LangChain, я обеспечил бесперебойную связь между GPT-3 и поисковой базой данных Chroma, что позволило пользователям взаимодействовать с приложением, используя запросы на естественном языке.
def get_filter_documents_chain(api_key: str) -> LLMChain: llm = OpenAI( openai_api_key=api_key, streaming=False, verbose=True, temperature=0.0, max_tokens=1000, ) prompt = PromptTemplate( template=FILTER_DOCUMENTS_PROMPT_TEMPLATE, input_variables=["question", "documents"] ) return LLMChain(llm=llm, prompt=prompt)
Шаг 4. Внедрение понимания естественного языка (NLU)
Это одна из «волшебных» частей, в которой приложение использует мощные возможности понимания и генерации естественного языка OpenAI GPT.
Он состоит из нескольких шагов, необходимых для обработки и анализа налоговых запросов. Каждый шаг служит определенной цели, и все они работают вместе, чтобы обеспечить точные и надежные результаты.
- Во-первых, вопрос пользователя преобразуется во встраивание.
- Во-вторых, база данных Chroma запрашивается с использованием этих вложений, возвращая до пяти релевантных результатов. Затем эти результаты объединяются в один текст.
- В-третьих, GPT используется для возврата файла JSON с наиболее релевантными документами, относящимися к данному вопросу.
- В-четвертых, предыдущий список результатов добавляется к исходному вопросу и используется, чтобы попросить GPT сгенерировать хороший ответ пользователю, который ссылается на документы и всесторонне и точно охватывает заданный вопрос. (Здесь необходимы некоторые преобразования, чтобы убедиться, что возвращенный JSON полезен).
query_results = docsearch.max_marginal_relevance_search( query=question, k=4, ) filter_documents_chain = get_filter_documents_chain(api_key) documents = build_yaml_documents(query_results) result = await filter_documents_chain.acall({ "question": question, "documents": documents, }) try: result_text = result['text'].replace("```json", "").replace("```", "") result_results = json.loads(result_text)["results"] except Exception as e: logging.error(e) result_results = [] sources = [source['source'] for source in result_results] filtered_query_results = list(filter(lambda x: x.metadata['source'] in sources, query_results)) filtered_documents = build_yaml_documents(filtered_query_results) streaming_chain = get_streaming_chain(stream_handler, api_key) result = await streaming_chain.acall({ "question": question, "documents": filtered_documents, })
Шаг 5. Создание API и внешнего интерфейса с помощью FastAPI и Vite
Чтобы создать удобный интерфейс для приложения, я выбрал FastAPI и Vite в качестве основных технологий для создания API и внешнего интерфейса соответственно.
FastAPI — это современная и высокопроизводительная среда Python для создания API, которая позволила мне разработать надежное и надежное соединение между серверной системой и пользовательским интерфейсом. На фронтенд меня вдохновил проект Clarity-AI (🎉 респект Маккею! 🎉)
Учитывая, что целью была простота, я решил настроить Vite с некоторыми плагинами, чтобы создать один файл index.html
, содержащий весь контент javascript и CSS, который обслуживается FastAPI. Процесс сборки выполняется во время действий GitHub, и только один HTML-файл развертывается на fly.io.
Шаг 6. Развертывание приложения с помощью GitHub Actions и Fly.io
Чтобы упростить процесс развертывания и сделать приложение доступным для пользователей по всему миру, я использовал GitHub Actions и fly.io в качестве облачного провайдера. GitHub Actions предоставил автоматизированный рабочий процесс для непрерывной интеграции и развертывания, гарантируя, что приложение всегда будет в курсе последних изменений и улучшений.
Fly.io, глобальный облачный провайдер, позволяет мне размещать приложение с минимальной задержкой, предлагая пользователям быстрый и надежный опыт. Используя GitHub Actions и fly.io, я упростил процесс развертывания и обеспечил бесперебойную работу для пользователей, независимо от их местоположения.
Заключение
Это испанское приложение для налогового поиска на базе GPT является ярким примером того, как технологии LLM революционизируют способы доступа пользователей к информации. Упростив процесс поиска и включив запросы на естественном языке, я сделал налоговую информацию более доступной и удобной для пользователя, чем когда-либо прежде.
Открытый исходный код и доступность исходного кода
Я твердо верю в силу разработки и сотрудничества с открытым исходным кодом. Свидетельством этого является то, что исходный код RentaGPT находится в открытом доступе на GitHub.
Я приглашаю разработчиков и пользователей исследовать, вносить свой вклад и улучшать мою работу. Делясь кодом и идеями, я надеюсь вдохновить на дальнейшие инновации и разработки в области обработки естественного языка (и в области налоговой информации 😎).
Репозиторий GitHub: https://github.com/mpuig/rentagpt