LangGraph для 1С-разработчика: агент ждёт вашего «да» перед записью, стенд за 3 минуты

30.09.26

Интеграция - Нейросети

Стенд с LangGraph для 1С: агент перед записью в базу останавливается и ждёт вашего «да». Три звена: общий модуль 1С, шлюз на FastAPI и граф LangGraph с паузой interrupt(). 1С отправляет инцидент и сразу получает ответ 202, граф работает в фоне, адаптер опрашивает статус. Пока оператор не ответил, модуль 1С отказывается применять пакет исправлений; после одобрения пакет записывается в справочник в транзакции. Проверено: модуль проходит проверку модулей платформы 8.3.27, путь от вызова из 1С до записи реквизита в карточку пройден на ней через внешнее соединение, три теста pytest проходят, стенд запускается за 3 минуты (LangGraph 1.2.12, Python 3.12). В статье: состояние графа, идемпотентность, три свойства interrupt(), которые определяют устройство узла, и найденные ошибки. Инструменты агентов в стенде - заглушки, чего ещё нет (PostgresSaver, авторизация шлюза, реальная модель), перечислено в тексте. Код приложен.

Файлы

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Скачано Купить файл
LangGraph для 1С-разработчика: агент ждёт вашего «да» перед записью, стенд за 3 минуты
.zip 324,65Kb
0 2 500 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Вы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

От монолита к автономным агентам (Часть 3): супервайзер LangGraph в контуре 1С с паузой на подтверждение человека

Серия «От монолита к автономным агентам», часть 3 из 4. Часть 1: архитектура и граф состояний ИИ-агента в 1С:Предприятие. Часть 2: почему линейный граф состояний не масштабируется.

В этой части собран рабочий стенд из трёх звеньев: общий модуль 1С, шлюз на FastAPI и граф LangGraph, который перед записью в базу останавливается и ждёт решения человека. Код стенда приложен к статье. Модуль 1С проверен встроенной проверкой Конфигуратора платформы 8.3.27, а весь путь от вызова из 1С до записи в карточку номенклатуры пройден на этой платформе через внешнее соединение.

Что здесь заглушка. Агенты Auditor, Inspector и Remediator возвращают фиксированные ответы, а роль модели играет детерминированный мок. Стенд показывает каркас: маршрутизацию, состояние, паузу interrupt(), возобновление и обмен с 1С. Разбор реальных логов и метаданных в него не входит. Чего ещё нет, перечислено в разделе «Что стенд не умеет».

Схема стенда: учётная база 1С, шлюз, граф LangGraph и пауза на подтверждение

🔍 Что получится и на чём проверено

К концу статьи у вас будет стенд, в котором регламентное задание 1С передаёт инцидент внешнему графу, не блокируя сеанс. Граф собирает исправление и останавливается на паузе. Пока человек не ответит, адаптер 1С отказывается что-либо записывать. После одобрения пакет применяется в транзакции, а реквизит СтавкаНДС у карточки номенклатуры заполняется.

Стенд собран и проверен на одной машине:

Компонент Версия
ОС Windows 11 Pro 10.0.22631
Платформа 1С 8.3.27.2130 (x86), файловая база, внешнее соединение
Python 3.12.10
LangGraph 1.2.12 (langchain-core 1.6.6)
FastAPI / Uvicorn / Pydantic 0.141.1 / 0.54.0 / 2.13.5
pytest 9.1.1

Версии закреплены в requirements.txt. От пустой папки до паузы графа и одобрения в консольном клиенте прошло 100 и 117 секунд в двух прогонах (evidence/fresh_run.py): окружение, pip install без кэша (77 и 92 с), тесты, запуск шлюза. Три минуты в названии статьи - это эти замеры, округлённые вверх. Платформа 1С в них не участвует.

Проверено и не проверено:

Что Статус
Три теста pytest: полный цикл с паузой, отказ оператора, шлюз с дедупликацией прошли
Консольный эмулятор 1С (app.mock_1c_service) против запущенного шлюза отработал
Модуль НОП_АгентИнтеграция: проверка модулей платформой 8.3.27 ошибок нет
Вызов из 1С через внешнее соединение, пауза, решение, запись в справочник пройден
docker compose, образ из Dockerfile не запускал
PostgresSaver вместо MemorySaver не запускал
Настоящая модель вместо мока не запускал
Тесты YAxUnit для модуля 1С не писал, фреймворк на стенде не стоял
Уведомление оператора в Telegram не реализовано

🔍 Ретроспектива: что мы решили в частях 1 и 2

В первой части агент был линейной цепочкой: принять запрос из формы 1С, классифицировать, выполнить одним узлом и вернуть команду в 1С.

Запрос из 1С
↓
analyze_intent
↓
plan_executor
↓
deliver_to_1c
↓
Применение в 1С

Во второй части мы разобрали, почему с ростом числа сценариев узел plan_executor превращается в узкое место. Ему нужны описания всех инструментов сразу, ошибка внутри узла обрывает всю цепочку, а цикл уточнения («сходи в 1С, проверь метаданные, если реквизита нет, возьми лог ошибки, потом предложи исправление») в линейной схеме не выразить.

Выход: разделить монолитный узел на независимых исполнителей и поставить над ними распорядителя, супервайзера. В этой части мы делаем это на LangGraph.

🔍 Архитектура стенда

Мы взяли LangGraph по трём причинам, которые проверили на стенде. Граф допускает циклы: исполнитель возвращает управление распорядителю. Переход задаётся кодом (Command(goto=...)), свободного диалога между моделями нет. И в одной библиотеке есть остановка графа interrupt() с сохранением состояния. С другими библиотеками для агентов мы этот стенд не сравнивали.

Вычисления модели вынесены за пределы серверов 1С: граф живёт отдельным процессом и общается с базой только по HTTP. Часть 1 называла это Zero-Footprint Core. Стенд состоит из трёх звеньев.

Рис. 1. Три звена стенда: 1С, шлюз FastAPI, граф LangGraph

Рис. 1. Три звена стенда. Жёлтая плашка внизу перечисляет, что в стенде заглушка и чего нет.

Граф в стенде плоский: один узел-распорядитель и три исполнителя, подграфов нет. Иерархию из части 2 (подграфы для каждого исполнителя) стенд не строит.

