Приветственные письма для сорока новых клиентов, сертификат для каждого участника, счёт‑фактура для каждого заказа: один и тот же документ, каждый раз другая запись. С работающим локально GroupDocs.Assembly.Mcp (dnx GroupDocs.Assembly.Mcp --yes) Claude Desktop, Cursor или Copilot делают это одним вызовом инструмента assemble_per_record, который заполняет ваш шаблон Word один раз для каждого элемента массива JSON, сохраняет каждый результат как отдельный PDF с именем, выбранным из поля, и сообщает о записях, которые завершились ошибкой, вместо того чтобы остановиться на первой:
Сделайте PDF‑приветственное письмо для каждого клиента в customers.json, используя welcome-letter.docx, и назовите каждый файл по имени клиента.
Пошаговая версия с конфигурацией и устранением неполадок находится в документации: Как сгенерировать один документ на запись (mail merge) с помощью AI‑агента.
Почему не позволить агенту выполнять цикл?
Он может, и для пяти записей это нормально. Но цикл из сорока вызовов инструмента — это сорок шансов, что агент потеряет счёт, пропустит запись, когда контекст станет длинным, или «добросовестно» переформулирует одно письмо. Инструмент «по записи» переносит цикл на сервер: один вызов, один шаблон, один проход по массиву и отчёт, в котором точно указано, сколько файлов было записано и какие записи не прошли. Задача агента — выбрать шаблон, файл данных и поле для именования, а затем прочитать вам отчёт.
Что агент проверяет в первую очередь?
Две вещи: как выглядят данные и что читает шаблон. В нашем тесте — пять клиентов в customers.json и одностраничное приветственное письмо:
-> get_document_info {"file":{"filePath":"customers.json"}}
<- { "dataKind": "json",
"data": { "root": "array", "length": 5, "keys": null } }
-> get_template_fields {"template":{"filePath":"welcome-letter.docx"}}
<- { "dataSourceNames": [ "customer" ],
"fields": [ "customer.Contact", "customer.Plan", "customer.Name",
"customer.Manager", "customer.FirstInvoice" ] }
Шаблон называет одну запись customer; сервер определяет это сам, поэтому агенту не нужно передавать dataSourceName.
Один вызов, пять записей, одна ошибка
-> assemble_per_record {"template":{"filePath":"welcome-letter.docx"},
"data":{"filePath":"customers.json"},"fileNameField":"Name",
"format":"pdf"}
<- Assembled 4 of 5 document(s) from 'welcome-letter.docx' with
'customers.json', one per record (each record as 'customer'), as PDF.
Saved to:
.../Contoso Pharma.pdf
.../Fabrikam Logistics.pdf
.../Tailspin Toys.pdf
.../Litware Labs.pdf
1 record(s) failed:
record 4: InvalidOperationException: ... Can not get the value of
member 'FirstInvoice' on type 'System.Data.DataRow'. The template
reads 'FirstInvoice', which the data does not have. ...
Запись 4, Wingtip Travel, не имела даты первой счёт‑фактуры в данных. Остальные четыре письма были созданы, каждое названо по имени клиента, а ошибка вернулась с номером записи и полем, которого не хватало. Агент сообщает об этом как «четыре письма готовы; у Wingtip Travel нет даты первой счёт‑фактуры, оставить строку пустой или подождать дату?». Именно такой диалог нужен, а не сорок файлов без понимания, какой из них ошибочен.
Как работает «fan‑out»
customers.json [ {Name:"Contoso Pharma",...}, {Name:"Fabrikam ..."},
... 5 records ]
|
assemble_per_record {"template":{"filePath":"welcome-letter.docx"},
"data":{"filePath":"customers.json"},
"fileNameField":"Name","format":"pdf"}
|
+--> Contoso Pharma.pdf
+--> Fabrikam Logistics.pdf ... 2 more
'--> "4 of 5 ... 1 record(s) failed: record 4 ..."
Три параметра делают работу. fileNameField задаёт имя каждому выходному файлу по полю записи, например customer.Name или номеру счёт‑фактуры; без него файлы получают порядковый номер. recordsPath указывает на массив, когда JSON‑объект содержит его, например "customers" или "batch.items". А maxDocuments — это ограничитель.
Что останавливает «бегущий» пакет?
maxDocuments, по умолчанию 100 и может достигать 1000. Прежде чем что‑то записать, сервер считает записи; если их больше лимита, он отказывает. Мы установили его в 3 против наших пяти клиентов:
-> assemble_per_record {..., "maxDocuments":3}
<- Per-record assembly failed for 'welcome-letter.docx': there are 5
records, more than maxDocuments=3. Raise 'maxDocuments' (up to 1000)
or pass fewer records.
Ничего не записано. Для рассылки реальным людям такой дефолт правильный: случайно указав весь экспорт CRM, вы получите вопрос, а не десять тысяч PDF‑ов.
Мои данные — таблица, а не JSON
Генерация «по записи» читает только JSON: массив объектов или объект, содержащий массив. Когда список находится в CSV или таблице, агент сначала преобразует строки в JSON, что агенты делают хорошо, и записывает файл в папку хранилища. Если вам нужен один документ, в котором перечислены все строки (прайс‑лист, реестр), используйте assemble_document с <<foreach>> в шаблоне — он читает CSV и таблицы напрямую.
Честные ограничения
- Генерация «по записи» читает только JSON; данные CSV и таблиц требуют предварительного преобразования.
- Режим оценки: каждый сгенерированный файл начинается с «Evaluation Only. Created with GroupDocs.Assembly 26.7…», а шаблоны e‑mail сохраняют только их текстовое тело. Лицензируйте сервер перед реальной рассылкой.
- Отсутствующее поле приводит к ошибке этой записи.
allowMissingMembers: trueзаписывает её с пустой строкой; решите, что вам нужно. - Даты: записывайте их как
2026-12-01T00:00:00и форматируйте в шаблоне, как в нашем письме с<<[customer.FirstInvoice]:"d MMMM yyyy">>. В нашем тесте строка даты без времени сместилась на один день на машине UTC+2. - Сервер записывает файлы; он их не отправляет. Доставка — отдельный шаг.
FAQ
Можно ли выполнить mail‑merge с Claude без загрузки списка клиентов?
Да. Сервер работает локально и читает customers.json из вашей папки хранилища; сгенерированные PDF‑ы также записываются туда.
Как именуются выходные файлы?
По полю записи, которое вы передаёте как fileNameField, здесь — имя клиента, например Contoso Pharma.pdf. Без этого используется порядковый номер.
Можно ли сделать каждый документ e‑mail‑ом вместо PDF?
Да: используйте шаблон EML или MSG, либо передайте format: "eml". В режиме оценки тело HTML‑письма заменяется строкой оценки, поэтому тестируйте e‑mail‑вывод с лицензией.
Углубиться
- Документация, канонический how‑to: Как сгенерировать один документ на запись (mail merge) с помощью AI‑агента
- Центр документации: GroupDocs.Assembly MCP Server
- Начните здесь: Позвольте модели заполнить данные, а не писать документ
- Связано: Что нужно этому шаблону? Спросите у него.
- Связано: Диаграмма берётся из шаблона, цифры — из данных
- Связано: Ваш репозиторий уже содержит данные; позвольте агенту создать PDF
- On‑premise и модель безопасности: 3 архитектуры для AI‑обработки документов, и та, что держит файлы внутри вашей сети
- Вопросы: GroupDocs Assembly forum
- Исходный код: GroupDocs.Assembly.Mcp on GitHub