AI-система генерации грантовых заявок

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
AI-система генерации грантовых заявок
Простая
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

AI-генерация заявок на гранты

Грантовая заявка — специфический жанр: структурированный документ с жёсткими требованиями к объёму, разделам и формулировкам. Организации тратят 40–80 часов на одну заявку в крупный фонд. AI не пишет заявку за исследователя — это невозможно и опасно. AI ускоряет черновую работу: трансформирует технические описания в грантовый язык, проверяет соответствие требованиям фонда, генерирует шаблоны разделов и помогает с логикой нарратива.

Архитектура системы генерации заявок

from anthropic import Anthropic
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
import json
from pathlib import Path

client = Anthropic()
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")


class GrantRequirementsAnalyzer:
    """Анализирует требования грантового фонда"""

    def parse_call_for_proposals(self, cfp_text: str) -> dict:
        """Извлекает структурированные требования из конкурсной документации"""
        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=2048,
            messages=[{
                "role": "user",
                "content": f"""Проанализируй конкурсную документацию и извлеки требования.
Верни JSON:
{{
  "funder_name": "название фонда",
  "program_name": "название программы",
  "max_grant_size": "сумма или null",
  "eligible_organizations": ["тип орг. 1", "тип орг. 2"],
  "priority_areas": ["приоритет 1", "приоритет 2"],
  "required_sections": [
    {{"name": "название раздела", "max_words": 500, "description": "что должно быть"}}
  ],
  "evaluation_criteria": [
    {{"criterion": "название", "weight": "процент или описание"}}
  ],
  "key_keywords": ["ключевые слова для использования"],
  "prohibited": ["что нельзя указывать или делать"],
  "deadline": "дата или null",
  "reporting_requirements": "требования к отчётности"
}}

Документация:
{cfp_text[:8000]}

Только JSON."""
            }],
        )

        text = response.content[0].text
        return json.loads(text[text.find("{"):text.rfind("}") + 1])


class OrganizationProfileBuilder:
    """Строит профиль организации для переиспользования в заявках"""

    def __init__(self, org_id: str):
        self.vectorstore = Chroma(
            collection_name=f"org_profile_{org_id}",
            embedding_function=embeddings,
        )

    def add_document(self, doc_type: str, content: str, metadata: dict = {}):
        """
        doc_type: "previous_grant" | "publication" | "team_bio" |
                  "project_results" | "financial_report" | "partner_letter"
        """
        self.vectorstore.add_texts(
            texts=[content],
            metadatas=[{"doc_type": doc_type, **metadata}]
        )

    def find_relevant_evidence(self, query: str, doc_types: list = None) -> list[dict]:
        """Находит релевантные материалы из архива организации"""
        filter_dict = {"doc_type": {"$in": doc_types}} if doc_types else None
        results = self.vectorstore.similarity_search_with_score(
            query, k=6, filter=filter_dict
        )
        return [
            {
                "content": doc.page_content,
                "doc_type": doc.metadata.get("doc_type"),
                "relevance": round(1 - score, 2),
            }
            for doc, score in results
            if (1 - score) > 0.5
        ]


class GrantSectionWriter:
    """Пишет отдельные разделы грантовой заявки"""

    SECTION_PROMPTS = {
        "problem_statement": """Напиши раздел «Обоснование проблемы».
Используй данные и статистику. Покажи значимость проблемы для целевой аудитории фонда.
Избегай общих слов — конкретные факты, ссылки на исследования.""",

        "objectives": """Напиши раздел «Цели и задачи проекта».
Цели должны быть SMART: конкретными, измеримыми, достижимыми, релевантными, ограниченными по времени.
Формат: 1-2 цели + 4-6 задач.""",

        "methodology": """Напиши раздел «Методология / план реализации».
Детальное описание: этапы, методы, инструменты, сроки.
Включи план управления рисками.""",

        "expected_results": """Напиши раздел «Ожидаемые результаты и показатели».
Чёткие количественные KPI. Как будет измеряться успех?
Разделить: прямые результаты (outputs), результаты изменений (outcomes), долгосрочное воздействие (impact).""",

        "team": """Напиши раздел «Команда проекта».
Компетенции ключевых участников. Релевантный опыт. Почему именно эта команда справится?""",

        "budget_justification": """Напиши обоснование бюджета.
Каждая статья расходов должна быть обоснована необходимостью для проекта.
Покажи соответствие запрашиваемых сумм рыночным ценам.""",

        "sustainability": """Напиши раздел «Устойчивость проекта».
Как проект будет развиваться/поддерживаться после окончания грантового периода?
Источники дальнейшего финансирования.""",

        "innovation": """Напиши раздел «Новизна и инновационность».
В чём уникальность подхода? Чем отличается от существующих решений?""",
    }

    def write_section(
        self,
        section_name: str,
        project_brief: dict,
        requirements: dict,
        org_evidence: list[dict],
        word_limit: int = 500,
    ) -> str:
        """Генерирует раздел заявки"""
        section_instruction = self.SECTION_PROMPTS.get(
            section_name,
            f"Напиши раздел '{section_name}'."
        )

        evidence_text = ""
        if org_evidence:
            evidence_text = "\n\nРелевантные материалы организации:\n" + "\n\n".join([
                f"[{e['doc_type']}]: {e['content'][:500]}"
                for e in org_evidence[:4]
            ])

        keywords_instruction = ""
        if requirements.get("key_keywords"):
            keywords_instruction = f"\nВажно использовать ключевые слова фонда: {', '.join(requirements['key_keywords'][:10])}"

        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=2048,
            system=f"""Ты — эксперт по написанию грантовых заявок.
Пишешь для фонда: {requirements.get('funder_name', 'неизвестен')}.
Программа: {requirements.get('program_name', '')}.
Приоритеты фонда: {', '.join(requirements.get('priority_areas', []))}.
Ограничение: {word_limit} слов.{keywords_instruction}

Стиль: профессиональный, конкретный, ориентированный на результат.
НЕ используй шаблонные фразы вроде «данный проект направлен на...»""",
            messages=[{
                "role": "user",
                "content": f"""{section_instruction}

Описание проекта:
{json.dumps(project_brief, ensure_ascii=False, indent=2)}
{evidence_text}

Напиши раздел ({word_limit} слов)."""
            }],
        )

        return response.content[0].text