Узел Что должен делать Что делает в стенде Запись в 1С
Supervisor выбирает следующего исполнителя, решает, когда закончить мок по правилам: нет причины - Auditor, нет метаданных - Inspector, нет одобренного исправления - Remediator, иначе завершение; после отказа оператора завершение нет
Inspector читает метаданные: реквизиты, типы, допустимые значения возвращает строку про перечисление СтавкиНДС нет, только чтение
Auditor разбирает журнал регистрации и технологический журнал возвращает фиксированную причину: не заполнена СтавкаНДС нет, только чтение
Remediator формирует исправление возвращает фиксированный пакет и вызывает interrupt() только через подтверждение человека

Рабочие инструменты этих узлов (чтение журнала регистрации, запрос метаданных, разбор техжурнала) в стенде не написаны. Это следующий шаг, и в части 4 он пойдёт через MCP.

🔍 Состояние графа и контракты Pydantic

Состояние графа - типизированный словарь TypedDict. Вложенные структуры описаны моделями Pydantic v2: контекст инцидента, шаг аудиторского следа, пакет исправления, ответ распорядителя.

Листинг 1. Состояние и контракты (app/state.py)

Python UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# -*- coding: utf-8 -*-
"""
Схема типизированного состояния мультиагентного графа NOPik.
Pydantic v2 контракты для сопряжения с платформой «1С:Предприятие 8.3».
"""

from enum import Enum
from typing import Annotated, Sequence, Optional, Dict, Any, List, Literal
from typing_extensions import TypedDict
from pydantic import BaseModel, Field
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages


class ExecutionSeverity(str, Enum):
    INFO = "INFO"
    WARNING = "WARNING"
    CRITICAL = "CRITICAL"


class OneCContext(BaseModel):
    """Контекст исполнения на стороне корпоративного контура 1С."""
    infobase_id: str = Field(description="Уникальный идентификатор информационной базы 1С")
    task_guid: str = Field(description="GUID задачи БСП / Длительной операции")
    user_name: str = Field(default="RobotAgent", description="Пользователь, инициировавший операцию")
    idempotency_key: str = Field(description="Токен защиты от повторного выполнения (X-Idempotency-Key)")
    subsystem: str = Field(default="ИнтеграцияМаркетплейсы", description="Имя подсистемы 1С")
    error_payload: Optional[str] = Field(default=None, description="Сырой текст ошибки из журнала регистрации")


class DecisionStep(BaseModel):
    """Шаг сквозного аудиторского следа (Audit Trail)."""
    agent_name: str
    action_taken: str
    rationale: str
    timestamp_utc: str
    tool_inputs: Optional[Dict[str, Any]] = None
    tool_output: Optional[Dict[str, Any]] = None


class RemediationAction(BaseModel):
    """Пакет корректирующих действий для контура 1С."""
    action_type: Literal["SET_FIELD_VALUE", "RERUN_BACKGROUND_JOB", "NOTIFY_OPERATOR", "APPLY_HOTPATCH"] = "SET_FIELD_VALUE"
    target_object_meta: str = Field(description="Имя объекта метаданных 1С (например, Справочник.Номенклатура)")
    target_object_guid: Optional[str] = Field(None, description="Строковый GUID ссылки объекта в 1С")
    parameters: Dict[str, Any] = Field(default_factory=dict, description="Параметры операции для 1С")
    requires_human_approval: bool = Field(True, description="Флаг обязательного ручного подтверждения (HITL)")
    is_approved: bool = Field(False, description="Флаг фактического одобрения оператором")


class SupervisorOutput(BaseModel):
    """Строго типизированный вывод супервайзера для роутинга."""
    next_step: Literal["agent_inspector", "agent_auditor", "agent_remediator", "FINISH"] = Field(
        description="Следующий узел для маршрутизации или завершение графа"
    )
    routing_reason: str = Field(description="Обоснование решения для логов и телеметрии")
    requires_interrupt: bool = Field(default=False, description="Нужно ли прервать граф для подтверждения человеком")


class NopikAgentState(TypedDict):
    """Общее состояние мультиагентного графа NOPik."""
    # Сообщения диалога с поддержкой append-only слияния
    messages: Annotated[Sequence[BaseMessage], add_messages]
    
    # Корпоративный контекст транзакции 1С
    enterprise_context: OneCContext
    
    # Текущий назначенный узел
    next: str
    
    # Диагностические факты
    identified_error_signature: Optional[str]
    root_cause_analysis: Optional[str]
    
    # Сформированный пакет исправлений
    remediation: Optional[RemediationAction]
    
    # Статус подтверждения человеком (HITL)
    is_approved_by_human: bool
    
    # Решение оператора: None пока не спрашивали, "approved" или "rejected"
    operator_decision: Optional[str]
    
    # Сквозной аудит действий для SRE
    audit_trail: List[DecisionStep]

Две детали, которые пригодятся при доработке. Поле operator_decision появилось в стенде после проверки: без него граф после отказа оператора возвращался к Remediator и спрашивал заново. Поле messages накапливает сообщения по правилу add_messages.

🔍 Шлюз FastAPI: асинхронный протокол 1С и графа

Синхронный вызов модели из пользовательского сеанса или из транзакции проведения занимает серверный вызов на всё время ожидания ответа. Поэтому 1С только отправляет инцидент и получает в ответ 202 Accepted. Дальше граф работает в фоне шлюза, а 1С опрашивает статус. На стенде вызов из 1С вернул управление за 35 и 74 миллисекунды в двух сохранённых прогонах (замер ниже).

Шлюз принимает инцидент, регистрирует его и запускает граф в фоне:

Python UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
async def run_graph_task(task_guid: str, initial_state: NopikAgentState):
    """Асинхронный исполнитель графа LangGraph."""
    config = {
        "configurable": {"thread_id": task_guid},
        "recursion_limit": settings.recursion_limit
    }
    
    TASKS_DB[task_guid]["status"] = "RUNNING"
    TASKS_DB[task_guid]["updated_at"] = datetime.now(timezone.utc).isoformat()
    
    try:
        # Запуск до первой точки останова (interrupt) или до END
        await compiled_app.ainvoke(initial_state, config=config)
        
        # Проверяем, остановился ли граф на interrupt()
        state_snapshot = await compiled_app.aget_state(config)
        if state_snapshot.next and len(state_snapshot.next) > 0:
            # Граф находится в точке прерывания
            interrupt_val = state_snapshot.tasks[0].interrupts[0].value if state_snapshot.tasks and state_snapshot.tasks[0].interrupts else {}
            TASKS_DB[task_guid]["status"] = "INTERRUPTED_AWAITING_APPROVAL"
            TASKS_DB[task_guid]["interrupt_data"] = interrupt_val
            TASKS_DB[task_guid]["audit_trail"] = state_snapshot.values.get("audit_trail", [])
        else:
            # Граф завершился (END)
            TASKS_DB[task_guid]["status"] = "COMPLETED"
            TASKS_DB[task_guid]["remediation"] = state_snapshot.values.get("remediation")
            TASKS_DB[task_guid]["audit_trail"] = state_snapshot.values.get("audit_trail", [])
            
    except Exception as e:
        TASKS_DB[task_guid]["status"] = "FAILED"
        TASKS_DB[task_guid]["error"] = str(e)
    finally:
        TASKS_DB[task_guid]["updated_at"] = datetime.now(timezone.utc).isoformat()

Ключ идемпотентности нужен, потому что клиент, не получивший ответ, повторит запрос и не узнает, дошёл ли первый. Шлюз хранит ключи в словаре и на повтор возвращает уже созданную задачу:

Python UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
    idemp_key = x_idempotency_key or req.idempotency_key
    
    # 1. Проверка идемпотентности
    if idemp_key in IDEMPOTENCY_STORE:
        existing_task_guid = IDEMPOTENCY_STORE[idemp_key]
        return {
            "status": "DUPLICATE_ACCEPTED",
            "task_guid": existing_task_guid,
            "message": "Задача с данным ключом идемпотентности уже обрабатывается"
        }
        
    IDEMPOTENCY_STORE[idemp_key] = req.task_guid

В стенде этот словарь лежит в памяти процесса. Для рабочей базы нужна таблица с индексом по ключу, где вторая вставка с тем же ключом отклоняется самой базой. Такой таблицы в стенде нет.

Шлюз выставляет четыре метода. Интерфейс OpenAPI, который FastAPI строит сам, показывает их так:

Рис. 2. Методы шлюза в Swagger UI (снимок стенда)

Рис. 2. Снимок http://127.0.0.1:8000/docs запущенного стенда.

Обработчик run_graph_task объявлен async и вызывает ainvoke. В исходной редакции стенда там стоял синхронный invoke: внутри async-функции он занимает цикл событий FastAPI на всё время работы графа, и при работе с моделью шлюз не отвечал бы на опросы. Узлы графа у нас синхронные; ainvoke их выполняет, и тесты с прогоном это подтверждают.

🔍 Супервайзер и исполнители на LangGraph

Распорядитель выбирает следующий шаг. Когда включена модель (USE_MOCK_LLM=false, заданы ключ и имя модели), решает она со структурным ответом SupervisorOutput. Иначе работает мок с теми же правилами маршрутизации. С реальной моделью стенд не запускался, поэтому все прогоны в статье идут через мок.

Листинг 2. Распорядитель (app/graph.py)

Python UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def mock_supervisor_reasoning(state: NopikAgentState) -> SupervisorOutput:
    """Детерминированная модель рассуждений для демо-режима и тестов."""
    if state.get("operator_decision") == "rejected":
        return SupervisorOutput(
            next_step="FINISH",
            routing_reason="Оператор отклонил исправление. Завершаем граф без изменений в 1С."
        )

    if not state.get("root_cause_analysis"):
        return SupervisorOutput(
            next_step="agent_auditor",
            routing_reason="Первопричина сбоя неизвестна. Направляем задачу агенту аудита логов 1С."
        )
    
    # Если аудитор отработал, но метаданные еще не сверены
    audit_history = [s.agent_name for s in state.get("audit_trail", [])]
    if "Inspector" not in audit_history:
        return SupervisorOutput(
            next_step="agent_inspector",
            routing_reason="Локализована ошибка в поле СтавкаНДС. Направляем инспектору для проверки перечисления 1С."
        )
        
    # Если первопричина ясна и метаданные проверены, но патча еще нет
    if not state.get("remediation") or not state.get("is_approved_by_human"):
        return SupervisorOutput(
            next_step="agent_remediator",
            routing_reason="Факты собраны. Направляем задачу агенту исправления для синтеза патча и запроса HITL."
        )
        
    return SupervisorOutput(
        next_step="FINISH",
        routing_reason="Пакет исправлений успешно сформирован и подтвержден оператором. Завершение графа."
    )


def supervisor_node(state: NopikAgentState) -> Command[Literal["agent_inspector", "agent_auditor", "agent_remediator", "__end__"]]:
    """Узел супервайзера: оценивает контекст и перенаправляет задачу."""
    llm = get_llm()
    if llm:
        supervisor_structured = llm.with_structured_output(SupervisorOutput)
        prompt = [
            SystemMessage(content=SUPERVISOR_SYSTEM_PROMPT),
            HumanMessage(content=f"Контекст 1С: {state['enterprise_context'].model_dump_json()}\n"
                                 f"Сигнатура: {state.get('identified_error_signature')}\n"
                                 f"Анализ: {state.get('root_cause_analysis')}\n"
                                 f"Патч: {state.get('remediation')}\n"
                                 f"Решение оператора: {state.get('operator_decision')}")
        ] + list(state.get("messages", []))
        decision = supervisor_structured.invoke(prompt)
    else:
        decision = mock_supervisor_reasoning(state)
        
    audit_entry = DecisionStep(
        agent_name="Supervisor",
        action_taken=f"Routing to: {decision.next_step}",
        rationale=decision.routing_reason,
        timestamp_utc=datetime.now(timezone.utc).isoformat()
    )
    
    current_trail = list(state.get("audit_trail", [])) + [audit_entry]
    
    if decision.next_step == "FINISH":
        return Command(
            update={
                "next": "FINISH",
                "audit_trail": current_trail
            },
            goto=END
        )
        
    return Command(
        update={
            "next": decision.next_step,
            "audit_trail": current_trail
        },
        goto=decision.next_step
    )

