- BrainTools - https://www.braintools.ru -
Мой отец — человек, переживший несколько сложнейших операций на сердце. Жизнь с хроническим заболеванием — это бесконечный поток анализов, заключений и схем приёма лекарств. Находясь далеко (я живу во Вьетнаме), я постоянно волновался: не забудет ли он про дозу, правильно ли понял назначение, задал ли все нужные вопросы врачу?
Мне нужен был не просто бот-напоминалка, а второй пилот — умный, конфиденциальный и мультимодальный AI-Кардиолог. Ассистент, который знает его анамнез наизусть, понимает голосовые команды и может “прочитать” фотографию свежего анализа.
Я решил собрать полноценный автономный агент с возможностью вызова внешних инструментов (Tool-Calling) и локальной базой знаний (RAG), но без использования громоздких фреймворков вроде LangChain или LlamaIndex.
Моя цель: максимальная надёжность, локальность данных и предсказуемость.
Ядро системы — это Полноценный Tool-Calling Pipeline на GPT-4o-mini, который работает в два этапа: Планирование и Генерация.
|
Компонент |
Технология |
Роль в системе |
|
Планировщик |
GPT-4o-mini (OpenRouter) |
Принимает решение: |
|
База знаний (RAG) |
ChromaDB + SentenceTransformer (локально) |
Хранит историю болезни, анализы, заметки. |
|
Веб-поиск |
DuckDuckGo Search (DDGS) |
Предоставляет актуальные медицинские данные из сети. |
|
Мультимодальность |
Tesseract OCR + AssemblyAI STT |
Понимание фотоанализов и голосовых сообщений. |
|
Метаданные |
SQLite |
Надёжное хранение истории чата и метаинформации о документах. |
Вместо того чтобы надеяться на то, что модель сама “вспомнит” или “погуглит”, я заставляю её выбрать инструмент, прежде чем давать ответ.
Мы передаём модели текущий вопрос и историю диалога. Самое важное: мы заставляем её вернуть ответ в строгом формате JSON:
JSON
{
"tool": "local_rag,internet_search",
"query": "последний уровень холестерина и побочные эффекты статинов"
}
Преимущества JSON: Это делает пайплайн невероятно надёжным. При ошибке [1] парсинга я выполняю Graceful Fallback — автоматически переключаюсь на local_rag как н�� самый безопасный вариант.
После получения плана мы выполняем поиск:
Локальный RAG: Используем поисковый запрос (query из JSON) для извлечения релевантных личных данных из ChromaDB.
Веб-поиск: Используем DuckDuckGo Search с региональными настройками (region='ru-ru') для получения актуальной информации.
Все найденные данные объединяются в один системный промпт для финальной модели. При этом я использую жёсткую маркировку (например, === ИНФОРМАЦИЯ ИЗ ВЕБ-ПОИСКА ===), чтобы модель чётко разделяла личные факты и общие знания.
Вот как выглядит ядро пайплайна на Python.
Эта функция объединяет планирование и генерацию. Обратите внимание [2], как мы фиксируем текущую дату в промпте — это критически важно для медицинского ассистента при расчёте сроков действия рецептов или возраста пациента.
Python
# ГЛАВНАЯ ФУНКЦИЯ: Tool Calling (Планирование) с фиксацией даты
def chat_with_assistant(user_id: int, message_text: str) -> str:
# 💡 ИСПРАВЛЕНИЕ: Получаем текущую дату и время
current_datetime_str = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
PLANNING_PROMPT = f"""
Проанализируй следующий вопрос... Текущая дата: {current_datetime_str}.
Тебе нужно решить, какой инструмент необходим...
# ... (Остальная часть промпта) ...
Вопрос пациента: "{message_text}"
"""
# --- ВЫЗОВ 1: ПЛАНИРОВАНИЕ (Формат JSON) ---
data_plan = {
"model": "gpt-4o-mini",
# ...
"response_format": {"type": "json_object"} # Принудительный JSON
}
# ... (Обработка ответа, извлечение tools и query) ...
# --- ВЫПОЛНЕНИЕ ПЛАНА (Сбор контекста) ---
# ... (Вызовы retrieve_relevant и search_internet) ...
# --- ВЫЗОВ 2: ГЕНЕРАЦИЯ ОТВЕТА ---
if context_parts:
# Жёсткая инструкция для финального ответа
STRICT_INSTRUCTION = "nnВНИМАНИЕ! ... Твой ответ ОБЯЗАН быть основан на информации из раздела 'ИНФОРМАЦИЯ ИЗ ВЕБ-ПОИСКА'. ..."
context_joined = "nn=== РЕЛЕВАНТНЫЙ КОНТЕКСТ (ОБЯЗАТЕЛЬНО ИСПОЛЬЗУЙ) ===n" + "nn---nn".join(context_parts)
full_system_prompt = SYSTEM_PROMPT + STRICT_INSTRUCTION + context_joined
# ... (Добавление истории и отправка финального запроса) ...
Вся медицинская история хранится локально с помощью ChromaDB и SentenceTransformer.
Python
# ---------------------------
# ChromaDB Embedder Initialization
# ---------------------------
try:
# ... (импорт и инициализация) ...
LOCAL_EMBEDDING_MODEL_NAME = "all-MiniLM-L6-v2"
CHROMA_EMBEDDER = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name=LOCAL_EMBEDDING_MODEL_NAME,
device='cpu' # Ключевой момент: все локально!
)
# ... (создание коллекции) ...
except Exception as e:
logger.error("Ошибка при инициализации SentenceTransformer: %s", e)
Это делает ассистента удобным для человека, не слишком активно пользующегося новыми технологиями.
Фотографии документов переводятся в текст, затем отправляются в LLM на расшифровку и сохраняются в RAG.
Python
@bot.message_handler(content_types=['photo'])
def handle_photo(message):
# ... (скачивание файла) ...
raw_text = extract_text_from_image_bytes(downloaded)
# Анализ и саммаризация текста нейросетью
summary = analyze_medical_text(raw_text)
# Сохраняем в RAG
add_to_chroma(doc_id, summary, metadata)
# ... (ответ пользователю) ...
Я добавил логику [3] автоматического распознавания намерения для голосовых сообщений, начинающихся со слова “запомни”.
Python
@bot.message_handler(content_types=['voice'])
def handle_voice(message):
# ... (транскрипция с AssemblyAI) ...
# 💡 ЛОГИКА АВТОМАТИЧЕСКОГО ЗАПОМИНАНИЯ ДЛЯ ГОЛОСА
if transcribed_text.lower().startswith("запомни"):
memory_text = transcribed_text[len("запомни"):].strip()
if memory_text:
# Сохраняем данные в RAG и прерываем обычный диалог
add_to_chroma(doc_id, memory_text, metadata)
# ...
return
# Если это не команда "запомни", передаем в основную логику чата
resp = chat_with_assistant(message.chat.id, transcribed_text)
bot.reply_to(message, resp)
Я создал автономного медицинского ассистента, который:
Всегда помнит его личную историю, анализы и заметки.
Умеет и��кать актуальную информацию в сети.
Понимает любой ввод: текст, фото или голос.
Сам выбирает, что делать с помощью двухэтапного Tool-Calling.
Это не просто код, это часть заботы. Проект показал, как можно использовать современные возможности LLM, RAG и мультимодальности для решения реальных и очень личных проблем, сохраняя при этом контроль, конфиденциальность и надёжность.
Надеюсь, мой опыт [4] вдохновит и вас на создание социально-значимых проектов, где код служит самой важной цели.
Автор: Stasiao
Источник [5]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/21258
URLs in this post:
[1] ошибке: http://www.braintools.ru/article/4192
[2] внимание: http://www.braintools.ru/article/7595
[3] логику: http://www.braintools.ru/article/7640
[4] опыт: http://www.braintools.ru/article/6952
[5] Источник: https://habr.com/ru/articles/961280/?utm_source=habrahabr&utm_medium=rss&utm_campaign=961280
Нажмите здесь для печати.