class GrantApplicationSystem:
    """Полная система генерации грантовых заявок"""

    def __init__(self, org_id: str):
        self.analyzer = GrantRequirementsAnalyzer()
        self.org_profile = OrganizationProfileBuilder(org_id)
        self.writer = GrantSectionWriter()

    def generate_full_application(
        self,
        cfp_text: str,
        project_brief: dict,
    ) -> dict:
        """Генерирует полную грантовую заявку"""

        # 1. Анализируем требования фонда
        requirements = self.analyzer.parse_call_for_proposals(cfp_text)

        # 2. Генерируем разделы
        application = {
            "metadata": {
                "funder": requirements.get("funder_name"),
                "program": requirements.get("program_name"),
                "generated_at": __import__("datetime").datetime.now().isoformat(),
            },
            "sections": {},
        }

        for section_req in requirements.get("required_sections", []):
            section_name = section_req["name"]
            word_limit = section_req.get("max_words", 500)

            # Находим релевантные материалы из архива
            query = f"{section_name} {project_brief.get('title', '')} {project_brief.get('problem', '')}"
            evidence = self.org_profile.find_relevant_evidence(
                query,
                doc_types=["previous_grant", "project_results", "publication"],
            )

            # Маппинг названий разделов на типы промптов
            section_type = self._map_section_type(section_name)

            content = self.writer.write_section(
                section_name=section_type,
                project_brief=project_brief,
                requirements=requirements,
                org_evidence=evidence,
                word_limit=word_limit,
            )

            application["sections"][section_name] = {
                "content": content,
                "word_count": len(content.split()),
                "word_limit": word_limit,
            }

        # 3. Проверяем соответствие критериям
        application["compliance_check"] = self._check_compliance(
            application["sections"],
            requirements,
        )

        return application

    def _map_section_type(self, section_name: str) -> str:
        """Маппинг произвольных названий разделов на типы"""
        name_lower = section_name.lower()
        mapping = {
            "problem": "problem_statement",
            "проблем": "problem_statement",
            "цели": "objectives",
            "задачи": "objectives",
            "методол": "methodology",
            "план": "methodology",
            "результат": "expected_results",
            "команд": "team",
            "бюджет": "budget_justification",
            "устойчив": "sustainability",
            "нови": "innovation",
            "инновац": "innovation",
        }
        for key, value in mapping.items():
            if key in name_lower:
                return value
        return "problem_statement"

    def _check_compliance(self, sections: dict, requirements: dict) -> dict:
        """Проверяет заявку на соответствие требованиям"""
        issues = []
        warnings = []

        # Проверяем объёмы
        for section_name, section_data in sections.items():
            if section_data["word_count"] > section_data["word_limit"] * 1.1:
                issues.append(f"Раздел '{section_name}': превышен лимит слов ({section_data['word_count']} > {section_data['word_limit']})")

        # Проверяем наличие ключевых слов
        all_text = " ".join(s["content"] for s in sections.values()).lower()
        for keyword in requirements.get("key_keywords", [])[:5]:
            if keyword.lower() not in all_text:
                warnings.append(f"Отсутствует ключевое слово: '{keyword}'")

        return {
            "passed": len(issues) == 0,
            "issues": issues,
            "warnings": warnings,
        }

Практический кейс: НКО, 12 заявок в год

Ситуация: некоммерческая организация подаёт 10–15 заявок в год в разные фонды (Президентский фонд культурных инициатив, Фонд «Сколково», региональные фонды). Каждая заявка требует переработки под требования конкретного фонда. 2–3 сотрудника тратили суммарно 600+ часов в год на написание.

Что сделали:

  • Проиндексировали 35 предыдущих успешных заявок в org_profile
  • Система генерирует черновые разделы за 15–20 минут
  • Compliance-чекер проверяет соответствие лимитам и ключевым словам
  • Финальная правка живым автором: 6–10 часов вместо 40–80

Результаты:

  • Время подготовки заявки: 50–80 ч → 8–12 ч
  • Число заявок в год: 12 → 22 (та же команда)
  • Процент успешных заявок: без изменений (46% до и после) — AI не влияет на содержательное качество

Важное ограничение: AI-черновик требует тщательной проверки. В заявках для Сколково система включила формулировки из предыдущих заявок, которые противоречили новым требованиям программы. Compliance-чекер это не поймал — добавили шаг ручного просмотра критериев оценки перед финальной подачей.

Сроки

  • Анализатор требований фонда: 3–5 дней
  • Профиль организации + индексирование архива: 3–5 дней
  • Генератор разделов + compliance-чекер: 1 неделя
  • Полная система с веб-интерфейсом: 3–4 недели