Возвращая Command, узел сам говорит, куда идти дальше и что записать в состояние. Ребро в графе объявлено только одно, от START к распорядителю, остальные переходы задают команды.

Исполнители в стенде выглядят так. Auditor целиком:

Python UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def agent_auditor_node(state: NopikAgentState) -> Command[Literal["supervisor"]]:
    """Субагент аудита журнала регистрации и технологического журнала 1С."""
    context = state["enterprise_context"]
    error_raw = context.error_payload or "HTTP 422: field 'vat_rate' is required for category 'Electronics'"
    
    detected_signature = "Поле не заполнено: СтавкаНДС при выгрузке в Wildberries API"
    root_cause = "В карточке номенклатуры не указана ставка НДС, модуль обмена падает в исключение"
    
    audit_entry = DecisionStep(
        agent_name="Auditor",
        action_taken="Inspect 1C Event Log & Exception Trace",
        rationale=f"Анализ ошибки '{error_raw}'. Локализована незаполненность реквизита СтавкаНДС.",
        timestamp_utc=datetime.now(timezone.utc).isoformat(),
        tool_output={"detected_signature": detected_signature, "root_cause": root_cause}
    )
    
    msg = AIMessage(content=f"[Auditor]: Установлена первопричина: {root_cause}")
    
    return Command(
        update={
            "messages": [msg],
            "identified_error_signature": detected_signature,
            "root_cause_analysis": root_cause,
            "audit_trail": list(state.get("audit_trail", [])) + [audit_entry]
        },
        goto="supervisor"
    )

Это заглушка: сигнатура и причина записаны в коде, а error_raw попадает только в текст обоснования. Чтение журнала регистрации на месте этой заглушки - задача, которую в стенде никто не решал.

🔍 Пауза на подтверждение: interrupt() и resume

Правило стенда: агенты свободно читают и никогда не пишут. Запись в 1С проходит через решение человека.

Remediator формирует пакет исправления и вызывает interrupt(). Граф останавливается, состояние сохраняется в чекпоинтере, а шлюз выставляет задаче статус INTERRUPTED_AWAITING_APPROVAL вместе с данными паузы (interrupt_data). Решение приходит запросом POST /tasks/{id}/resume и превращается в Command(resume=...).

Листинг 3. Исполнитель с паузой и сборка графа (app/graph.py)

Python UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def agent_remediator_node(state: NopikAgentState) -> Command[Literal["supervisor"]]:
    """Субагент исправления с точкой прерывания Human-in-the-Loop (interrupt)."""
    
    remediation_proposal = RemediationAction(
        action_type="SET_FIELD_VALUE",
        target_object_meta="Справочник.Номенклатура",
        target_object_guid="d0e6c4e0-5b58-11ef-93a8-005056b23d99",
        parameters={
            "Field": "СтавкаНДС", 
            "Value": "НДС20", 
            "Reason": "Автоматическое заполнение ставки НДС по умолчанию для электроники"
        },
        requires_human_approval=True,
        is_approved=False
    )
    
    audit_entry = DecisionStep(
        agent_name="Remediator",
        action_taken="Prepare Remediation Action",
        rationale="Сформирован пакет обновления реквизита карточки номенклатуры в 1С.",
        timestamp_utc=datetime.now(timezone.utc).isoformat(),
        tool_output=remediation_proposal.model_dump()
    )
    
    # КРИТИЧЕСКАЯ ТОЧКА: Прерывание графа для подтверждения человеком
    operator_response = interrupt({
        "question": "Требуется подтверждение применения изменений в 1С",
        "action": remediation_proposal.model_dump(),
        "severity": ExecutionSeverity.WARNING
    })
    
    # Обработка ответа после возобновления Command(resume=...)
    is_approved = bool(operator_response.get("approved", False)) if isinstance(operator_response, dict) else False
    
    if not is_approved:
        reject_reason = operator_response.get("reason", "Отклонено оператором") if isinstance(operator_response, dict) else "Отклонено"
        msg = AIMessage(content=f"[Remediator]: Оператор отклонил план: {reject_reason}")
        return Command(
            update={
                "messages": [msg],
                "is_approved_by_human": False,
                "operator_decision": "rejected",
                "audit_trail": list(state.get("audit_trail", [])) + [audit_entry]
            },
            goto="supervisor"
        )
        
    remediation_proposal.is_approved = True
    msg = AIMessage(content="[Remediator]: Оператор одобрил исправление. Пакет готов к передаче в 1С.")
    
    return Command(
        update={
            "messages": [msg],
            "remediation": remediation_proposal,
            "is_approved_by_human": True,
            "operator_decision": "approved",
            "audit_trail": list(state.get("audit_trail", [])) + [audit_entry]
        },
        goto="supervisor"
    )


def create_nopik_workflow() -> StateGraph:
    """Создает граф оркестратора NOPik."""
    workflow = StateGraph(NopikAgentState)
    
    workflow.add_node("supervisor", supervisor_node)
    workflow.add_node("agent_auditor", agent_auditor_node)
    workflow.add_node("agent_inspector", agent_inspector_node)
    workflow.add_node("agent_remediator", agent_remediator_node)
    
    workflow.add_edge(START, "supervisor")
    
    return workflow
Рис. 3. Жизненный цикл запроса с паузой

Рис. 3. Путь одного инцидента. Числа на стрелках - порядок шагов, статусы взяты из ответов шлюза.

Три свойства interrupt() мы проверили отдельным скриптом (evidence/probe_langgraph.py), потому что от них зависит устройство узла:

1. recursion_limit=3: GraphRecursionError
2. создание RemediationAction в узле: до паузы 1, после resume 2
3. без чекпоинтера: исключения нет; ключи результата: ['__interrupt__', 'audit_trail', 'enterprise_context', 'identified_error_signature', 'is_approved_by_human', 'messages', 'next', 'remediation', 'root_cause_analysis'] | есть __interrupt__: True
   get_state без чекпоинтера: ValueError No checkpointer set
   resume без чекпоинтера: RuntimeError Cannot use Command(resume=...) without checkpointer
