{"id":488919,"date":"2026-07-28T16:35:32","date_gmt":"2026-07-28T16:35:32","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=488919"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=488919","title":{"rendered":"Shorts Maker v2: \u043a\u0430\u043a \u044f \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043b AI-\u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0432\u0438\u0434\u0435\u043e \u0432 \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c\u044b\u0439 production pipeline \u043d\u0430 Python"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440!<\/p>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0437\u0430\u0434 \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043f\u0435\u0440\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u0432\u0438\u0434\u0435\u043e \u0434\u043b\u044f Shorts, Reels \u0438 TikTok. \u041d\u0430 \u0432\u0445\u043e\u0434 \u043f\u043e\u0434\u0430\u0432\u0430\u043b\u0430\u0441\u044c \u0442\u0435\u043c\u0430 \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u00ab\u0421\u044e\u0436\u0435\u0442 \u201c1984\u201d \u0437\u0430 \u043e\u0434\u043d\u0443 \u043c\u0438\u043d\u0443\u0442\u0443\u00bb, \u2014 \u0430 \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0441\u044f \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u043e\u043b\u0438\u043a \u0441\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043c, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438, \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0435\u0439, \u043e\u0437\u0432\u0443\u0447\u043a\u043e\u0439 \u0438 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u0430\u043c\u0438.<\/p>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0434\u043e\u043a\u0430\u0437\u0430\u043b\u0430, \u0447\u0442\u043e \u0438\u0434\u0435\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041d\u043e \u043e\u043d\u0430 \u0436\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0430, \u0447\u0442\u043e <strong>\u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u043e\u043b\u0438\u043a \u043e\u0434\u0438\u043d \u0440\u0430\u0437<\/strong> \u0438 <strong>\u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0439 production pipeline<\/strong> \u2014 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u0430\u0434\u0430\u043b\u0430 \u043d\u0430 \u0432\u043e\u0441\u044c\u043c\u043e\u0439 \u0441\u0446\u0435\u043d\u0435 \u0438\u0437 \u0434\u0435\u0441\u044f\u0442\u0438, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u043c\u043e\u0433 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u0443\u0436\u0435 \u043e\u043f\u043b\u0430\u0447\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u043b\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435\u043c \u0444\u0430\u0439\u043b\u0430 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u0431\u044b\u043b\u043e \u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0446\u0435\u043d\u0443 \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0438\u043b\u0438 \u043d\u0435\u0442. \u0410 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 <code>asyncio.gather()<\/code> \u0434\u0430\u0432\u0430\u043b \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c, \u043d\u043e \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u043b \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0441 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c?<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 v2 \u044f \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043b \u043f\u043e\u0447\u0442\u0438 \u0441 \u043d\u0443\u043b\u044f. Clean Architecture \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c, \u043d\u043e \u0432\u043e\u043a\u0440\u0443\u0433 \u043d\u0435\u0451 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c DDD, \u0430\u0433\u0440\u0435\u0433\u0430\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u044f\u0432\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439, \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0435 checkpoint\u2019\u044b, content-addressed \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u043e\u0441\u043b\u0435 \u0441\u0431\u043e\u044f.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0443 \u043d\u0435 \u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043c\u043e\u0434\u0435\u043b\u0438, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0434\u0438\u043d API-\u0432\u044b\u0437\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0434\u043b\u0438\u0442\u044c\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442 \u0438 \u0441\u0442\u043e\u0438\u0442\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0435\u043d\u0435\u0433.<\/p>\n<h3>\u0427\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0435 \u0442\u0430\u043a \u0441 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439<\/h3>\n<p>\u0412 v1 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">script = await screenwriter.generate(topic)script = await art_director.enhance(script)script = await motion_director.enhance(script)await asyncio.gather(    generate_images(script),    generate_speech(script),)await generate_videos(script)return await editor.render(script)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0430 \u044d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043a\u043e\u0434: \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043d\u044f\u0442\u043d\u0430, \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441\u043a\u0440\u044b\u0442\u044b \u0437\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u043c\u0438, \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e.<\/p>\n<p>\u041d\u043e \u043f\u043e \u043c\u0435\u0440\u0435 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0440\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.<\/p>\n<h4>\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430<\/h4>\n<p>\u041f\u043e\u043a\u0430 Python-\u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0436\u0438\u0432, \u043e\u043d \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u0438\u0435 \u0441\u0446\u0435\u043d\u044b \u0433\u043e\u0442\u043e\u0432\u044b. \u041f\u043e\u0441\u043b\u0435 <code>Ctrl+C<\/code>, \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438\u043b\u0438 \u043f\u0430\u0434\u0435\u043d\u0438\u044f \u043c\u0430\u0448\u0438\u043d\u044b \u044d\u0442\u043e \u0437\u043d\u0430\u043d\u0438\u0435 \u0442\u0435\u0440\u044f\u0435\u0442\u0441\u044f.<\/p>\n<h4>\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043d\u0435 \u0431\u044b\u043b \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c<\/h4>\n<p>\u0420\u0435\u0442\u0440\u0430\u0439 \u0432\u0441\u0435\u0433\u043e \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u043c\u043e\u0433 \u0441\u043d\u043e\u0432\u0430 \u0432\u044b\u0437\u0432\u0430\u0442\u044c LLM, \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438\u043b\u0438 image-to-video API. \u0414\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432. \u0414\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u2014 \u043d\u043e\u0432\u044b\u0435 \u043c\u0438\u043d\u0443\u0442\u044b \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f, \u043d\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438 \u043d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0432 \u0431\u0438\u043b\u043b\u0438\u043d\u0433\u0435.<\/p>\n<h4>gather() \u0441\u043a\u0440\u044b\u0432\u0430\u043b \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0443\u0441\u043f\u0435\u0445<\/h4>\n<p>\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0438\u0437 \u0434\u0435\u0441\u044f\u0442\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0434\u0435\u0432\u044f\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0438\u0441\u044c, \u0430 \u043e\u0434\u043d\u043e \u0443\u043f\u0430\u043b\u043e \u043f\u043e timeout. \u0411\u0438\u0437\u043d\u0435\u0441-\u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0435 \u0440\u0430\u0432\u043d\u043e \u043d\u0438 \u00ab\u0443\u0441\u043f\u0435\u0445\u00bb, \u043d\u0438 \u00ab\u043e\u0448\u0438\u0431\u043a\u0430\u00bb. \u042d\u0442\u043e <strong>\u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0434\u0438\u044f<\/strong>, \u0438 \u0435\u0451 \u043d\u0443\u0436\u043d\u043e \u0443\u043c\u0435\u0442\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c.<\/p>\n<h4>Pydantic-\u043c\u043e\u0434\u0435\u043b\u0438 \u0441\u0442\u0430\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0440\u043e\u043b\u0435\u0439<\/h4>\n<p>\u041e\u0434\u043d\u0438 \u0438 \u0442\u0435 \u0436\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043a\u0430\u043a \u043e\u0442\u0432\u0435\u0442\u044b API, \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430 \u0434\u0438\u0441\u043a\u0435. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 JSON \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u043b\u043e \u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443.<\/p>\n<p>\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u043f\u043e\u0441\u043b\u0435 v1 \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0442\u0430\u043a\u0438\u043c: \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u044b. \u041c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438 \u0441\u0430\u043c\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043e\u043b\u0433\u043e\u0436\u0438\u0432\u0443\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. \u0417\u043d\u0430\u0447\u0438\u0442, \u043e\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0447\u0430\u0441\u0442\u044c\u044e \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043d\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438.<\/p>\n<h3>\u0427\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 v2<\/h3>\n<p>Shorts Maker v2 \u2014 \u044d\u0442\u043e \u043e\u0434\u0438\u043d bounded context: <strong>\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e<\/strong>.<\/p>\n<p>\u041e\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0442\u0435\u043c\u044b \u0438 \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0433\u043e\u0442\u043e\u0432\u044b\u043c \u0432\u0438\u0434\u0435\u043e\u0444\u0430\u0439\u043b\u043e\u043c. \u041f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0432 \u0441\u043e\u0446\u0441\u0435\u0442\u0438, \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430, \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432 \u043f\u043e\u043a\u0430 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0437\u0430 \u0435\u0433\u043e \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438.<\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0442\u0430\u0434\u0438\u0438:<\/p>\n<pre><code>topic  |  vplan -------&gt; images -------&gt; videos ---+  |                                      |  +----------&gt; speech -------------------+--&gt; render<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u0441\u0442\u0430\u0434\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u043e\u0437\u0432\u0443\u0447\u043a\u0430 \u0438 \u0432\u0438\u0434\u0435\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0446\u0435\u043d\u044b \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c\u044e. \u041a\u0430\u0436\u0434\u0430\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u0430\u044f \u0441\u0446\u0435\u043d\u0430 \u0441\u0440\u0430\u0437\u0443 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f.<\/p>\n<p>\u0422\u0435\u043a\u0443\u0449\u0438\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u044b:<\/p>\n<ul>\n<li>\n<p>CometAPI \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 production plan \u0447\u0435\u0440\u0435\u0437 LLM \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439;<\/p>\n<\/li>\n<li>\n<p>Kie\/Grok \u2014 image-to-video;<\/p>\n<\/li>\n<li>\n<p>ImgBB \u2014 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u043c\u043e\u0441\u0442 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 Kie;<\/p>\n<\/li>\n<li>\n<p>Yandex SpeechKit \u2014 \u043e\u0437\u0432\u0443\u0447\u043a\u0430;<\/p>\n<\/li>\n<li>\n<p>MoviePy \u0438 FFmpeg \u2014 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u043d\u0442\u0430\u0436, \u0430\u0443\u0434\u0438\u043e \u0438 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044b.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u0442\u044c \u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c <code>fake<\/code>. \u041e\u043d \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0442\u043e\u0442 \u0436\u0435 application flow, \u043d\u043e \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043b\u0430\u0442\u043d\u044b\u0435 API.<\/p>\n<h3>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430: \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u0432\u043d\u0443\u0442\u0440\u044c<\/h3>\n<p>\u041a\u043e\u0434 v2 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 <code>src\/shorts_maker<\/code> \u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d \u043d\u0430 \u0447\u0435\u0442\u044b\u0440\u0435 \u0441\u043b\u043e\u044f:<\/p>\n<pre><code>presentation \u2500\u2500\u2500\u2500\u2500&gt; application \u2500\u2500\u2500\u2500\u2500&gt; domain                           ^                           |infrastructure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500+<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>Domain<\/h4>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f <code>Project<\/code>, \u0441\u0446\u0435\u043d\u044b, \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0438, value objects, \u0438\u043d\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0430\u0434\u0438\u044f\u043c\u0438. \u0414\u043e\u043c\u0435\u043d \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e Pydantic, HTTP, MoviePy, \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0438 CLI.<\/p>\n<h4>Application<\/h4>\n<p>\u0417\u0434\u0435\u0441\u044c \u0436\u0438\u0432\u0443\u0442 use cases: \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442, \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043b\u0430\u043d, \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0440\u0435\u0447\u044c \u0438 \u0432\u0438\u0434\u0435\u043e, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0440\u0435\u043d\u0434\u0435\u0440. \u042d\u0442\u043e\u0442 \u0436\u0435 \u0441\u043b\u043e\u0439 \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u043f\u043e\u0440\u0442\u044b.<\/p>\n<h4>Infrastructure<\/h4>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0440\u0442\u043e\u0432: HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u044b \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432, JSON-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043c\u0435\u0434\u0438\u0430 \u0438 MoviePy-\u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440.<\/p>\n<h4>Presentation<\/h4>\n<p>CLI \u043d\u0430 Typer \u0438 Rich. \u041e\u043d \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 use case \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043d\u043e \u043d\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u0438\u0437\u043d\u0435\u0441-\u0440\u0435\u0448\u0435\u043d\u0438\u0439.<\/p>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u043c\u0435\u0441\u0442\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e \u0437\u043d\u0430\u0442\u044c \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u0441\u043b\u043e\u044f\u0445, \u2014 <code>bootstrap.py<\/code>. \u042d\u0442\u043e composition root, \u0433\u0434\u0435 \u043f\u043e\u0440\u0442\u044b \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c\u0438 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430\u043c\u0438.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c: \u043e\u043d \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u044b \u0447\u0435\u0440\u0435\u0437 <code>ast<\/code> \u0438 \u043f\u0430\u0434\u0430\u0435\u0442, \u0435\u0441\u043b\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>domain<\/code> \u043d\u0430\u0447\u0430\u043b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>infrastructure<\/code>.<\/p>\n<pre><code class=\"python\">FORBIDDEN_PREFIXES = {    \"domain\": (        \"shorts_maker.application\",        \"shorts_maker.infrastructure\",        \"shorts_maker.presentation\",        \"shorts_maker.bootstrap\",    ),    \"application\": (        \"shorts_maker.infrastructure\",        \"shorts_maker.presentation\",        \"shorts_maker.bootstrap\",    ),}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u043a\u0430\u0436\u0443\u0442\u0441\u044f \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u044b\u043c\u0438, \u043f\u043e\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439. \u041d\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 <code>architecture.md<\/code>, \u0440\u0430\u043d\u043e \u0438\u043b\u0438 \u043f\u043e\u0437\u0434\u043d\u043e \u043d\u0430\u0440\u0443\u0448\u0430\u0435\u0442\u0441\u044f. \u0418\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0436\u0438\u0432\u0451\u0442 \u0434\u043e\u043b\u044c\u0448\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.<\/p>\n<h3>Project \u043a\u0430\u043a \u0430\u0433\u0440\u0435\u0433\u0430\u0442<\/h3>\n<p>\u0412 \u0446\u0435\u043d\u0442\u0440\u0435 \u0434\u043e\u043c\u0435\u043d\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0430\u0433\u0440\u0435\u0433\u0430\u0442 <code>Project<\/code>. \u0421\u0446\u0435\u043d\u0430 \u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0438 \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0432\u043e\u0439 production status \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e: \u0432\u0441\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0440\u0435\u043d\u044c \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u0430.<\/p>\n<p>\u0414\u043e\u043c\u0435\u043d \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u044b\u0445 <code>dataclass<\/code>, \u0430 \u043d\u0435 \u043d\u0430 Pydantic:<\/p>\n<pre><code class=\"python\">@dataclass(slots=True)class Project:    id: ProjectId    topic: str    title: str    style_hint: str | None    characters: tuple[Character, ...]    scenes: list[Scene]    stages: dict[Stage, StageProgress]    revision: int    created_at: datetime    updated_at: datetime<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Pydantic \u0432 v2 \u043e\u0441\u0442\u0430\u043b\u0441\u044f, \u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u0445: \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, JSON-\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u0438 \u043e\u0442\u0432\u0435\u0442\u044b \u0432\u043d\u0435\u0448\u043d\u0438\u0445 API. \u042d\u0442\u043e \u0432\u0430\u0436\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435. \u0424\u043e\u0440\u043c\u0430\u0442 \u043e\u0442\u0432\u0435\u0442\u0430 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u2014 \u043d\u0435 \u043c\u043e\u044f \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c.<\/p>\n<p>\u0410\u0433\u0440\u0435\u0433\u0430\u0442 \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u0442 \u0438\u043d\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b:<\/p>\n<ul>\n<li>\n<p>\u0442\u0435\u043c\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u043e\u0439;<\/p>\n<\/li>\n<li>\n<p>\u043d\u043e\u043c\u0435\u0440\u0430 \u0441\u0446\u0435\u043d \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b, \u0438\u0434\u0443\u0442 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443 \u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u0441 \u043d\u0443\u043b\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0432 \u0440\u043e\u043b\u0438\u043a\u0435 \u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0440\u0451\u0445 \u0433\u043b\u0430\u0432\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0446\u0435\u043d\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0441\u044b\u043b\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0435\u0442 \u0432 character bible;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0442\u0430\u0434\u0438\u044e \u043d\u0435\u043b\u044c\u0437\u044f \u043d\u0430\u0447\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u044b \u0435\u0451 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0442\u0430\u0434\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u0432\u0438\u0434\u0435\u043e \u0438\u043b\u0438 \u0430\u0443\u0434\u0438\u043e;<\/p>\n<\/li>\n<li>\n<p>\u0432\u0440\u0435\u043c\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0437\u0430\u0434.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0447\u0430\u043b\u043e \u0441\u0442\u0430\u0434\u0438\u0438 \u2014 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435, \u0430 \u0434\u043e\u043c\u0435\u043d\u043d\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f:<\/p>\n<pre><code class=\"python\">def start_stage(self, stage: Stage, *, resume_interrupted: bool = False) -&gt; bool:    progress = self.stages[stage]    if progress.status is StageStatus.COMPLETED:        return False    missing = [        dependency        for dependency in STAGE_DEPENDENCIES[stage]        if self.stages[dependency].status is not StageStatus.COMPLETED    ]    if missing:        raise MissingStageDependencyError(...)    progress.status = StageStatus.RUNNING    progress.attempts += 1    self._touch()    return True<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 \u0432\u0435\u0440\u043d\u0443\u043b <code>False<\/code>, use case \u0437\u043d\u0430\u0435\u0442, \u0447\u0442\u043e \u0441\u0442\u0430\u0434\u0438\u044f \u0443\u0436\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u0438 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e.<\/p>\n<h3>\u041d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e success \u0438\u043b\u0438 failed<\/h3>\n<p>\u0423 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u0435\u0441\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u044f\u0442\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432:<\/p>\n<pre><code class=\"python\">class StageStatus(StrEnum):    PENDING = \"pending\"    RUNNING = \"running\"    COMPLETED = \"completed\"    PARTIAL = \"partial\"    FAILED = \"failed\"<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>PARTIAL<\/code> \u2014 \u043e\u0434\u0438\u043d \u0438\u0437 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 \u0432\u043e \u0432\u0441\u0451\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0434\u0432\u0435 \u0441\u0446\u0435\u043d\u044b \u0438\u0437 \u0442\u0440\u0451\u0445 \u0433\u043e\u0442\u043e\u0432\u044b, \u0441\u0442\u0430\u0434\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u0430\u044f. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0446\u0435\u043d\u044b \u0431\u0435\u0437 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0430:<\/p>\n<pre><code class=\"python\">def pending_scenes(self, stage: Stage) -&gt; tuple[Scene, ...]:    kind = SCENE_STAGE_ARTIFACT[stage]    return tuple(        scene for scene in self.scenes        if kind not in scene.artifacts    )<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 pipeline \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b\u043c \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438. \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e, \u043d\u043e \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u043d\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u0442\u0441\u044f.<\/p>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 <code>running<\/code> \u0442\u043e\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0431\u0440\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. \u041d\u043e\u0432\u044b\u0439 attempt \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u0441\u0442\u0430\u0434\u0438\u044e \u0432 \u0440\u0430\u0431\u043e\u0442\u0443 \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u0441 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.<\/p>\n<h3>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c \u0441 checkpoint \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0446\u0435\u043d\u044b<\/h3>\n<p>\u0412 v1 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0431\u044b\u043b <code>asyncio.gather()<\/code>. \u0412 v2 \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e, \u043d\u043e orchestration \u0443\u0441\u0442\u0440\u043e\u0435\u043d \u0438\u043d\u0430\u0447\u0435.<\/p>\n<p>\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439:<\/p>\n<pre><code class=\"python\">pending = project.pending_scenes(Stage.IMAGES)semaphore = asyncio.Semaphore(max_concurrency)async def generate(scene_id: SceneId):    async with semaphore:        return await image_generator.generate(...)tasks = [asyncio.create_task(generate(scene.id)) for scene in pending]for future in asyncio.as_completed(tasks):    result = await future    if isinstance(result, SceneSuccess):        project.attach_scene_artifact(            stage=Stage.IMAGES,            scene_id=result.scene_id,            artifact=result.value,        )    else:        project.record_scene_failure(...)    await repository.save(project, expected_revision=expected_revision)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c \u0442\u0440\u0438 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u0430.<\/p>\n<p>\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, <code>Semaphore<\/code> \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0447\u0438\u0441\u043b\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0411\u0435\u0437 \u043d\u0435\u0433\u043e \u043b\u0435\u0433\u043a\u043e \u0443\u043f\u0435\u0440\u0435\u0442\u044c\u0441\u044f \u0432 rate limit \u0438\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0443 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u043f\u043b\u0430\u0442\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<p>\u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, worker \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u043d\u0435 \u043c\u0443\u0442\u0438\u0440\u0443\u0435\u0442 \u0430\u0433\u0440\u0435\u0433\u0430\u0442. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0432 application loop, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043d\u0435\u0442 \u0433\u043e\u043d\u043a\u0438 \u0437\u0430 \u043e\u0431\u0449\u0438\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c <code>Project<\/code>.<\/p>\n<p>\u0412-\u0442\u0440\u0435\u0442\u044c\u0438\u0445, \u043f\u043e\u0441\u043b\u0435 <strong>\u043a\u0430\u0436\u0434\u043e\u0433\u043e<\/strong> \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f checkpoint. \u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0443\u043f\u0430\u0434\u0451\u0442 \u043f\u043e\u0441\u043b\u0435 \u0441\u0435\u0434\u044c\u043c\u043e\u0439 \u0441\u0446\u0435\u043d\u044b, \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0435\u043c\u044c \u043e\u0441\u0442\u0430\u043d\u0443\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u044c\u044e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>\u042d\u0442\u043e \u0447\u0443\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u0441\u043b\u043e\u0432\u043d\u0435\u0435 \u043e\u0434\u043d\u043e\u0433\u043e <code>gather()<\/code>, \u0437\u0430\u0442\u043e \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u044f\u0432\u043d\u043e\u0439.<\/p>\n<h3>\u041e\u0434\u0438\u043d JSON-\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u043a\u0430\u043a consistency boundary<\/h3>\n<p>\u0414\u043b\u044f v2 \u044f \u0441\u043e\u0437\u043d\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0435 \u0441\u0442\u0430\u043b \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c PostgreSQL. \u041e\u0434\u0438\u043d \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043e\u0434\u0438\u043d \u0430\u0433\u0440\u0435\u0433\u0430\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c schema-versioned \u0444\u0430\u0439\u043b\u0435:<\/p>\n<pre><code>output\/\u2514\u2500\u2500 the-trial-a1b2c3d4\/    \u251c\u2500\u2500 project.json    \u2514\u2500\u2500 assets\/        \u251c\u2500\u2500 scene-000\/        \u251c\u2500\u2500 scene-001\/        \u2514\u2500\u2500 final\/<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 <code>project.json<\/code> \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f production plan, \u0441\u0442\u0430\u0442\u0443\u0441\u044b \u0441\u0442\u0430\u0434\u0438\u0439, \u0447\u0438\u0441\u043b\u043e \u043f\u043e\u043f\u044b\u0442\u043e\u043a, \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b. \u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0441\u0445\u0435\u043c\u044b \u2014 <code>2<\/code>.<\/p>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u0442\u0430\u043a:<\/p>\n<ol>\n<li>\n<p>\u0417\u0430\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 async lock.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0442\u0435\u043c cross-process file lock.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u0430\u044f \u0440\u0435\u0432\u0438\u0437\u0438\u044f \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p>\u041d\u043e\u0432\u044b\u0439 JSON \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b.<\/p>\n<\/li>\n<li>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>flush()<\/code> \u0438 <code>fsync()<\/code>.<\/p>\n<\/li>\n<li>\n<p><code>os.replace()<\/code> \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0442\u0430\u0440\u044b\u0439 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"python\">with temporary.open(\"w\", encoding=\"utf-8\") as stream:    stream.write(content)    stream.flush()    os.fsync(stream.fileno())os.replace(temporary, path)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u043b\u0435 <code>revision<\/code> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a optimistic lock. Use case \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0440\u0435\u0432\u0438\u0437\u0438\u044e 12 \u0438 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442: \u00ab\u044f \u0438\u0437\u043c\u0435\u043d\u044f\u043b \u0438\u043c\u0435\u043d\u043d\u043e \u0432\u0435\u0440\u0441\u0438\u044e 12\u00bb. \u0415\u0441\u043b\u0438 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0443\u0436\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043b \u0432\u0435\u0440\u0441\u0438\u044e 13, \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043e\u0442\u043a\u043b\u043e\u043d\u0438\u0442 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u0438\u0445\u043e\u0439 \u043f\u043e\u0442\u0435\u0440\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<pre><code class=\"python\">if persisted.revision != expected_revision:    raise ConcurrentProjectUpdateError(...)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u0415\u0441\u043b\u0438 \u043f\u043e\u044f\u0432\u044f\u0442\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u0448\u0438\u043d \u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u0430\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u044c, consistency boundary \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043f\u0440\u0435\u0436\u043d\u0435\u0439, \u0430 \u0444\u0430\u0439\u043b\u043e\u0432\u044b\u0439 \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0410\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0441 \u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c\u0443<\/h3>\n<p>\u041c\u0435\u0434\u0438\u0430\u0444\u0430\u0439\u043b\u044b \u043d\u0435 \u043a\u043b\u0430\u0434\u0443\u0442\u0441\u044f \u043f\u043e\u0434 \u0438\u043c\u0435\u043d\u0430\u043c\u0438 \u0432\u0440\u043e\u0434\u0435 <code>scene_1_final_final_v2.png<\/code>. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442\u0441\u044f SHA-256, \u0430 \u0447\u0430\u0441\u0442\u044c checksum \u0432\u0445\u043e\u0434\u0438\u0442 \u0432 storage key:<\/p>\n<pre><code class=\"python\">checksum = hashlib.sha256(payload.content).hexdigest()key = (    f\"{project_id}\/assets\/{owner}\/\"    f\"{kind.value}-{checksum[:12]}.{payload.extension}\")<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430:<\/p>\n<ul>\n<li>\n<p>\u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u043e\u0435 \u0438\u043c\u044f;<\/p>\n<\/li>\n<li>\n<p>\u043a\u043e\u043d\u043a\u0443\u0440\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0438 \u043d\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u0440\u0430\u0437\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0434\u043d\u0438\u043c \u043f\u0443\u0442\u0451\u043c;<\/p>\n<\/li>\n<li>\n<p>manifest \u0441\u0441\u044b\u043b\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 immutable-\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u0438 \u0447\u0442\u0435\u043d\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c SHA-256 \u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u043f\u043e\u0432\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u043f\u0440\u043e\u0438\u0433\u0440\u0430\u043b\u0430 optimistic lock, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0435\u044e \u0444\u0430\u0439\u043b \u043c\u043e\u0436\u0435\u0442 \u043e\u0441\u0442\u0430\u0442\u044c\u0441\u044f orphaned. \u041d\u043e \u043e\u043d \u043d\u0435 \u043f\u043e\u043f\u0430\u0434\u0451\u0442 \u0432 winning manifest \u0438 \u043d\u0435 \u0438\u0441\u043f\u043e\u0440\u0442\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u041f\u043e\u0437\u0436\u0435 \u0442\u0430\u043a\u0438\u0435 \u0444\u0430\u0439\u043b\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u0447\u0438\u0441\u0442\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c garbage collector\u2019\u043e\u043c.<\/p>\n<h3>\u041f\u043e\u0440\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u043e \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432<\/h3>\n<p>\u0412 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0431\u044b\u043b\u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u043d\u0430 <code>ABC<\/code>. \u0412 v2 \u044f \u043f\u0435\u0440\u0435\u0448\u0451\u043b \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u0443\u044e \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 <code>Protocol<\/code>:<\/p>\n<pre><code class=\"python\">class ImageGenerator(Protocol):    async def generate(self, request: ImageRequest) -&gt; MediaPayload: ...class VideoGenerator(Protocol):    async def generate(self, request: VideoRequest) -&gt; MediaPayload: ...class ProjectRepository(Protocol):    async def get(self, project_id: ProjectId) -&gt; Project: ...    async def save(        self,        project: Project,        *,        expected_revision: int,    ) -&gt; None: ...<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410\u0434\u0430\u043f\u0442\u0435\u0440\u0443 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043e\u0431\u0449\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430. \u0414\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u043e\u0431\u043b\u044e\u0434\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 mypy.<\/p>\n<p>Composition root \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435:<\/p>\n<pre><code class=\"python\">if settings.profile == \"fake\":    planner = FakeScriptPlanner()    image_generator = FakeImageGenerator()    video_generator = FakeVideoGenerator()    speech_synthesizer = FakeSpeechSynthesizer()    renderer = FakeRenderer()else:    planner = CometScriptPlanner(...)    image_generator = CometImageGenerator(...)    video_generator = KieVideoGenerator(...)    speech_synthesizer = YandexSpeechSynthesizer(...)    renderer = MoviePyRenderer(...)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Application layer \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c \u0437\u0430\u043f\u0443\u0449\u0435\u043d.<\/p>\n<h3>\u0412\u043d\u0435\u0448\u043d\u0438\u0439 API \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0435\u043d\u0430\u0434\u0451\u0436\u0435\u043d<\/h3>\n<p>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u044b \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e URL \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c JSON. \u0423 \u043d\u0438\u0445 \u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u0445\u0435\u043c\u044b polling, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f, \u0441\u0442\u0430\u0442\u0443\u0441\u044b \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u044b.<\/p>\n<p>\u041e\u0431\u0449\u0438\u0439 HTTP-\u0441\u043b\u043e\u0439 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u0442 transport errors, HTTP 429 \u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 5xx \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u043c exponential backoff:<\/p>\n<pre><code class=\"python\">_RETRYABLE_STATUSES = {408, 409, 425, 429, 500, 502, 503, 504}delay = policy.initial_delayfor attempt in range(1, policy.attempts + 1):    ...    await asyncio.sleep(delay)    delay = min(delay * 2, policy.maximum_delay)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Polling \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u043c\u0435\u0435\u0442 \u0436\u0451\u0441\u0442\u043a\u0438\u0439 deadline. \u0418\u043d\u0430\u0447\u0435 \u043e\u0434\u043d\u0430 \u0437\u0430\u0432\u0438\u0441\u0448\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u0430 \u0443\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u044c pipeline \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u043e.<\/p>\n<p>\u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u043e\u0448\u0438\u0431\u043a\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e. \u0415\u0441\u043b\u0438 \u043d\u0435 \u0437\u0430\u0434\u0430\u043d <code>SHORTS_KIE_API_KEY<\/code>, \u044d\u0442\u043e \u043d\u0435 \u00ab\u043d\u0435\u0443\u0434\u0430\u0447\u0430 \u0441\u0446\u0435\u043d\u044b\u00bb \u0438 \u043d\u0435 \u043f\u043e\u0432\u043e\u0434 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0442\u044b\u0440\u0435 \u0440\u0435\u0442\u0440\u0430\u044f. CLI \u0441\u0440\u0430\u0437\u0443 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442, \u043a\u0430\u043a\u043e\u0439 \u0441\u0435\u043a\u0440\u0435\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442, \u0430 \u0441\u0442\u0430\u0434\u0438\u044f \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0439 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u043b\u0435 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f <code>.env<\/code>.<\/p>\n<h3>Human in the loop \u0434\u043b\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u041f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0443\u0434\u043e\u0431\u043d\u0430 \u0434\u043e \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0446\u0435\u043d\u044b \u0441 \u0448\u0435\u0441\u0442\u044c\u044e \u043f\u0430\u043b\u044c\u0446\u0430\u043c\u0438, \u0441\u043b\u043e\u043c\u0430\u043d\u043d\u043e\u0439 \u043f\u0435\u0440\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u043e\u0439 \u0438\u043b\u0438 \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0432\u0448\u0438\u043c\u0441\u044f \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u043c.<\/p>\n<p>\u0412 v2 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0444\u043b\u0430\u0433:<\/p>\n<pre><code class=\"powershell\">shorts-maker run &lt;project-id&gt; --review-images<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 CLI \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u043f\u0440\u0438\u043d\u044f\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441 \u0442\u0435\u043c \u0436\u0435 prompt;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c prompt \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u043f\u043e\u043f\u044b\u0442\u043a\u0443.<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u0430\u043c review \u0442\u043e\u0436\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0440\u0442\u043e\u043c <code>ImageReviewer<\/code>. \u0412 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <code>AutoApproveImageReviewer<\/code>, \u0430 \u0432 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u043c \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043b\u044f \u043a\u043e\u043d\u0441\u043e\u043b\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 human approval \u0432\u0441\u0442\u0440\u043e\u0435\u043d \u0432 use case, \u043d\u043e UI \u043d\u0435 \u043f\u0440\u043e\u0442\u0435\u043a\u0430\u0435\u0442 \u0432 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443.<\/p>\n<p>\u0427\u0438\u0441\u043b\u043e \u043f\u043e\u043f\u044b\u0442\u043e\u043a \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 <code>SHORTS_IMAGE_REVIEW_ATTEMPTS<\/code>: \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0446\u0438\u043a\u043b \u0442\u043e\u0436\u0435 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u044b\u043c.<\/p>\n<h3>\u0427\u0435\u0442\u044b\u0440\u0435 \u0440\u0435\u0436\u0438\u043c\u0430 prompt \u0434\u043b\u044f image-to-video<\/h3>\n<p>\u0412 v1 \u0431\u044b\u043b \u0431\u0443\u043b\u0435\u0432 \u0444\u043b\u0430\u0433 <code>--video-image-only<\/code>. \u0421\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0441\u0442\u0430\u043b\u043e \u044f\u0441\u043d\u043e, \u0447\u0442\u043e \u0434\u0432\u0443\u0445 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e.<\/p>\n<p>\u0412 v2 \u0443 video adapter \u0435\u0441\u0442\u044c \u0447\u0435\u0442\u044b\u0440\u0435 \u0440\u0435\u0436\u0438\u043c\u0430:<\/p>\n<pre><code class=\"python\">class VideoPromptMode(StrEnum):    COMBINED = \"combined\"  # image prompt + motion prompt    MOTION = \"motion\"      # \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435    IMAGE = \"image\"        # \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435    NONE = \"none\"          # \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0441\u0445\u043e\u0434\u043d\u0430\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"powershell\">shorts-maker run &lt;project-id&gt; --video-prompt motion<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u044d\u0432\u043e\u043b\u044e\u0446\u0438\u0438 \u043c\u043e\u0434\u0435\u043b\u0438: \u0432\u043c\u0435\u0441\u0442\u043e \u0444\u043b\u0430\u0433\u0430 \u0441 \u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e\u0439 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u043e\u0439 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0442\u0438\u043f, \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u044f\u044e\u0449\u0438\u0439 \u0432\u0441\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438.<\/p>\n<h3>CLI: \u0441\u043e\u0437\u0434\u0430\u0442\u044c, \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c, \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c<\/h3>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<pre><code class=\"powershell\">uv run shorts-maker make \"The Trial\" --style \"retro pixel art\" --profile real<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435:<\/p>\n<pre><code class=\"powershell\">uv run shorts-maker new \"1984\" --style \"Soviet constructivism\"uv run shorts-maker run &lt;project-id&gt; --until images --profile realuv run shorts-maker status &lt;project-id&gt;uv run shorts-maker run &lt;project-id&gt; --profile real<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><code>--until images<\/code> \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438. \u041c\u043e\u0436\u043d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043b\u0430\u043d \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044b, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u0445, \u0430 \u0443\u0436\u0435 \u043f\u043e\u0442\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0434\u043e\u0440\u043e\u0433\u0443\u044e \u0441\u0442\u0430\u0434\u0438\u044e image-to-video.<\/p>\n<p>\u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0435\u0441\u0442\u044c fake profile:<\/p>\n<pre><code class=\"powershell\">uv run shorts-maker make \"The Trial\" --style \"pixel art\" --profile fake<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u043d \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c orchestration, persistence \u0438 resume \u0431\u0435\u0437 API-\u043a\u043b\u044e\u0447\u0435\u0439.<\/p>\n<h3>\u041a\u0430\u043a \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f pipeline \u0441 \u0434\u043e\u0440\u043e\u0433\u0438\u043c\u0438 API<\/h3>\n<p>\u041f\u043b\u0430\u0442\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043d\u0435 \u0432\u0445\u043e\u0434\u044f\u0442 \u0432 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 test suite. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u044b \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043f\u043e \u0443\u0440\u043e\u0432\u043d\u044f\u043c:<\/p>\n<ul>\n<li>\n<p>domain tests \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442 \u0438\u043d\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439;<\/p>\n<\/li>\n<li>\n<p>architecture tests \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u044e\u0442 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>application \u0438 integration tests \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442 use cases \u0441 fake-\u043f\u043e\u0440\u0442\u0430\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>contract tests \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044e\u0442 HTTP-\u0430\u0434\u0430\u043f\u0442\u0435\u0440\u044b \u0447\u0435\u0440\u0435\u0437 <code>httpx.MockTransport<\/code>;<\/p>\n<\/li>\n<li>\n<p>smoke test \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043c\u0435\u0434\u0438\u0430\u0444\u0430\u0439\u043b \u0447\u0435\u0440\u0435\u0437 MoviePy \u0438 FFmpeg.<\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0446\u0435\u043d\u043d\u044b\u0445 integration-\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043b\u043e\u043c\u0430\u0435\u0442 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0439 \u0441\u0446\u0435\u043d\u044b. \u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0441\u0442\u0430\u0434\u0438\u044e \u043a\u0430\u043a <code>partial<\/code>, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u2014 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u0446\u0435\u043d\u0443.<\/p>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043e\u0434\u0438\u043d \u043f\u0440\u043e\u0435\u043a\u0442 \u0447\u0435\u0440\u0435\u0437 \u0434\u0432\u0430 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439 \u0430\u0433\u0440\u0435\u0433\u0430\u0442 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0442\u0435\u0440\u0435\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043d\u043e\u0432\u0443\u044e \u0440\u0435\u0432\u0438\u0437\u0438\u044e.<\/p>\n<p>\u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0442\u0435\u0441\u0442\u044b \u043d\u0430 happy path \u0432\u0430\u0436\u043d\u044b, \u043d\u043e \u0442\u0435\u0441\u0442\u044b \u043d\u0430 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0441\u0431\u043e\u0439 \u0432\u0430\u0436\u043d\u0435\u0435.<\/p>\n<h3>\u0421\u0442\u0435\u043a v2<\/h3>\n<ul>\n<li>\n<p>Python 3.12+;<\/p>\n<\/li>\n<li>\n<p><code>asyncio<\/code> \u2014 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u043e\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 I\/O-\u0437\u0430\u0434\u0430\u0447;<\/p>\n<\/li>\n<li>\n<p>dataclasses \u2014 \u0447\u0438\u0441\u0442\u0430\u044f \u0434\u043e\u043c\u0435\u043d\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c;<\/p>\n<\/li>\n<li>\n<p>Pydantic 2 \u0438 pydantic-settings \u2014 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p>httpx \u2014 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 provider adapters;<\/p>\n<\/li>\n<li>\n<p>Typer \u0438 Rich \u2014 CLI;<\/p>\n<\/li>\n<li>\n<p>filelock \u2014 \u043c\u0435\u0436\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043d\u0430\u044f \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0430 manifest;<\/p>\n<\/li>\n<li>\n<p>Pillow \u2014 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439;<\/p>\n<\/li>\n<li>\n<p>MoviePy \u0438 FFmpeg \u2014 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u043d\u0434\u0435\u0440;<\/p>\n<\/li>\n<li>\n<p>pytest, mypy \u0438 Ruff \u2014 \u0442\u0435\u0441\u0442\u044b \u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c.<\/p>\n<\/li>\n<\/ul>\n<p>LangChain, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0432 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438, \u0432 v2 \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e \u044f\u0434\u0440\u0430. \u0414\u043b\u044f \u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e LLM-\u0432\u044b\u0437\u043e\u0432\u0430 \u043f\u0440\u044f\u043c\u043e\u0439 \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u0447\u0435\u0440\u0435\u0437 HTTP \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043f\u0440\u043e\u0449\u0435 \u0438 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u0435\u0435.<\/p>\n<h3>\u0427\u0442\u043e \u044f \u043f\u043e\u043d\u044f\u043b \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u043d\u0438\u044f<\/h3>\n<h4>Clean Architecture \u0441\u0430\u043c\u0430 \u043f\u043e \u0441\u0435\u0431\u0435 \u043d\u0435 \u0434\u0430\u0451\u0442 \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u044c<\/h4>\n<p>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>VideoGenerator<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c Kling \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441. \u041d\u043e \u043e\u043d \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441\u044b, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435 timeout, \u043a\u043e\u0433\u0434\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438 \u043c\u043e\u0436\u043d\u043e \u043b\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u044b \u044f\u0432\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0438 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f.<\/p>\n<h4>\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435 \u0432\u0441\u0435\u0433\u043e pipeline<\/h4>\n<p>\u041f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043e\u0440\u043e\u0433\u043e. \u0412 \u043c\u043e\u0451\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0441\u0442\u0430\u043b\u0430 \u043e\u0434\u043d\u0430 \u0441\u0446\u0435\u043d\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0430\u0434\u0438\u0438.<\/p>\n<h4>\u041a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u0438 \u043a\u043e\u043d\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u043c\u0435\u0441\u0442\u0435<\/h4>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u0435\u0441\u044f\u0442\u044c coroutine \u043f\u0440\u043e\u0441\u0442\u043e. \u0413\u043e\u0440\u0430\u0437\u0434\u043e \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0440\u0435\u0448\u0438\u0442\u044c, \u043a\u0442\u043e \u0438 \u0432 \u043a\u0430\u043a\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043c\u0435\u043d\u044f\u0435\u0442 aggregate, \u043a\u043e\u0433\u0434\u0430 \u043f\u0438\u0448\u0435\u0442\u0441\u044f checkpoint \u0438 \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442 \u043f\u0440\u0438 \u0434\u0432\u0443\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430\u0445.<\/p>\n<h4>\u0424\u0430\u0439\u043b\u043e\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u043d\u0435\u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u044c<\/h4>\n<p>\u041e\u0434\u0438\u043d \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 JSON \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u0443\u043c\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c, \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c schema version, atomic replace, <code>fsync<\/code>, file lock \u0438 optimistic revision check. \u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0443\u0436\u043d\u0430 \u043f\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f\u043c \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0430, \u0430 \u043d\u0435 \u0434\u043b\u044f \u043a\u0440\u0430\u0441\u043e\u0442\u044b \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b.<\/p>\n<h4>Fake provider \u2014 \u0447\u0430\u0441\u0442\u044c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0430 \u043d\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430<\/h4>\n<p>\u041e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0432\u0435\u0441\u044c application flow. \u0411\u0435\u0437 \u043d\u0435\u0433\u043e \u043b\u044e\u0431\u0430\u044f \u043f\u0440\u0430\u0432\u043a\u0430 orchestration \u043b\u0438\u0431\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u0435\u043d\u0435\u0433, \u043b\u0438\u0431\u043e \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043d\u0435\u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u043e\u0439.<\/p>\n<h3>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435<\/h3>\n<p>v2 \u043f\u043e\u043a\u0430 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0430\u043b\u044c\u0444\u0430-\u0432\u0435\u0440\u0441\u0438\u0435\u0439. \u0411\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u044f:<\/p>\n<ul>\n<li>\n<p>\u043c\u0443\u043b\u044c\u0442\u0438\u044f\u0437\u044b\u0447\u043d\u0430\u044f \u043e\u0437\u0432\u0443\u0447\u043a\u0430 \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u043e\u043b\u0438\u043a \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 locale;<\/p>\n<\/li>\n<li>\n<p>web-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u043e\u043c \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c production plan;<\/p>\n<\/li>\n<li>\n<p>\u0440\u0443\u0447\u043d\u043e\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439, \u043d\u043e \u0438 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p>garbage collection orphaned-\u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430\u043c;<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u0435 workers, \u043a\u043e\u0433\u0434\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0441\u0442\u0430\u043d\u0435\u0442 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u0440\u043e\u043b\u0438\u043a\u043e\u0432 \u0432 Shorts, Reels \u0438 TikTok \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 bounded context.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 AI-pipeline\u2019\u044b, \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434\u044b: \u043a\u0430\u043a\u0443\u044e \u0435\u0434\u0438\u043d\u0438\u0446\u0443 \u043f\u043e\u0432\u0442\u043e\u0440\u0430 \u0432\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438, \u043a\u0430\u043a \u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441 \u0438 \u0433\u0434\u0435 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u0435 \u0433\u0440\u0430\u043d\u0438\u0446\u0443 \u043c\u0435\u0436\u0434\u0443 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u043a\u043e\u0439 \u0438 human in the loop?<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1064134\/\">https:\/\/habr.com\/ru\/articles\/1064134\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440!\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0437\u0430\u0434 \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043f\u0435\u0440\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u0432\u0438\u0434\u0435\u043e \u0434\u043b\u044f Shorts, Reels \u0438 TikTok. \u041d\u0430 \u0432\u0445\u043e\u0434 \u043f\u043e\u0434\u0430\u0432\u0430\u043b\u0430\u0441\u044c \u0442\u0435\u043c\u0430 \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u00ab\u0421\u044e\u0436\u0435\u0442 \u201c1984\u201d \u0437\u0430 \u043e\u0434\u043d\u0443 \u043c\u0438\u043d\u0443\u0442\u0443\u00bb, \u2014 \u0430 \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0441\u044f \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u043e\u043b\u0438\u043a \u0441\u043e \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043c, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438, \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0435\u0439, \u043e\u0437\u0432\u0443\u0447\u043a\u043e\u0439 \u0438 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u0430\u043c\u0438.\u041f\u0435\u0440\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0434\u043e\u043a\u0430\u0437\u0430\u043b\u0430, \u0447\u0442\u043e \u0438\u0434\u0435\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041d\u043e \u043e\u043d\u0430 \u0436\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0430, \u0447\u0442\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u043e\u043b\u0438\u043a \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043d\u0430\u0434\u0451\u0436\u043d\u044b\u0439 production pipeline \u2014 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438.\u0415\u0441\u043b\u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u0430\u0434\u0430\u043b\u0430 \u043d\u0430 \u0432\u043e\u0441\u044c\u043c\u043e\u0439 \u0441\u0446\u0435\u043d\u0435 \u0438\u0437 \u0434\u0435\u0441\u044f\u0442\u0438, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u043c\u043e\u0433 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u0443\u0436\u0435 \u043e\u043f\u043b\u0430\u0447\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b. \u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u043b\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435\u043c \u0444\u0430\u0439\u043b\u0430 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u0431\u044b\u043b\u043e \u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0446\u0435\u043d\u0443 \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0438\u043b\u0438 \u043d\u0435\u0442. \u0410 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 asyncio.gather() \u0434\u0430\u0432\u0430\u043b \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c, \u043d\u043e \u043d\u0435 \u043e\u0442\u0432\u0435\u0447\u0430\u043b \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441: \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0441 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c?\u041f\u043e\u044d\u0442\u043e\u043c\u0443 v2 \u044f \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043b \u043f\u043e\u0447\u0442\u0438 \u0441 \u043d\u0443\u043b\u044f. Clean Architecture \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c, \u043d\u043e \u0432\u043e\u043a\u0440\u0443\u0433 \u043d\u0435\u0451 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c DDD, \u0430\u0433\u0440\u0435\u0433\u0430\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u044f\u0432\u043d\u0430\u044f \u043c\u0430\u0448\u0438\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439, \u0430\u0442\u043e\u043c\u0430\u0440\u043d\u044b\u0435 checkpoint\u2019\u044b, content-addressed \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u043e\u0441\u043b\u0435 \u0441\u0431\u043e\u044f.\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0443 \u043d\u0435 \u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043c\u043e\u0434\u0435\u043b\u0438, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043e\u0434\u0438\u043d API-\u0432\u044b\u0437\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0434\u043b\u0438\u0442\u044c\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442 \u0438 \u0441\u0442\u043e\u0438\u0442\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0435\u043d\u0435\u0433.\u0427\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0435 \u0442\u0430\u043a \u0441 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439\u0412 v1 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a:script = await screenwriter.generate(topic)script = await art_director.enhance(script)script = await motion_director.enhance(script)await asyncio.gather(    generate_images(script),    generate_speech(script),)await generate_videos(script)return await editor.render(script)\u0414\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0430 \u044d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u043a\u043e\u0434: \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043d\u044f\u0442\u043d\u0430, \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441\u043a\u0440\u044b\u0442\u044b \u0437\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\u043c\u0438, \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e.\u041d\u043e \u043f\u043e \u043c\u0435\u0440\u0435 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0440\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430\u041f\u043e\u043a\u0430 Python-\u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0436\u0438\u0432, \u043e\u043d \u0437\u043d\u0430\u0435\u0442, \u043a\u0430\u043a\u0438\u0435 \u0441\u0446\u0435\u043d\u044b \u0433\u043e\u0442\u043e\u0432\u044b. \u041f\u043e\u0441\u043b\u0435 Ctrl+C, \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438\u043b\u0438 \u043f\u0430\u0434\u0435\u043d\u0438\u044f \u043c\u0430\u0448\u0438\u043d\u044b \u044d\u0442\u043e \u0437\u043d\u0430\u043d\u0438\u0435 \u0442\u0435\u0440\u044f\u0435\u0442\u0441\u044f.\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043d\u0435 \u0431\u044b\u043b \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c\u0420\u0435\u0442\u0440\u0430\u0439 \u0432\u0441\u0435\u0433\u043e \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u043c\u043e\u0433 \u0441\u043d\u043e\u0432\u0430 \u0432\u044b\u0437\u0432\u0430\u0442\u044c LLM, \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438\u043b\u0438 image-to-video API. \u0414\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432. \u0414\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u2014 \u043d\u043e\u0432\u044b\u0435 \u043c\u0438\u043d\u0443\u0442\u044b \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f, \u043d\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438 \u043d\u043e\u0432\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0432 \u0431\u0438\u043b\u043b\u0438\u043d\u0433\u0435.gather() \u0441\u043a\u0440\u044b\u0432\u0430\u043b \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0443\u0441\u043f\u0435\u0445\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0438\u0437 \u0434\u0435\u0441\u044f\u0442\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0434\u0435\u0432\u044f\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0438\u0441\u044c, \u0430 \u043e\u0434\u043d\u043e \u0443\u043f\u0430\u043b\u043e \u043f\u043e timeout. \u0411\u0438\u0437\u043d\u0435\u0441-\u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0435 \u0440\u0430\u0432\u043d\u043e \u043d\u0438 \u00ab\u0443\u0441\u043f\u0435\u0445\u00bb, \u043d\u0438 \u00ab\u043e\u0448\u0438\u0431\u043a\u0430\u00bb. \u042d\u0442\u043e \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0434\u0438\u044f, \u0438 \u0435\u0451 \u043d\u0443\u0436\u043d\u043e \u0443\u043c\u0435\u0442\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c.Pydantic-\u043c\u043e\u0434\u0435\u043b\u0438 \u0441\u0442\u0430\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0440\u043e\u043b\u0435\u0439\u041e\u0434\u043d\u0438 \u0438 \u0442\u0435 \u0436\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043a\u0430\u043a \u043e\u0442\u0432\u0435\u0442\u044b API, \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430 \u0434\u0438\u0441\u043a\u0435. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 JSON \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u043b\u043e \u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0443.\u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u043f\u043e\u0441\u043b\u0435 v1 \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0442\u0430\u043a\u0438\u043c: \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u044b. \u041c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438 \u0441\u0430\u043c\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043e\u043b\u0433\u043e\u0436\u0438\u0432\u0443\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. \u0417\u043d\u0430\u0447\u0438\u0442, \u043e\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0447\u0430\u0441\u0442\u044c\u044e \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043d\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438.\u0427\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 v2Shorts Maker v2 \u2014 \u044d\u0442\u043e \u043e\u0434\u0438\u043d bounded context: \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e.\u041e\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0442\u0435\u043c\u044b \u0438 \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0433\u043e\u0442\u043e\u0432\u044b\u043c \u0432\u0438\u0434\u0435\u043e\u0444\u0430\u0439\u043b\u043e\u043c. \u041f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0432 \u0441\u043e\u0446\u0441\u0435\u0442\u0438, \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0430, \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432 \u043f\u043e\u043a\u0430 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0437\u0430 \u0435\u0433\u043e \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438.\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0442\u0430\u0434\u0438\u0438:topic  |  vplan &#8212;&#8212;-&gt; images &#8212;&#8212;-&gt; videos &#8212;+  |                                      |  +&#8212;&#8212;&#8212;-&gt; speech &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&gt; render\u0412\u043d\u0443\u0442\u0440\u0438 \u0441\u0442\u0430\u0434\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u043e\u0437\u0432\u0443\u0447\u043a\u0430 \u0438 \u0432\u0438\u0434\u0435\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0446\u0435\u043d\u044b \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0439 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u044c\u044e. \u041a\u0430\u0436\u0434\u0430\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u0430\u044f \u0441\u0446\u0435\u043d\u0430 \u0441\u0440\u0430\u0437\u0443 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f.\u0422\u0435\u043a\u0443\u0449\u0438\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u044b:CometAPI \u2014 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 production plan \u0447\u0435\u0440\u0435\u0437 LLM \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439;Kie\/Grok \u2014 image-to-video;ImgBB \u2014 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u043c\u043e\u0441\u0442 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 Kie;Yandex SpeechKit \u2014 \u043e\u0437\u0432\u0443\u0447\u043a\u0430;MoviePy \u0438 FFmpeg \u2014 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u043d\u0442\u0430\u0436, \u0430\u0443\u0434\u0438\u043e \u0438 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044b.\u0415\u0441\u0442\u044c \u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u044b\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c fake. \u041e\u043d \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0442\u043e\u0442 \u0436\u0435 application flow, \u043d\u043e \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043b\u0430\u0442\u043d\u044b\u0435 API.\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430: \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u0432\u043d\u0443\u0442\u0440\u044c\u041a\u043e\u0434 v2 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 src\/shorts_maker \u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0451\u043d \u043d\u0430 \u0447\u0435\u0442\u044b\u0440\u0435 \u0441\u043b\u043e\u044f:presentation \u2500\u2500\u2500\u2500\u2500&gt; application \u2500\u2500\u2500\u2500\u2500&gt; domain                           ^                           |infrastructure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500+Domain\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f Project, \u0441\u0446\u0435\u043d\u044b, \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0438, value objects, \u0438\u043d\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0430\u0434\u0438\u044f\u043c\u0438. \u0414\u043e\u043c\u0435\u043d \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e Pydantic, HTTP, MoviePy, \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0438 CLI.Application\u0417\u0434\u0435\u0441\u044c \u0436\u0438\u0432\u0443\u0442 use cases: \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442, \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043b\u0430\u043d, \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0440\u0435\u0447\u044c \u0438 \u0432\u0438\u0434\u0435\u043e, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0440\u0435\u043d\u0434\u0435\u0440. \u042d\u0442\u043e\u0442 \u0436\u0435 \u0441\u043b\u043e\u0439 \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u043f\u043e\u0440\u0442\u044b.Infrastructure\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0440\u0442\u043e\u0432: HTTP-\u043a\u043b\u0438\u0435\u043d\u0442\u044b \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432, JSON-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u043c\u0435\u0434\u0438\u0430 \u0438 MoviePy-\u0440\u0435\u043d\u0434\u0435\u0440\u0435\u0440.PresentationCLI \u043d\u0430 Typer \u0438 Rich. \u041e\u043d \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 use case \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043d\u043e \u043d\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u0438\u0437\u043d\u0435\u0441-\u0440\u0435\u0448\u0435\u043d\u0438\u0439.\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u043c\u0435\u0441\u0442\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e \u0437\u043d\u0430\u0442\u044c \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u0441\u043b\u043e\u044f\u0445, \u2014 bootstrap.py. \u042d\u0442\u043e composition root, \u0433\u0434\u0435 \u043f\u043e\u0440\u0442\u044b \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c\u0438 \u0430\u0434\u0430\u043f\u0442\u0435\u0440\u0430\u043c\u0438.\u041d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u043c: \u043e\u043d \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u044b \u0447\u0435\u0440\u0435\u0437 ast \u0438 \u043f\u0430\u0434\u0430\u0435\u0442, \u0435\u0441\u043b\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, domain \u043d\u0430\u0447\u0430\u043b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c infrastructure.FORBIDDEN_PREFIXES = {    &#171;domain&#187;: (        &#171;shorts_maker.application&#187;,        &#171;shorts_maker.infrastructure&#187;,        &#171;shorts_maker.presentation&#187;,        &#171;shorts_maker.bootstrap&#187;,    ),    &#171;application&#187;: (        &#171;shorts_maker.infrastructure&#187;,        &#171;shorts_maker.presentation&#187;,        &#171;shorts_maker.bootstrap&#187;,    ),}\u0422\u0430\u043a\u0438\u0435 \u0442\u0435\u0441\u0442\u044b \u043a\u0430\u0436\u0443\u0442\u0441\u044f \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u044b\u043c\u0438, \u043f\u043e\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439. \u041d\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 architecture.md, \u0440\u0430\u043d\u043e \u0438\u043b\u0438 \u043f\u043e\u0437\u0434\u043d\u043e \u043d\u0430\u0440\u0443\u0448\u0430\u0435\u0442\u0441\u044f. \u0418\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0436\u0438\u0432\u0451\u0442 \u0434\u043e\u043b\u044c\u0448\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.Project \u043a\u0430\u043a \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u0412 \u0446\u0435\u043d\u0442\u0440\u0435 \u0434\u043e\u043c\u0435\u043d\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0430\u0433\u0440\u0435\u0433\u0430\u0442 Project. \u0421\u0446\u0435\u043d\u0430 \u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0438 \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0432\u043e\u0439 production status \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e: \u0432\u0441\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0440\u0435\u043d\u044c \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u0430.\u0414\u043e\u043c\u0435\u043d \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 \u043e\u0431\u044b\u0447\u043d\u044b\u0445 dataclass, \u0430 \u043d\u0435 \u043d\u0430 Pydantic:@dataclass(slots=True)class Project:    id: ProjectId    topic: str    title: str    style_hint: str | None    characters: tuple[Character, &#8230;]    scenes: list[Scene]    stages: dict[Stage, StageProgress]    revision: int    created_at: datetime    updated_at: datetimePydantic \u0432 v2 \u043e\u0441\u0442\u0430\u043b\u0441\u044f, \u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u0445: \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, JSON-\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u0438 \u043e\u0442\u0432\u0435\u0442\u044b \u0432\u043d\u0435\u0448\u043d\u0438\u0445 API. \u042d\u0442\u043e \u0432\u0430\u0436\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435. \u0424\u043e\u0440\u043c\u0430\u0442 \u043e\u0442\u0432\u0435\u0442\u0430 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u2014 \u043d\u0435 \u043c\u043e\u044f \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c.\u0410\u0433\u0440\u0435\u0433\u0430\u0442 \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u0442 \u0438\u043d\u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b:\u0442\u0435\u043c\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0443\u0441\u0442\u043e\u0439;\u043d\u043e\u043c\u0435\u0440\u0430 \u0441\u0446\u0435\u043d \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b, \u0438\u0434\u0443\u0442 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443 \u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u0441 \u043d\u0443\u043b\u044f;\u0432 \u0440\u043e\u043b\u0438\u043a\u0435 \u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0440\u0451\u0445 \u0433\u043b\u0430\u0432\u043d\u044b\u0445 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0435\u0439;\u0441\u0446\u0435\u043d\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0441\u044b\u043b\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u0436\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0435\u0442 \u0432 character bible;\u0441\u0442\u0430\u0434\u0438\u044e \u043d\u0435\u043b\u044c\u0437\u044f \u043d\u0430\u0447\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u044b \u0435\u0451 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438;\u0441\u0442\u0430\u0434\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u0432\u0438\u0434\u0435\u043e \u0438\u043b\u0438 \u0430\u0443\u0434\u0438\u043e;\u0432\u0440\u0435\u043c\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0437\u0430\u0434.\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0447\u0430\u043b\u043e \u0441\u0442\u0430\u0434\u0438\u0438 \u2014 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435, \u0430 \u0434\u043e\u043c\u0435\u043d\u043d\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f:def start_stage(self, stage: Stage, *, resume_interrupted: bool = False) -&gt; bool:    progress = self.stages[stage]    if progress.status is StageStatus.COMPLETED:        return False    missing = [        dependency        for dependency in STAGE_DEPENDENCIES[stage]        if self.stages[dependency].status is not StageStatus.COMPLETED    ]    if missing:        raise MissingStageDependencyError(&#8230;)    progress.status = StageStatus.RUNNING    progress.attempts += 1    self._touch()    return True\u0415\u0441\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 \u0432\u0435\u0440\u043d\u0443\u043b False, use case \u0437\u043d\u0430\u0435\u0442, \u0447\u0442\u043e \u0441\u0442\u0430\u0434\u0438\u044f \u0443\u0436\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, \u0438 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e.\u041d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e success \u0438\u043b\u0438 failed\u0423 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u0435\u0441\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u044f\u0442\u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432:class StageStatus(StrEnum):    PENDING = &#171;pending&#187;    RUNNING = &#171;running&#187;    COMPLETED = &#171;completed&#187;    PARTIAL = &#171;partial&#187;    FAILED = &#171;failed&#187;PARTIAL \u2014 \u043e\u0434\u0438\u043d \u0438\u0437 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 \u0432\u043e \u0432\u0441\u0451\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.\u0415\u0441\u043b\u0438 \u0434\u0432\u0435 \u0441\u0446\u0435\u043d\u044b \u0438\u0437 \u0442\u0440\u0451\u0445 \u0433\u043e\u0442\u043e\u0432\u044b, \u0441\u0442\u0430\u0434\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u0430\u044f. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0446\u0435\u043d\u044b \u0431\u0435\u0437 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u0430:def pending_scenes(self, stage: Stage) -&gt; tuple[Scene, &#8230;]:    kind = SCENE_STAGE_ARTIFACT[stage]    return tuple(        scene for scene in self.scenes        if kind not in scene.artifacts    )\u042d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 pipeline \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b\u043c \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438. \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e, \u043d\u043e \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u043d\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u0442\u0441\u044f.\u0421\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 running \u0442\u043e\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0431\u0440\u0430\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. \u041d\u043e\u0432\u044b\u0439 attempt \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u0441\u0442\u0430\u0434\u0438\u044e \u0432 \u0440\u0430\u0431\u043e\u0442\u0443 \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u0441 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c \u0441 checkpoint \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0446\u0435\u043d\u044b\u0412 v1 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0431\u044b\u043b asyncio.gather(). \u0412 v2 \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e, \u043d\u043e orchestration \u0443\u0441\u0442\u0440\u043e\u0435\u043d \u0438\u043d\u0430\u0447\u0435.\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439:pending = project.pending_scenes(Stage.IMAGES)semaphore = asyncio.Semaphore(max_concurrency)async def generate(scene_id: SceneId):    async with semaphore:        return await image_generator.generate(&#8230;)tasks = [asyncio.create_task(generate(scene.id)) for scene in pending]for future in asyncio.as_completed(tasks):    result = await future    if isinstance(result, SceneSuccess):        project.attach_scene_artifact(            stage=Stage.IMAGES,            scene_id=result.scene_id,            artifact=result.value,        )    else:        project.record_scene_failure(&#8230;)    await repository.save(project, expected_revision=expected_revision)\u0417\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c \u0442\u0440\u0438 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u0430.\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, Semaphore \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0447\u0438\u0441\u043b\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0411\u0435\u0437 \u043d\u0435\u0433\u043e \u043b\u0435\u0433\u043a\u043e \u0443\u043f\u0435\u0440\u0435\u0442\u044c\u0441\u044f \u0432 rate limit \u0438\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0443 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u043f\u043b\u0430\u0442\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447.\u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, worker \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u043d\u0435 \u043c\u0443\u0442\u0438\u0440\u0443\u0435\u0442 \u0430\u0433\u0440\u0435\u0433\u0430\u0442. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0432 application loop, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043d\u0435\u0442 \u0433\u043e\u043d\u043a\u0438 \u0437\u0430 \u043e\u0431\u0449\u0438\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c Project.\u0412-\u0442\u0440\u0435\u0442\u044c\u0438\u0445, \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f checkpoint. \u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0443\u043f\u0430\u0434\u0451\u0442 \u043f\u043e\u0441\u043b\u0435 \u0441\u0435\u0434\u044c\u043c\u043e\u0439 \u0441\u0446\u0435\u043d\u044b, \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0435\u043c\u044c \u043e\u0441\u0442\u0430\u043d\u0443\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u044c\u044e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430.\u042d\u0442\u043e \u0447\u0443\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u0441\u043b\u043e\u0432\u043d\u0435\u0435 \u043e\u0434\u043d\u043e\u0433\u043e gather(), \u0437\u0430\u0442\u043e \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u044f\u0432\u043d\u043e\u0439.\u041e\u0434\u0438\u043d JSON-\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u043a\u0430\u043a consistency boundary\u0414\u043b\u044f v2 \u044f \u0441\u043e\u0437\u043d\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0435 \u0441\u0442\u0430\u043b \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0442\u044c PostgreSQL. \u041e\u0434\u0438\u043d \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043e\u0434\u0438\u043d \u0430\u0433\u0440\u0435\u0433\u0430\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c schema-versioned \u0444\u0430\u0439\u043b\u0435:output\/\u2514\u2500\u2500 the-trial-a1b2c3d4\/    \u251c\u2500\u2500 project.json    \u2514\u2500\u2500 assets\/        \u251c\u2500\u2500 scene-000\/        \u251c\u2500\u2500 scene-001\/        \u2514\u2500\u2500 final\/\u0412 project.json \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f production plan, \u0441\u0442\u0430\u0442\u0443\u0441\u044b \u0441\u0442\u0430\u0434\u0438\u0439, \u0447\u0438\u0441\u043b\u043e \u043f\u043e\u043f\u044b\u0442\u043e\u043a, \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-488919","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/488919","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=488919"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/488919\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=488919"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=488919"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=488919"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}