Научите вашего AI-агента читать Notion-страницы, создавать документы, управлять базами данных и обновлять контент в реальном времени. Полный гайд по интеграции Notion API с Python-агентами: от получения API-ключа до автономного AI-ассистента, который сам ведёт вашу документацию, трекает задачи и обогащает знания из внешних источников.
Первым делом нужно создать интеграцию в Notion и получить API-ключ. Без этого шага AI-агент не сможет взаимодействовать с вашим workspace.
Read content, Update content, Insert content. Для агента, который будет создавать и редактировать страницы, нужны все три.secret_ или ntn_).# .env или прямо в коде NOTION_API_KEY = "secret_abc123..." # Ваш Internal Integration Secret NOTION_DATABASE_ID = "c4f8e2a1..." # ID базы данных (из URL) NOTION_VERSION = "2022-06-28" # Версия API (обязательный заголовок)
Notion предоставляет официальный Python SDK — notion-client. Установим его вместе с openai для LLM и python-dotenv для управления переменными окружения.
# Установка зависимостей
pip install notion-client openai python-dotenv rich
# config.py — базовая конфигурация агента import os from dotenv import load_dotenv from notion_client import Client from openai import OpenAI load_dotenv() # Notion client notion = Client(auth=os.environ["NOTION_API_KEY"]) database_id = os.environ["NOTION_DATABASE_ID"] # OpenAI client openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) # Проверка подключения к Notion try: me = notion.users.me() print(f"✅ Подключено как: {me['name']} ({me['type']})") except Exception as e: print(f"❌ Ошибка подключения: {e}") raise
AI-агент использует function calling (tool use) — мы описываем функции, а LLM решает, какую вызвать и с какими аргументами. Ниже — полный набор инструментов для работы с Notion. Каждая функция возвращает структурированный JSON, который LLM может интерпретировать и пересказать пользователю.
# notion_tools.py — инструменты для AI-агента from notion_client import Client import json notion = Client(auth="secret_...") # ═══ 1. Поиск страниц и баз данных ═══ def search_notion(query: str, filter_type: str = "page") -> str: """Поиск в Notion по ключевым словам. filter_type: page или database.""" results = notion.search( query=query, filter={"property": "object", "value": filter_type} ).get("results", []) if not results: return json.dumps({"found": 0, "message": "Ничего не найдено"}) items = [] for r in results[:10]: title = "" if "title" in r and r["title"]: title = " ".join([t.get("plain_text", "") for t in r["title"]]) items.append({ "id": r["id"].replace("-", ""), "title": title, "url": r.get("url", ""), "type": r["object"] }) return json.dumps({"found": len(items), "items": items}, ensure_ascii=False) # ═══ 2. Чтение содержимого страницы ═══ def get_page_content(page_id: str) -> str: """Получить содержимое Notion-страницы: все блоки с текстом.""" blocks = [] has_more = True cursor = None while has_more: response = notion.blocks.children.list( block_id=page_id, page_size=100, start_cursor=cursor ) blocks.extend(response.get("results", [])) has_more = response.get("has_more", False) cursor = response.get("next_cursor") text_parts = [] for block in blocks: block_type = block.get("type", "") content = block.get(block_type, {}) rich_text = content.get("rich_text", []) line = "".join([t.get("plain_text", "") for t in rich_text]) if line.strip(): prefix = {"heading_1": "# ", "heading_2": "## ", "heading_3": "### "}.get(block_type, "") text_parts.append(prefix + line) full_text = "\n".join(text_parts)[:8000] # Ограничиваем контекст return json.dumps({ "page_id": page_id, "block_count": len(blocks), "content": full_text, "truncated": len("\n".join(text_parts)) > 8000 }, ensure_ascii=False) # ═══ 3. Создание новой страницы ═══ def create_notion_page(title: str, content: str, parent_page_id: str = None) -> str: """Создать страницу в Notion с markdown-подобным контентом.""" parent = {} if parent_page_id: parent = {"type": "page_id", "page_id": parent_page_id} else: parent = {"type": "database_id", "database_id": database_id} new_page = notion.pages.create( parent=parent, properties={ "title": {"title": [{"type": "text", "text": {"content": title}}]} }, children=_text_to_blocks(content) ) return json.dumps({ "status": "created", "page_id": new_page["id"].replace("-", ""), "url": new_page.get("url", ""), "title": title }, ensure_ascii=False) # ═══ 4. Обновление содержимого страницы ═══ def update_page_content(page_id: str, new_content: str, mode: str = "replace") -> str: """Обновить содержимое страницы. mode: replace — заменить всё, append — добавить в конец.""" if mode == "replace": # Удаляем все существующие блоки existing = notion.blocks.children.list(block_id=page_id, page_size=100) for block in existing.get("results", []): notion.blocks.delete(block_id=block["id"]) # Добавляем новые блоки notion.blocks.children.append( block_id=page_id, children=_text_to_blocks(new_content) ) return json.dumps({ "status": "updated", "page_id": page_id, "mode": mode }) # ═══ 5. Запрос к базе данных ═══ def query_database(db_id: str, status_filter: str = None, limit: int = 20) -> str: """Запросить записи из базы данных с опциональным фильтром по статусу.""" filter_obj = {} if status_filter: filter_obj = { "property": "Status", "status": {"equals": status_filter} } response = notion.databases.query( database_id=db_id, filter=filter_obj if filter_obj else None, page_size=limit ) rows = [] for page in response.get("results", []): props = page.get("properties", {}) title = "" for key, prop in props.items(): if prop.get("type") == "title": title = "".join([t.get("plain_text", "") for t in prop.get("title", [])]) break rows.append({ "id": page["id"].replace("-", ""), "title": title, "url": page.get("url", "") }) return json.dumps({ "total": len(rows), "has_more": response.get("has_more", False), "rows": rows }, ensure_ascii=False) # ═══ Вспомогательная функция: текст → Notion блоки ═══ def _text_to_blocks(text: str) -> list: """Конвертирует markdown-подобный текст в Notion block objects.""" blocks = [] for line in text.strip().split("\n"): if not line.strip(): continue block_type = "paragraph" content = line if line.startswith("# "): block_type = "heading_1"; content = line[2:] elif line.startswith("## "): block_type = "heading_2"; content = line[3:] elif line.startswith("- "): block_type = "bulleted_list_item"; content = line[2:] blocks.append({ "object": "block", "type": block_type, block_type: {"rich_text": [{"type": "text", "text": {"content": content}}]} }) return blocks
Теперь собираем AI-агента, который использует OpenAI Function Calling для выбора и вызова Notion-инструментов. Агент принимает промпт пользователя на естественном языке, выбирает нужную функцию, вызывает Notion API и возвращает понятный ответ.
# agent.py — AI-агент с Notion tools import json from openai import OpenAI from notion_tools import ( search_notion, get_page_content, create_notion_page, update_page_content, query_database ) client = OpenAI() # ═══ Определение функций для Function Calling ═══ TOOLS = [ { "type": "function", "function": { "name": "search_notion", "description": "Поиск страниц и баз данных в Notion по ключевым словам", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "Поисковый запрос"}, "filter_type": {"type": "string", "enum": ["page", "database"], "description": "Тип объекта"} }, "required": ["query"] } } }, { "type": "function", "function": { "name": "get_page_content", "description": "Получить полное содержимое Notion-страницы по её ID", "parameters": { "type": "object", "properties": { "page_id": {"type": "string", "description": "ID страницы Notion (32 символа, без дефисов)"} }, "required": ["page_id"] } } }, { "type": "function", "function": { "name": "create_notion_page", "description": "Создать новую страницу в Notion с заданным заголовком и содержимым", "parameters": { "type": "object", "properties": { "title": {"type": "string", "description": "Заголовок страницы"}, "content": {"type": "string", "description": "Содержимое (markdown): каждая строка = блок"}, "parent_page_id": {"type": "string", "description": "ID родительской страницы (опционально)"} }, "required": ["title", "content"] } } }, { "type": "function", "function": { "name": "update_page_content", "description": "Обновить содержимое существующей страницы (заменить или дополнить)", "parameters": { "type": "object", "properties": { "page_id": {"type": "string", "description": "ID страницы"}, "new_content": {"type": "string", "description": "Новое содержимое (markdown)"}, "mode": {"type": "string", "enum": ["replace", "append"], "description": "Режим обновления"} }, "required": ["page_id", "new_content"] } } }, { "type": "function", "function": { "name": "query_database", "description": "Запросить записи из базы данных Notion с фильтрацией", "parameters": { "type": "object", "properties": { "db_id": {"type": "string", "description": "ID базы данных"}, "status_filter": {"type": "string", "description": "Фильтр по статусу (например: In Progress, Done)"}, "limit": {"type": "integer", "description": "Максимальное количество записей"} }, "required": ["db_id"] } } } ] # ═══ Маппинг функций ═══ AVAILABLE_FUNCTIONS = { "search_notion": search_notion, "get_page_content": get_page_content, "create_notion_page": create_notion_page, "update_page_content": update_page_content, "query_database": query_database, } # ═══ Основной цикл агента ═══ def run_agent(user_prompt: str) -> str: """Запустить AI-агента с пользовательским промптом.""" messages = [ {"role": "system", "content": """Ты AI-ассистент с доступом к Notion workspace пользователя. Ты можешь: искать страницы, читать содержимое, создавать новые документы, обновлять существующие, и запрашивать базы данных. Отвечай на русском. Всегда указывай URL созданных/найденных страниц."""}, {"role": "user", "content": user_prompt} ] # Первый вызов: LLM решает, нужен ли tool call response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=TOOLS, tool_choice="auto", temperature=0.3 ) response_message = response.choices[0].message tool_calls = response_message.tool_calls # Если tool call не нужен — возвращаем текстовый ответ if not tool_calls: return response_message.content # Добавляем ответ ассистента с tool_calls в историю messages.append(response_message) # Выполняем все tool calls for tool_call in tool_calls: function_name = tool_call.function.name function_args = json.loads(tool_call.function.arguments) print(f"🔧 Вызов: {function_name}({function_args})") function_to_call = AVAILABLE_FUNCTIONS[function_name] function_response = function_to_call(**function_args) # Добавляем результат tool call в историю messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": function_response }) # Второй вызов: LLM формулирует финальный ответ на основе результатов final_response = client.chat.completions.create( model="gpt-4o", messages=messages, temperature=0.5 ) return final_response.choices[0].message.content # ═══ Пример использования ═══ if __name__ == "__main__": result = run_agent("Найди все страницы про API интеграции и создай сводный документ с их кратким содержанием") print(f"\n📄 Ответ агента:\n{result}")
Когда базовые инструменты готовы, можно строить сложные AI-пайплайны. Два самых востребованных сценария: RAG-поиск по всей базе знаний (агент читает Notion-страницы, индексирует их содержимое и отвечает на вопросы с цитированием источников) и авто-документирование (агент мониторит изменения в коде/задачах и автоматически обновляет документацию в Notion).
# rag_notion.py — RAG-поиск по Notion workspace from notion_tools import search_notion, get_page_content from openai import OpenAI import json client = OpenAI() def rag_search_notion(question: str, top_k: int = 5) -> str: """RAG: найти релевантные страницы → прочитать → ответить с цитатами.""" # Шаг 1: Поиск релевантных страниц search_result = json.loads(search_notion(query=question, filter_type="page")) if search_result["found"] == 0: return "❌ Не найдено релевантных страниц в Notion." # Шаг 2: Читаем содержимое top-K страниц contexts = [] for item in search_result["items"][:top_k]: page_data = json.loads(get_page_content(item["id"])) contexts.append({ "title": item["title"], "url": item["url"], "content": page_data.get("content", "") }) # Шаг 3: Формируем промпт с контекстом и отправляем LLM context_text = "\n\n---\n\n".join([ f"Источник: {c['title']} ({c['url']})\n{c['content'][:3000]}" for c in contexts ]) rag_response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "Отвечай на вопрос, используя ТОЛЬКО предоставленный контекст из Notion. Указывай источники (URL страниц). Если информации недостаточно — скажи об этом честно. Отвечай на русском."}, {"role": "user", "content": f"Контекст из Notion:\n{context_text}\n\nВопрос: {question}"} ], temperature=0.2 ) return rag_response.choices[0].message.content
# auto_docs.py — автоматическое обновление Notion-документации from notion_tools import update_page_content, search_notion import json def auto_update_release_notes(version: str, changes: list): """Автоматически обновляет Release Notes в Notion с новыми изменениями.""" # Ищем страницу Release Notes search = json.loads(search_notion(query="Release Notes", filter_type="page")) if search["found"] == 0: return "❌ Страница Release Notes не найдена. Создайте её вручную и подключите интеграцию." page_id = search["items"][0]["id"] # Формируем блок с изменениями new_section = f"## {version}\n\n" for change in changes: new_section += f"- {change}\n" # Добавляем в начало страницы (append) result = json.loads(update_page_content( page_id=page_id, new_content=new_section, mode="append" )) return f"✅ Release Notes обновлены: добавлена версия {version}" # Пример использования changes_list = [ "Добавлен AI-агент для Notion", "Исправлен баг с пагинацией блоков", "Обновлён SDK до версии 2.3.0" ] print(auto_update_release_notes("v2.3.0", changes_list)) # Вывод: ✅ Release Notes обновлены: добавлена версия v2.3.0
Мы создали полноценного AI-агента, который умеет работать с Notion API через function calling. Агент понимает промпты на естественном языке и автоматически выбирает нужный инструмент: поиск страниц, чтение содержимого, создание новых документов, обновление существующих и запросы к базам данных.
RAG-поиск по всему workspace с цитированием источников
Автоматическое создание и обновление страниц
Фильтрация и анализ данных в Notion-базах
Авто-релиз ноуты из GitHub/GitLab в Notion
notion-client). Документация полная, сообщество активное..env + python-dotenv. Ограничивайте доступ интеграции только к нужным страницам через Notion Connections.has_more и next_cursor для больших страниц.Разверните агента как Telegram-бота или Slack-приложение. Добавьте эмбеддинги для семантического поиска. Подключите вебхуки для real-time синхронизации.
📖 Notion API ReferenceQantCore Guide • ai-agent-notion • Июнь 2026 • slug: ai-agent-notion