4. полный проход (пауза и одобрение) укладывается в recursion_limit=6
  1. Узел выполняется с начала. После resume тело Remediator запускается заново: объект RemediationAction создан дважды, до паузы и после. Всё, что стоит в узле перед interrupt(), должно быть безопасно повторять. Запись в базу или отправка сообщения там недопустимы.
  2. Без чекпоинтера пауза не работает. invoke возвращает результат с ключом __interrupt__, но get_state падает с ValueError, а resume с RuntimeError.
  3. Цикл ограничивает recursion_limit. При лимите 3 граф падает с GraphRecursionError. Полный проход с паузой и одобрением укладывается в 6, в стенде стоит 15.

Кто нажимает «Одобрить». У шлюза стенда нет авторизации. Любой, кто достучится до /resume, сыграет роль человека. Пауза защищает от автономных действий агента, а от злоумышленника в сети защиты нет: для рабочей базы шлюз закрывают аутентификацией и TLS, а решение привязывают к пользователю.

🔍 Адаптер в 1С

Общий модуль НОП_АгентИнтеграция (серверный, с флагами «Вызов сервера» и «Внешнее соединение») делает четыре вещи: запускает задачу, опрашивает статус, передаёт решение оператора и применяет одобренный пакет.

Листинг 4. Общий модуль НОП_АгентИнтеграция

BSL UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Общий модуль: НОП_АгентИнтеграция
// Назначение: Серверный адаптер взаимодействия с LangGraph Supervisor Gateway
// Свойства модуля: Сервер, Внешнее соединение, Вызов сервера (ServerCall = True)

#Область ПрограммныйИнтерфейс

// Запускает анализ и исправление инцидента через внешний супервайзер LangGraph
//
// Параметры:
//  КонтекстИнцидента - Структура:
//      * ИдентификаторБазы - Строка - Уникальное имя базы в кластере
//      * СигнатураОшибки   - Строка - Текст ошибки или сырой лог сбоя
//      * МетаданныеОбъекта - Строка - Имя метаданных (например, Справочник.Номенклатура)
//      * СсылкаОбъекта     - ЛюбаяСсылка - Ссылка на сбойный объект в 1С
//  КлючИдемпотентности - Строка - Необязательный. Повторный вызов с тем же ключом не запускает вторую задачу;
//      для повторов из своего кода ключ строят из инцидента (база, объект, ошибка) и передают сюда
//
// Возвращаемое значение:
//  Строка - GUID запущенной задачи (Task GUID)
//
Функция ЗапуститьАнализИнцидента(КонтекстИнцидента, КлючИдемпотентности = Неопределено) Экспорт
    
    Если КлючИдемпотентности = Неопределено Тогда
        КлючИдемпотентности = Строка(Новый УникальныйИдентификатор);
    КонецЕсли;
    
    СтруктураЗапроса = Новый Структура;
    СтруктураЗапроса.Вставить("infobase_id", КонтекстИнцидента.ИдентификаторБазы);
    СтруктураЗапроса.Вставить("task_guid", КлючИдемпотентности);
    СтруктураЗапроса.Вставить("user_name", ПользователиИнформационнойБазы.ТекущийПользователь().Имя);
    СтруктураЗапроса.Вставить("idempotency_key", КлючИдемпотентности);
    СтруктураЗапроса.Вставить("subsystem", "ИнтеграцияМаркетплейсы");
    СтруктураЗапроса.Вставить("error_payload", КонтекстИнцидента.СигнатураОшибки);
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, СтруктураЗапроса);
    ТелоJSON = ЗаписьJSON.Закрыть();
    
    // Получаем безопасное соединение
    HTTPСоединение = ПолучитьСоединениеСоШлюзом();
    
    HTTPЗапрос = Новый HTTPЗапрос("/api/v1/supervisor/tasks");
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
    HTTPЗапрос.Заголовки.Вставить("X-Idempotency-Key", КлючИдемпотентности);
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоJSON, КодировкаТекста.UTF8);
    
    Попытка
        HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
        Если HTTPОтвет.КодСостояния <> 202 Тогда
            ВызватьИсключение СтрШаблон("Ошибка запуска супервайзера: HTTP %1 - %2", 
                HTTPОтвет.КодСостояния, HTTPОтвет.ПолучитьТелоКакСтроку());
        КонецЕсли;
    Исключение
        ЗаписьЖурналаРегистрации("NOPik.Supervisor", УровеньЖурналаРегистрации.Ошибка,,, 
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение;
    КонецПопытки;
    
    Возврат КлючИдемпотентности;
КонецФункции

// Опрашивает шлюз о текущем статусе исполнения задачи (Polling)
//
// Параметры:
//  TaskGUID - Строка - Идентификатор задачи
//
// Возвращаемое значение:
//  Структура - Текущее состояние задачи, статус, пакет исправлений
//
Функция ПолучитьСтатусЗадачи(TaskGUID) Экспорт
    
    HTTPСоединение = ПолучитьСоединениеСоШлюзом();
    HTTPЗапрос = Новый HTTPЗапрос(СтрШаблон("/api/v1/supervisor/tasks/%1", TaskGUID));
    
    HTTPОтвет = HTTPСоединение.Получить(HTTPЗапрос);
    Если HTTPОтвет.КодСостояния <> 200 Тогда
        ВызватьИсключение СтрШаблон("Ошибка опроса статуса: HTTP %1", HTTPОтвет.КодСостояния);
    КонецЕсли;
    
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(HTTPОтвет.ПолучитьТелоКакСтроку());
    Результат = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    Возврат Результат;
КонецФункции

// Передаёт решение оператора шлюзу: граф LangGraph продолжит работу с точки interrupt()
//
// Параметры:
//  TaskGUID - Строка - Идентификатор задачи
//  Одобрено - Булево - Решение оператора
//  Причина  - Строка - Комментарий оператора
//
Процедура ОтправитьРешениеОператора(TaskGUID, Одобрено, Причина = "") Экспорт
    
    Тело = Новый Структура("approved, reason", Одобрено, Причина);
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, Тело);
    ТелоJSON = ЗаписьJSON.Закрыть();
    
    HTTPЗапрос = Новый HTTPЗапрос(СтрШаблон("/api/v1/supervisor/tasks/%1/resume", TaskGUID));
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
    HTTPЗапрос.УстановитьТелоИзСтроки(ТелоJSON, КодировкаТекста.UTF8);
    
    HTTPОтвет = ПолучитьСоединениеСоШлюзом().ОтправитьДляОбработки(HTTPЗапрос);
    Если HTTPОтвет.КодСостояния <> 200 Тогда
        ВызватьИсключение СтрШаблон("Ошибка передачи решения: HTTP %1", HTTPОтвет.КодСостояния);
    КонецЕсли;
    
КонецПроцедуры

// Применяет согласованный пакет исправлений в локальной базе 1С
// с гарантией транзакционной целостности и аудита
//
// Параметры:
//  ПакетИсправлений - Структура - Десериализованный JSON плана исправления
//
Процедура ПрименитьПакетИсправлений(ПакетИсправлений) Экспорт
    
    // Проверка защитного барьера Human-in-the-Loop
    Если ПакетИсправлений.requires_human_approval И Не ПакетИсправлений.is_approved Тогда
        ВызватьИсключение "Попытка несанкционированного применения изменений без подтверждения HITL!";
    КонецЕсли;
    
    НачатьТранзакцию();
    Попытка
        Если ПакетИсправлений.action_type = "SET_FIELD_VALUE" Тогда
            
            Ссылка = ПолучитьСсылкуПоGUID(ПакетИсправлений.target_object_meta, ПакетИсправлений.target_object_guid);
            Объект = Ссылка.ПолучитьОбъект();
            
            Поле = ПакетИсправлений.parameters.Field;
            Значение = ПреобразоватьСтрокуВТип1С(ПакетИсправлений.parameters.Value, ПакетИсправлений.target_object_meta, Поле);
            
            Объект[Поле] = Значение;
            Объект.Записать();
            
            ЗаписьЖурналаРегистрации("NOPik.Remediation", УровеньЖурналаРегистрации.Информация,,,
                СтрШаблон("Агент NOPik успешно обновил реквизит %1 у объекта %2", Поле, Ссылка));
        Иначе
            ВызватьИсключение "Неподдерживаемый тип действия: " + ПакетИсправлений.action_type;
        КонецЕсли;
        
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("NOPik.Remediation", УровеньЖурналаРегистрации.Ошибка,,,
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение;
    КонецПопытки;
    
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция ПолучитьСоединениеСоШлюзом()
    // Параметры стенда. В рабочей базе - константа или регистр сведений, а перед шлюзом - обратный прокси с TLS
    Хост = "127.0.0.1";
    Порт = 8000;
    Таймаут = 15;
    
    Возврат Новый HTTPСоединение(Хост, Порт,,,, Таймаут);
КонецФункции

Функция ПолучитьСсылкуПоGUID(ИмяМетаданных, GUIDСтрока)
    УИД = Новый УникальныйИдентификатор(GUIDСтрока);
    Части = СтрРазделить(ИмяМетаданных, ".");
    Если Части[0] <> "Справочник" Тогда
        ВызватьИсключение "Стенд поддерживает только справочники: " + ИмяМетаданных;
    КонецЕсли;
    
    Возврат Справочники[Части[1]].ПолучитьСсылку(УИД);
КонецФункции

Функция ПреобразоватьСтрокуВТип1С(ЗначениеСтрока, ИмяМетаданных, ИмяРеквизита)
    Если ИмяРеквизита = "СтавкаНДС" Тогда
        Возврат Перечисления.СтавкиНДС[ЗначениеСтрока];
    КонецЕсли;
    Возврат ЗначениеСтрока;
КонецФункции

#КонецОбласти

Барьер в ПрименитьПакетИсправлений работает на стороне 1С и не зависит от шлюза: пакет без is_approved не применится, даже если шлюз отдаст его без одобрения.

Модуль я прогнал через проверку модулей Конфигуратора платформы 8.3.27.2130 (/CheckModules для тонкого клиента, сервера и внешнего соединения). Исходная редакция модуля не прошла её:

{Общий модуль.NOP_AgentIntegration.Модуль(146,12)}: Ожидается выражение
    Возврат<<?>> [ТипОбъекта][ИмяОбъекта].ПолучитьСсылку(УИД);

Конструкции [ТипОбъекта][ИмяОбъекта] в языке нет. Функция ПолучитьСсылкуПоGUID теперь обращается к менеджеру справочников: Справочники[Части[1]]. Стенд поддерживает только справочники и говорит об этом исключением.

Исправлено ещё три места:

Было Стало
соединение через ЗащищенноеСоединениеOpenSSL к шлюзу, который слушает обычный HTTP соединение без TLS для стенда; для рабочей базы TLS ставят перед шлюзом
условие барьера Не requires_human_approval Или Не is_approved бросало исключение при любом пакете, где подтверждение не требуется requires_human_approval И Не is_approved
ключ идемпотентности всегда новый: повтор из своего кода запускал вторую задачу необязательный параметр КлючИдемпотентности; для повторов ключ строят из инцидента

Адаптер вызывается напрямую. В базе на БСП вызов оформляют длительной операцией (ДлительныеОперации), чтобы не держать сеанс. Этот вариант в стенде не проверялся.

🔍 Сквозной прогон: 1С, шлюз, граф, запись в карточку

Проверочная база создана скриптом evidence/1c-check/build_ib.py: справочник Номенклатура с реквизитом СтавкаНДС, перечисление СтавкиНДС, модуль адаптера. Скрипт e2e.ps1 подключается к ней через внешнее соединение и проходит весь путь при запущенном шлюзе. В стенде Remediator всегда указывает один и тот же GUID карточки, поэтому скрипт заводит карточку Арт-10492 с этим GUID и пустой ставкой.

карточка Арт-10492 создана, ставка НДС: не заполнена
t=35 мс: задача запущена (c24f2a15-545a-4182-8532-6f1501a9b357)
t=47 мс: опрос 1 -> INTERRUPTED_AWAITING_APPROVAL
применение без подтверждения оператора: Попытка несанкционированного применения изменений без подтверждения HITL!
t=73 мс: решение оператора отправлено
t=79 мс: опрос 1 -> RESUMING
t=193 мс: опрос 2 -> COMPLETED
применение пакета: []
t=219 мс: пакет применён в транзакции
ставка НДС в карточке после применения: НДС20
два вызова с одним ключом вернули один идентификатор: True (dup-key-1)

Построчно:

  • карточка создана без ставки;
  • задача запущена, граф дошёл до паузы (INTERRUPTED_AWAITING_APPROVAL) с первого опроса;
  • попытка применить пакет без решения оператора отклонена барьером;
  • после ОтправитьРешениеОператора шлюз ещё держит статус RESUMING, пока граф доходит до конца, следующий опрос видит COMPLETED; пакет применён в транзакции, у карточки стоит НДС20;
  • два вызова с одним ключом вернули один идентификатор (то, что шлюз отвечает на такой повтор как на дубль, проверяет тест шлюза).
Рис. 4. Замер того же прогона

Рис. 4. Время от начала прогона до каждого события.

В двух сохранённых прогонах (e2e-run1.txt, e2e-run2.txt) весь путь занял 307 и 219 миллисекунд, в четырёх более ранних - от 114 до 197 (их вывод не сохранён). Это накладные расходы стенда: заглушки отвечают сразу, а время человека и модели в замер не входит. Сколько будет длиться разбор с моделью и сколько человек будет решать, стенд не показывает.

Сценарий отказа проверен тестом. Оператор отвечает approved: false, граф завершается, remediation остаётся пустым, повторного вопроса нет.

🔍 Тесты

Тестов три, обходятся без 1С и сети:

  • test_supervisor_full_cycle_with_hitl - полный цикл графа: пауза, Command(resume=...), завершение, проверка аудиторского следа;
  • test_remediation_rejected_by_operator - отказ: граф завершается, пакета нет;
  • test_api_end_to_end_gateway_flow - шлюз целиком: 202, повтор с тем же ключом возвращает DUPLICATE_ACCEPTED, пауза, resume, статус COMPLETED.

Тест отказа из стенда (после исправления он проверяет, что граф не возвращается к вопросу):

Python UTF-8 Открыть файл
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def test_remediation_rejected_by_operator(initial_test_state):
    """Проверка ветки, когда оператор отклоняет исправление."""
    workflow = create_nopik_workflow()
    checkpointer = MemorySaver()
    app = workflow.compile(checkpointer=checkpointer)
    
    config = {"configurable": {"thread_id": "test-thread-rejected"}}
    
    # Доходим до прерывания
    app.invoke(initial_test_state, config=config)
    
    # Оператор отклоняет
    app.invoke(
        Command(resume={"approved": False, "reason": "Неверная карточка"}),
        config=config
    )
    
    state_final = app.get_state(config)
    assert state_final.values["is_approved_by_human"] is False
    assert state_final.values["remediation"] is None
    assert len(state_final.next) == 0, "После отказа граф завершается и не спрашивает оператора повторно"
tests/test_api_gateway.py::test_api_end_to_end_gateway_flow PASSED       [ 33%]
tests/test_supervisor.py::test_supervisor_full_cycle_with_hitl PASSED    [ 66%]
tests/test_supervisor.py::test_remediation_rejected_by_operator PASSED   [100%]

============================== 3 passed in 1.42s ==============================

Первая версия этого теста проверяла только is_approved_by_human is False и проходила, хотя граф после отказа снова вставал на паузу. Проверка not state_final.next эту ошибку ловит.

Тестов для модуля 1С в статье нет. Их удобно писать на YAxUnit, но фреймворк на стенде не стоял, и выдавать непроверенный набор за тесты я не стал. Проверка адаптера на платформе - скрипт e2e.ps1 выше.

🔍 Что стенд не умеет

  • Рабочие инструменты агентов. Чтение журнала регистрации и техжурнала, запрос метаданных, разбор блокировок: везде заглушки.
  • Модель вместо мока. Ветка с ChatOpenAI и with_structured_output написана, но не запускалась.
  • Постоянный чекпоинтер. MemorySaver теряет состояние вместе с процессом, зависшие паузы пропадут при перезапуске. Для рабочей базы нужен постоянный чекпоинтер, например PostgresSaver из пакета langgraph-checkpoint-postgres. В стенде его нет, docker-compose.yml PostgreSQL не поднимает.
  • Идемпотентность в базе. Ключи лежат в словаре в памяти.
  • Защита шлюза. Нет аутентификации и TLS, /resume открыт всем.
  • Уведомление оператора. Стенд не отправляет карточку в Telegram или на форму 1С, оператор сам читает interrupt_data из статуса.
  • Ограничение нагрузки на шлюз. Circuit breaker не реализован.
  • Опрос из БСП. Адаптер вызывается напрямую, длительные операции не подключены.

🔍 Как повторить

Стенд (Python 3.12, около трёх минут вместе с установкой):

Bash UTF-8 CLI
1
2
3
4
5
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
pytest tests -v
uvicorn app.main:app --port 8000

В соседнем терминале консольный эмулятор 1С пройдёт тот же путь с паузой и одобрением:

Bash UTF-8 CLI
1
python -m app.mock_1c_service

Проверка на платформе 1С: python evidence/1c-check/build_ib.py stand/1c/NOP_AgentIntegration.bsl создаёт файловую базу и проверяет модули, затем e2e.ps1 запускается 32-битным PowerShell (платформа x86) при работающем шлюзе.

Если что-то не заводится, напишите в комментариях версию Python и платформы и на каком шаге остановились.

🔍 Итоги и что дальше

Стенд показывает три вещи: 1С не ждёт ответа графа, запись в базу отделена от рассуждений агента паузой, а барьер в модуле 1С работает независимо от шлюза. На пути к рабочей базе остаются рабочие инструменты агентов, постоянный чекпоинтер и защита шлюза.

В части 4 мы заменим заглушки инструментами: агент сам обратится к метаданным и коду 1С через Model Context Protocol. Какая часть задуманного заработает, покажем замером, как здесь.

Вопросы к вам: где в вашей базе граница между «агент читает» и «агент пишет»? Какие действия вы бы поставили за подтверждение в первую очередь?

Вступайте в нашу телеграмм-группу Инфостарт

LangGraph 1С:Предприятие 8.3 ИИ-агент Python FastAPI Human-in-the-Loop interrupt HTTP-сервис внешнее соединение идемпотентность Pydantic супервайзер мультиагентная система автономный агент интеграция 1С

См. также

SALE! %

Банковские операции Обмен с интернет-банком Мастера заполнения Нейросети Разработчик Бухгалтер Пользователь 1С:Предприятие 8 1C:ERP 1С:Бухгалтерия 3.0 1С:ERP Управление предприятием 2 1С:Управление холдингом 1С:ERP. Управление холдингом 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Управление торговлей 11 1С:Розница 3.0 Платные (руб)

Корректируйте банковские документы быстро и легко! Создайте правило обработки — и оно автоматически применится при загрузке выписки (отбор по любому реквизиту или регулярному выражению). Решение заполняет расшифровку платежа, комиссию эквайринга, подбирает ведомости на выплату зарплаты, помечает дубли из банка на удаление и многое другое. Доплачивать за алгоритмы не нужно — они включены в решение. Обработка работает при загрузке из файлов клиент-банка и через DirectBank. Новое — искусственный интеллект: модель приводит нестандартные назначения платежа к виду, понятному алгоритмам, а ИИ-ассистент прямо в 1С консультирует по решению и разбирает код правил и алгоритмов. Поддерживаются локальные и облачные OpenAI-совместимые модели — данные могут не покидать ваш контур.

17500 руб.

20.12.2024    19887    104    32    

85

Нейросети Системный администратор Разработчик Аналитик Бухгалтер Пользователь Руководитель проекта 1С 8.3 1С:Документооборот 1С:Бухгалтерия 3.0 1С:Зарплата и Управление Персоналом 3.x Россия Платные (руб)

Задавайте вопросы базе 1С обычными словами: получайте данные, находите ошибки и связанные документы, проверяйте права, работайте с вложениями и контролируемо вносите изменения. Всё это работает в самой программе, а Codex и Claude подключаются по желанию.

16990 руб.

30.07.2026    13948    29    4    

28

Инструментарий разработчика Нейросети Платные (руб)

Первые попытки разработки на 1С с использованием больших языковых моделей (LLM) могут разочаровать. LLMки сильно галлюцинируют, потому что не знают устройства конфигураций 1С, не знают нюансов синтаксиса. Но если дать им подсказки с помощью MCP, то результат получается кардинально лучше. Далее в публикации: MCP для поиска по метаданным 1С, справке синтакс-помощника и проверки синтаксиса.

15250 руб.

25.08.2025    71956    143    41    

151

Распознавание документов и образов Нейросети 1С:Предприятие 8 1С 8.3 1С 8.5 1C:Бухгалтерия 1С:Зарплата и Управление Персоналом 3.x Беларусь Россия Казахстан Армения Платные (руб)

ИИ-сканер документов с REST API для интеграции с 1С и корпоративными системами. Извлекайте данные из счетов, паспортов, дипломов, патентов и трудовых книжек за секунды. Точность человека - скорость машины. Приложение поддерживает восемь типов документов, четырех провайдеров ИИ (имеется возможность использования локальных ИИ), локальный REST API и экспорт в JSON. Важно! модель должна поддерживать функцию Vision (распознавание файлов и картинок). Запускайте используя локальные ИИ, без подписок и ограничений

6100 руб.

24.08.2026    659    3    0    

1

Нейросети Разработчик 1С 8.3 Бесплатно (free)

В первой статье цикла граф состояний NOPik был намеренно упрощен до линейной цепочки: analyze_intent -> plan_executor -> deliver_to_1c. Такая цепочка наглядно демонстрирует переход от одного гигантского промпта к формальному FSM, но у нее есть системный недостаток: один узел plan_executor отвечает сразу за все прикладные сценарии - и построение отчета, и диагностику ошибки, и общий ответ. По мере роста числа сценариев этот узел неизбежно превращается в новую версию того самого «монолитного промпта», от которого мы уходили. Решение - декомпозиция по ролям с явным супервайзером, а не наращивание ветвлений внутри одного узла: та же логика, что в микросервисной архитектуре - каждая роль отвечает за одну зону ответственности и не обязана знать о существовании соседних ролей.

22.09.2026    443    Ninel_S    0    

1

WEB-интеграция Нейросети Мессенджеры и боты Разработчик 1С 8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление холдингом Бесплатно (free)

Представленные материалы открывают цикл статей о создании автономного ИИ-агента NOPik для экосистемы «1С:Предприятие», предлагая переход от примитивных чат-форм к сложной агентной архитектуре. Автор критикует «наивный» подход к интеграции LLM из-за его ненадежности, галлюцинаций моделей и блокировки пользовательских транзакций. Взамен предлагается концепция Zero-Footprint Core, где логика рассуждений вынесена за пределы учетной системы, а взаимодействие строится на базе конечных автоматов (FSM/HSM). Особое внимание уделено паттерну «Адаптер», который позволяет использовать ИИ уже сегодня через команды форм, сохраняя готовность к будущим обновлениям платформы. Источники также содержат практические рекомендации по DevOps-инструментам, таким как ibcmd и Git, для ускорения разработки интеллектуальных систем. В конечном итоге проект NOPik служит дорожной картой для внедрения мультиагентных систем в строгий корпоративный контур.

21.09.2026    525    Ninel_S    0    

1

Нейросети 1С:Управление торговлей 11 Бесплатно (free)

Я не считаю покупку специализированных платных инструментов обязательной для разработки с ИИ: нужную обвязку тоже можно создать с агентом. Показываю этот подход на расширении УТ 11 с динамическим списком остатков. Одно задание Codex, 37 минут до проверки, работающая форма. Рассказываю, как устроено окружение, почему первую попытку пришлось переснять и что получилось в повторном прогоне.

17.09.2026    6438    112    Ibrogim    63    

22

Инструментарий разработчика Нейросети Разработчик 1С:Предприятие 8 Бесплатно (free)

Как мы в 42Clouds научили агента открывать формы, заполнять поля, проверять списки и собирать доказательные отчёты — и чем Answer42 отличается от Vanessa Automation MCP, Тестер, CodexTestBridge UI-worker и браузерных инструментов.

04.09.2026    2501    amoarok    17    

18
Для отправки сообщения требуется регистрация/авторизация