<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>🤓Бартов: про работу с ИТ- и медтекстами и прочее</title><subtitle>Евгений Бартов, редактор/переводчик/копирайтер/автоматизатор.
Главред бюро переводов «Альянс ПРО» (ИТ, ИБ | медицина)
Почта: bartov-e@ya.ru

</subtitle><author><name>🤓Бартов: про работу с ИТ- и медтекстами и прочее</name></author><id>https://teletype.in/atom/bartov</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/bartov?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@bartov?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/bartov?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-10-08T00:13:41.223Z</updated><entry><id>bartov:H-mSGIlFVGQ</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/H-mSGIlFVGQ?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Формулы, которые нельзя просто отскриншотить</title><published>2026-10-07T20:45:00.091Z</published><updated>2026-10-07T20:45:00.091Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/53/59/5359b918-105b-4cb6-bb90-5f2d3051f8ee.png"></media:thumbnail><category term="it" label="IT"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/0d/6a/0d6a53e9-be02-4516-831d-fbc4c6499053.png&quot;&gt;Кстати, когда мы обсуждали заказ на перевод книги по теории информации, клиент предложил мне просто скриншотить формулы, чтобы он их потом вставил в книгу (MS Word).</summary><content type="html">
  &lt;p id=&quot;e09H&quot;&gt;Кстати, когда мы обсуждали заказ на перевод книги по теории информации, клиент предложил мне просто скриншотить формулы, чтобы он их потом вставил в книгу (MS Word).&lt;/p&gt;
  &lt;blockquote id=&quot;Vw7N&quot;&gt;— А как же локализация формул?&lt;br /&gt;— Евгений, не пугайте меня. Их разве надо локализовать?&lt;br /&gt;— Разумеется. Поэтому скриншотами не обойдёмся.&lt;/blockquote&gt;
  &lt;p id=&quot;lJfU&quot;&gt;Попалась на глаза хорошая иллюстрация.&lt;/p&gt;
  &lt;figure id=&quot;0rdq&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/0d/6a/0d6a53e9-be02-4516-831d-fbc4c6499053.png&quot; width=&quot;455&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;pDjm&quot;&gt;Возможно, кому-то будет полезно учитывать этот момент в обсуждении своих проектов.&lt;/p&gt;

</content></entry><entry><id>bartov:KW4OZWeQdR2</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/KW4OZWeQdR2?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Galois Field или как два плюс два превращаются в ноль</title><published>2026-10-07T18:09:18.399Z</published><updated>2026-10-07T18:16:36.640Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/00/32/0032c447-2fd0-4e24-8dad-7f5924444f17.png"></media:thumbnail><category term="it" label="IT"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/8c/1d/8c1d393b-fcee-47d8-9639-7090395c8bd2.png&quot;&gt;Какие только мысли не приходят в голову, пока переводишь текстики. Вот внезапно вспомнилась хохма из моих школьных времен (1990-е годы): какие-то шутники-студенты каким-то хитроумным способом какому-то там профессору доказали, что 2 + 2 = 0. Ну и лопух же этот профессор, если повелся на такую глупость, думал я тогда.</summary><content type="html">
  &lt;p id=&quot;x8ro&quot;&gt;Какие только мысли не приходят в голову, пока переводишь текстики. Вот внезапно вспомнилась хохма из моих школьных времен (1990-е годы): какие-то шутники-студенты каким-то хитроумным способом какому-то там профессору доказали, что &lt;code&gt;2 + 2 = 0&lt;/code&gt;. Ну и лопух же этот профессор, если повелся на такую глупость, думал я тогда. &lt;/p&gt;
  &lt;p id=&quot;wgHY&quot;&gt;И вот встречаю в тексте &lt;a href=&quot;https://teletype.in/@bartov/aosbFpDlW2RvJf0x&quot; target=&quot;_blank&quot;&gt;книги по теории информации&lt;/a&gt; фразу примерно такого содержания (перевод):&lt;/p&gt;
  &lt;blockquote id=&quot;3ufr&quot;&gt;«…можно ли построить то же самое над любым коммутативным кольцом, где деление есть не всегда? В общем случае — нет, и виноваты &amp;quot;делители нуля&amp;quot; (zero divisors): ненулевые числа, которые в произведении с другим ненулевым числом дают ноль».&lt;/blockquote&gt;
  &lt;p id=&quot;z8de&quot;&gt;Учитывая, что тут упоминается свойство коммутативности (в школьном изложении оно звучит как «&lt;strong&gt;от перестановки слагаемых сумма не меняется&lt;/strong&gt;»), в этой фразе содержится ничто иное, как то самое «шутливое» утверждение из моих школьных времён. &lt;/p&gt;
  &lt;p id=&quot;jmFa&quot;&gt;&lt;u&gt;Так каким же образом может быть &lt;code&gt;2 + 2 = 0&lt;/code&gt;?&lt;/u&gt;&lt;/p&gt;
  &lt;p id=&quot;ZPOY&quot;&gt;В моём конкретном случае речь идёт о полях Галуа (конечных полях). &lt;/p&gt;
  &lt;p id=&quot;sPsZ&quot;&gt;Давние подписчики моего блога, возможно, помнят &lt;a href=&quot;https://teletype.in/@bartov/aW4YnvDh_BFEjyIj&quot; target=&quot;_blank&quot;&gt;заметку об этом французском учёном — Эваристе Галуа — из начала XIX века&lt;/a&gt;. В 17 лет он решил задачу из высшей математики, над которой лучшие математические умы мира бились без малого три века до него. В некоторых книгах даже упоминается, что именно он стал основателем высшей математики. Увы, нелепо погиб на дуэли в 20 лет.&lt;/p&gt;
  &lt;figure id=&quot;WY9b&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/8c/1d/8c1d393b-fcee-47d8-9639-7090395c8bd2.png&quot; width=&quot;472.5&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Z2UL&quot;&gt;Отвлёкся. Так вот, о &lt;strong&gt;полях Галуа (Galois field) &lt;/strong&gt;или о конечных полях. &lt;/p&gt;
  &lt;figure id=&quot;dGR4&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/97/cd/97cd56cc-7656-404d-944a-56c8c6d6355a.png&quot; width=&quot;301.5&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;8pgz&quot;&gt;Одна из идей &lt;strong&gt;конечных полей (finite fields)&lt;/strong&gt; — это конечное число используемых чисел. Как только мы в своих подсчётах доходим до последнего числа, мы снова откатываемся на начало. Вот и получается: если в нашем поле всего четыре числа — &lt;code&gt;0, 1, 2, 3&lt;/code&gt;, — то &lt;code&gt;2 + 2&lt;/code&gt; в нормальной математике дают &lt;code&gt;4&lt;/code&gt;, а поскольку в этом поле такого числа нет, то после &lt;code&gt;3&lt;/code&gt; будет &lt;code&gt;0&lt;/code&gt;. Таким образом, мы и получаем: &lt;code&gt;2 + 2 = 0&lt;/code&gt;, или применительно к умножению &lt;code&gt;2 × 2 = 0&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;Fxus&quot;&gt;Вот такая вот математика для гуманитариев типа меня :)&lt;/p&gt;
  &lt;p id=&quot;FKwO&quot;&gt;Надеюсь, было интересно.&lt;/p&gt;

</content></entry><entry><id>bartov:9nZNs_UyiBl</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/9nZNs_UyiBl?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Про локализацию листингов (кода) в книгах</title><published>2026-10-01T17:43:24.586Z</published><updated>2026-10-01T21:17:50.358Z</updated><category term="it" label="IT"></category><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/fb/2a/fb2a4943-972c-471b-b76d-c63cff6ed948.png&quot;&gt;Про локализацию кода в стайлгайдах ИТ-компаний написано скупо. И то, что написано, мягко говоря, не учитывает массу edge cases.</summary><content type="html">
  &lt;p id=&quot;SlsR&quot;&gt;Про локализацию кода в стайлгайдах ИТ-компаний написано скупо. И то, что написано, мягко говоря, не учитывает массу edge cases. &lt;/p&gt;
  &lt;p id=&quot;v7RH&quot;&gt;Темы локализации промптов там нет вообще. А я на эти грабли наступил, когда переводил  &lt;a href=&quot;https://www.piter.com/product/osnovy-graphrag-uluchshennyy-rag-na-baze-grafov-znaniy&quot; target=&quot;_blank&quot;&gt;книгу Томажа Братанича «Основы GraphRAG. Улучшенный RAG на базе графов знаний»&lt;/a&gt;. &lt;/p&gt;
  &lt;p id=&quot;SSQ7&quot;&gt;Подробно про ту проблематику я сейчас расписывать не буду. Косвенно я её &lt;a href=&quot;https://t.me/c/3728779175/5/399&quot; target=&quot;_blank&quot;&gt;упомянул в телеграм-группе «Дебагеры Read IT Club»&lt;/a&gt; . Вот цитата: &lt;/p&gt;
  &lt;p id=&quot;ai5I&quot;&gt;&lt;/p&gt;
  &lt;blockquote id=&quot;3iQJ&quot;&gt;Под листингами я понимаю следующие вещи:&lt;br /&gt;&lt;em&gt;1. Фрагменты кода / результаты его выполнения. &lt;br /&gt;2. Логи/выводы команд.&lt;br /&gt;3. Промпты/инференсы/выводы (в т.ч. системные и для взаимодействия с MCP).&lt;br /&gt;4. Сообщения об ошибках. &lt;/em&gt;&lt;/blockquote&gt;
  &lt;blockquote id=&quot;mJ0t&quot;&gt;Может, ещё что-то забыл — пожалуйста, дополните. Но, думаю, направление мыслей понятно. &lt;/blockquote&gt;
  &lt;blockquote id=&quot;lBxn&quot;&gt;С одной стороны, нельзя оставлять читателя без перевода — ему может быть непонятно. &lt;br /&gt;С другой стороны, переводить даже значения строковых переменных опасно. Вдруг автору захочется посчитать в них слова, буквы или байты, отсортировать их или использовать строковые значения в качестве заголовков полей в БД (типа &lt;code&gt;orders&lt;/code&gt;). Плюс нельзя упускать из виду такой момент: если вы в первой главе что-то перевели по определённому паттерну, то и до конца книги придётся переводить так же. Или не переводить вообще.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;QAz8&quot; data-align=&quot;center&quot;&gt;&lt;strong&gt;С промптами тоже всё неоднозначно. То, что прекрасно работает на английском в одной модели, может работать совсем не так — или совсем никак — на русском в этой же модели. И уж тем более в другой: мало ли на каких тренировочных сетах они выращивались.&lt;/strong&gt;&lt;/blockquote&gt;
  &lt;blockquote id=&quot;qRso&quot;&gt;С выводами выполнения команд тоже непросто. Вроде как там можно перевести, но надо, видимо, какое-то предупреждение для читателя выводить: этот перевод понарошку, в реальности он всё равно увидит на экране английские слова. Потому что этот шелл, справка или утилита на русский никогда не переводились — и вряд ли кому захочется оплачивать такой перевод.&lt;/blockquote&gt;
  &lt;p id=&quot;HtHc&quot;&gt;•••&lt;/p&gt;
  &lt;p id=&quot;fNmG&quot;&gt;Жарких дискуссий мой вопрос не вызвал.  Но сейчас я работаю над переводом книги &lt;a href=&quot;https://codelibs.ru/media/books/go-in-action-2-ed.pdf&quot; target=&quot;_blank&quot;&gt;Go In Action&lt;/a&gt; , и мне этот вопрос надо было как-то решать. &lt;/p&gt;
  &lt;figure id=&quot;hBtz&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fb/2a/fb2a4943-972c-471b-b76d-c63cff6ed948.png&quot; width=&quot;275&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;F00b&quot;&gt;Поэтому, когда я в конце месяца сдавал наработку, он неизбежно возник в обсуждении с заказчиком.&lt;/p&gt;
  &lt;p id=&quot;eqib&quot;&gt;Заказчик предложил переводить строковые значения там, где это безобидно. Например, если в книге даётся так: &lt;/p&gt;
  &lt;pre id=&quot;jOsi&quot; data-lang=&quot;go&quot;&gt;... 

	// Look at the text one byte at a time.
	for i := 0; i &amp;lt; len(text); i++ {
		if text[i] == &amp;#x27; &amp;#x27; {
			numSpaces++
		}
	}

	fmt.Println(&amp;quot;Found&amp;quot;, numSpaces+1, &amp;quot;words&amp;quot;)
}&lt;/pre&gt;
  &lt;p id=&quot;3Y6Z&quot;&gt;То в переводе это должно выглядеть так: &lt;/p&gt;
  &lt;pre id=&quot;KpqI&quot; data-lang=&quot;go&quot;&gt;...

	// Просматриваем текст по одному байту за раз.
	for i := 0; i &amp;lt; len(text); i++ {
		if text[i] == &amp;#x27; &amp;#x27; {
			numSpaces++
		}
	}

	fmt.Println(&amp;quot;Найдено&amp;quot;, numSpaces+1, &amp;quot;слов(а)&amp;quot;)
}&lt;/pre&gt;
  &lt;p id=&quot;8RlY&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;J2ia&quot;&gt;Или если в командной строке вывод выглядит так:&lt;/p&gt;
  &lt;pre id=&quot;RJot&quot; data-lang=&quot;bash&quot;&gt;$ ./wordcount
Found 4 words&lt;/pre&gt;
  &lt;p id=&quot;rOSg&quot;&gt;То в переводе должен выглядеть так: &lt;/p&gt;
  &lt;pre id=&quot;dDzn&quot; data-lang=&quot;bash&quot;&gt;$ ./wordcount
Найдено 4 слов(а)&lt;/pre&gt;
  &lt;p id=&quot;csZh&quot;&gt;На первый взгляд логично, и я тоже раньше так думал. Но сейчас я возразил клиенту, так как, покодив немного сам, я понял, что перевод безопаснее ставить в инлайновые комментарии. Вот так:&lt;/p&gt;
  &lt;pre id=&quot;V1HX&quot; data-lang=&quot;go&quot;&gt;... 

	// Просматриваем текст по одному байту за раз.
	for i := 0; i &amp;lt; len(text); i++ {
		if text[i] == &amp;#x27; &amp;#x27; {
			numSpaces++
		}
	}

	fmt.Println(&amp;quot;Found&amp;quot;, numSpaces+1, &amp;quot;words&amp;quot;) // Найдено &amp;lt;N&amp;gt; слов(а)
}&lt;/pre&gt;
  &lt;p id=&quot;hC7m&quot;&gt;Или так:&lt;/p&gt;
  &lt;pre id=&quot;U0qr&quot; data-lang=&quot;bash&quot;&gt;$ ./wordcount
Found 4 words # Найдено 4 слова&lt;/pre&gt;
  &lt;p id=&quot;qaYW&quot;&gt;Клиент не согласился: &lt;/p&gt;
  &lt;blockquote id=&quot;fNhs&quot;&gt;- Это же не код (&amp;quot;&lt;code&gt;Found 4 words&lt;/code&gt;&amp;quot;), а вывод программы. Так? &lt;/blockquote&gt;
  &lt;blockquote id=&quot;fPLY&quot;&gt;- Да, так. Только вы уверены, что дальше по тексту автору не захочется, например, превратить переведённое слово из строкового значения в переменную? В английском это запросто. Допустим, имеем такой листинг: &lt;/blockquote&gt;
  &lt;pre id=&quot;QnJE&quot; data-lang=&quot;go&quot;&gt;func main() {
	if len(os.Args) &amp;lt; 2 {
		log.Println(&amp;quot;need to provide at least one filename!&amp;quot;)
		os.Exit(1)
	}
}&lt;/pre&gt;
  &lt;p id=&quot;1yVn&quot;&gt;Если мы дадим такой перевод: &lt;/p&gt;
  &lt;pre id=&quot;ALRZ&quot; data-lang=&quot;go&quot;&gt;func main() {
	if len(os.Args) &amp;lt; 2 {
		log.Println (&amp;quot;Нужно предоставить хотя бы одно имя файла!&amp;quot;)
		os.Exit(1)
	}
}&lt;/pre&gt;
  &lt;p id=&quot;aEwA&quot;&gt;то уже на следующем листинге мы получаем шах и мат: &lt;/p&gt;
  &lt;pre id=&quot;rmYM&quot; data-lang=&quot;go&quot;&gt;for _, filename := range os.Args[1:] {
	file, err := os.Open(filename)
}&lt;/pre&gt;
  &lt;p id=&quot;jXOW&quot;&gt;Видите, да? &lt;code&gt;&amp;quot;filename&amp;quot;&lt;/code&gt; внезапно из строкового значения превратился в переменную &lt;code&gt;filename&lt;/code&gt;. &lt;strong&gt;И что мы теперь будем делать с нашим &amp;quot;имя файла&amp;quot;? &lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;dRoe&quot;&gt;И, чтобы далеко не отходить, рассмотрим наш исходный пример, где мы имеем те же метаморфозы со словом &lt;strong&gt;words&lt;/strong&gt;: &lt;/p&gt;
  &lt;pre id=&quot;tPoV&quot; data-lang=&quot;go&quot;&gt;func main() {
	text := &amp;quot;let&amp;#x27;s count some words!&amp;quot;
	words := strings.Fields(text)
	fmt.Println(&amp;quot;Found&amp;quot;, len(words), &amp;quot;words&amp;quot;)
}&lt;/pre&gt;
  &lt;p id=&quot;5AHs&quot;&gt;Если мы начнем строковые значения переводить, а переменные нет &lt;em&gt;(а их точно нельзя переводить)&lt;/em&gt;, мы и читателя запутаем, и сами запутаемся, и логику программы запросто сломаем. &lt;/p&gt;
  &lt;p id=&quot;Xikq&quot;&gt;Поэтому, чтобы не ломать эту логику, я и предлагаю все переводы давать в инлайновых комментариях (или не давать вообще): &lt;/p&gt;
  &lt;pre id=&quot;ly4l&quot; data-lang=&quot;go&quot;&gt;func main() {
	text := &amp;quot;let&amp;#x27;s count some words!&amp;quot;          // давайте посчитаем слова!
	words := strings.Fields(text)      
	fmt.Println(&amp;quot;Found&amp;quot;, len(words), &amp;quot;words&amp;quot;)  // Найдено &amp;lt;N&amp;gt; слов(а)
}&lt;/pre&gt;
  &lt;p id=&quot;usj6&quot;&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;TDOn&quot;&gt;На том и договорились. &lt;/p&gt;
  &lt;p id=&quot;BTCn&quot;&gt;Надеюсь, было полезно. &lt;/p&gt;
  &lt;p id=&quot;CW8M&quot;&gt;Ваш Евгений Б. &lt;/p&gt;

</content></entry><entry><id>bartov:69d78c2f9d834851b1be7692</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/69d78c2f9d834851b1be7692?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Видеокомментарии к русско-английскому учебному переводу в сфере ИБ (выпуск от 09 апреля 2026 г.)</title><published>2026-09-30T12:33:23.205Z</published><updated>2026-10-01T21:16:02.486Z</updated><tt:hashtag>учимся_переводить</tt:hashtag><summary type="html">Видео автора «Бартов: про работу с ИТ- и медтекстами и про всякое » в Дзене 🎦</summary><content type="html">
  &lt;figure id=&quot;98td&quot; class=&quot;m_custom&quot;&gt;
    &lt;iframe src=&quot;https://dzen.ru/embed/oJEZf9EYMAAA&quot;&gt;&lt;/iframe&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;QPe5&quot;&gt;&lt;em&gt;Видео автора «Бартов: про работу с ИТ- и медтекстами и про всякое » в Дзене&lt;/em&gt; 🎦&lt;/p&gt;
  &lt;tt-tags id=&quot;ZC8U&quot;&gt;
    &lt;tt-tag name=&quot;учимся_переводить&quot;&gt;#учимся_переводить&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;
  &lt;p id=&quot;gXTC&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;mvtl&quot;&gt;Продолжаю разбирать русско-английский учебный перевод вопросов к собеседованию на должность специалиста по ИБ, переводит технический писатель. &lt;/p&gt;
  &lt;p id=&quot;bU0H&quot;&gt;Разобрали вопросы/ответы №19-22. В этом выпуске я объясняю, среди прочих моментов, почему &amp;#x27;installer that supports PXE (Preboot eXecution Environment) network boot&amp;#x27; это «масло масляное», почему переводчик правильно поступил, что не стал переводить в лоб «порт отключается и вы получаете сообщение», и почему «подкованный» — это всё же не tech-savvy. &lt;/p&gt;
  &lt;p id=&quot;Tbfe&quot;&gt;Надеюсь будет интересно и познавательно. &lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;x8vd&quot;&gt;P.S. По всем вопросам (обучение переводчиков, в т.ч. в вузах, автоматизация работы с текстами, переводы, копирайтинг и пр.) писать на bartov-e*yandex.ru или в мессенджеры (искать по телефону +79039550466)&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;6F9M&quot;&gt;~&lt;/p&gt;

</content></entry><entry><id>bartov:KWfbFa56Vx-</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/KWfbFa56Vx-?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Вайб-кодинг с отладкой</title><published>2026-09-30T08:02:56.967Z</published><updated>2026-09-30T08:02:56.967Z</updated><category term="automation" label="Automation"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/81/7b/817ba77c-70a6-416c-a987-43018b04895a.png&quot;&gt;Этот пост — для тех, кто пишет свои скрипты для автоматизации. Делюсь граблями и, возможно, приёмом, который помогает наступать на них реже.</summary><content type="html">
  &lt;p id=&quot;Duhm&quot;&gt;Этот пост — для тех, кто пишет свои скрипты для автоматизации. Делюсь граблями и, возможно, приёмом, который помогает наступать на них реже.&lt;/p&gt;
  &lt;p id=&quot;1M07&quot;&gt;Недавно выяснилось: я писал свои скрипты неправильно. Раньше как было?&lt;/p&gt;
  &lt;ol id=&quot;kiCs&quot;&gt;
    &lt;li id=&quot;ch5p&quot;&gt;Пишу запрос: «Сделай мне Python-скрипт, который делает то-то и то-то. И ещё вот это, это и это».&lt;/li&gt;
    &lt;li id=&quot;Eojr&quot;&gt;Дипсик что-то выкатывает. Пробегаю глазами по коду. Похоже на то, что я хотел, — вставляю в VS Code, запускаю. Поехали.&lt;/li&gt;
    &lt;li id=&quot;Epjt&quot;&gt;Вылазят ошибки. Они всегда вылазят. Скидываю лог, Дипсик думает, выдвигает версию и пишет что-нибудь в духе: «Ну вот теперь-то 200% заработает».&lt;/li&gt;
    &lt;li id=&quot;MclJ&quot;&gt;Запускаю снова. Что-то начинает работать, что-то перестаёт. Дальше — тяни-толкай: Дипсик правит две ошибки, попутно сносит логику двух других функций, появляются ещё две ошибки.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;dWQt&quot;&gt;В эту игру можно играть долго. Каждый раз Дипсик авторитетно заявляет: «Ну вот сейчас я ВАЩЕ ВСЁ-ВСЁ-ВСЁ учёл, стопудово заработает». Ты запускаешь код — и он опять через минуту, пять, пятнадцать или сорок выбрасывает ошибку.&lt;/p&gt;
  &lt;p id=&quot;1ogw&quot;&gt;• • •&lt;/p&gt;
  &lt;p id=&quot;s70G&quot;&gt;Как вы помните, я недавно писал &lt;a href=&quot;https://habr.com/ru/articles/1085382/&quot; target=&quot;_blank&quot;&gt;статью для своего клиента про эксперимент с вайб-кодингом (статья, кстати, даже залетела в топ Хабра&lt;/a&gt;) , где клиент мне описывал фанатов ИИ, гоняющихся &lt;s&gt;за линией горизонта &lt;/s&gt; за шансом ухватить версию работающего кода, глядя на обещания ИИ. Я узнал себя. &lt;/p&gt;
  &lt;p id=&quot;845j&quot;&gt;В разговоре, когда мы обсуждали саму статью, клиент сказал: реализовать тот или иной алгоритм программно — никаких проблем, с этим справится junior-разработчик. &lt;/p&gt;
  &lt;p id=&quot;8gSU&quot;&gt;Проблема не в этом. Проблема в том, что вокруг программной реализации алгоритма и «идеального сценария его использования» (happy path) нужно написать кучу сценариев того, как всё пойдёт не по плану. А это уже 95% оставшегося кода. &lt;/p&gt;
  &lt;p id=&quot;Oxdm&quot;&gt;Выходит, сначала надо проговорить с ИИ основную логику. Потом подолгу обсуждать краевые/запасные варианты развития событий. И только потом давать команду на выкатывание кода.&lt;/p&gt;
  &lt;p id=&quot;YytN&quot;&gt;Выглядит это так. Пишу промпт:&lt;/p&gt;
  &lt;ol id=&quot;qF4F&quot;&gt;
    &lt;li id=&quot;NHcI&quot;&gt;Хочу Python-скрипт, который делает то-то и то-то (далее пошаговый идеальный алгоритм).&lt;/li&gt;
    &lt;li id=&quot;kH95&quot;&gt;Проанализируй предложенную реализацию, выяви слабые и спорные места, предложи оптимизации.&lt;/li&gt;
    &lt;li id=&quot;PWcL&quot;&gt;Никогда не выводи код, пока не прояснишь все уточняющие вопросы. Код выводи только после моей команды «Выводи полный код».&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;vbpU&quot;&gt;• • •&lt;/p&gt;
  &lt;p id=&quot;gz2f&quot;&gt;И тут начинается нудный многочасовой допрос. Примеры из обсуждения скрипта для дедупликации книг в библиотеке:&lt;/p&gt;
  &lt;blockquote id=&quot;MgLS&quot;&gt;— Хэширование текста без полного извлечения — принципиально? Предлагаю такую-то реализацию. Согласен?&lt;br /&gt;— Что значит «уникальный файл»?&lt;br /&gt;— Где живёт индекс? Как скрипт узнаёт, что файл «новый»?&lt;br /&gt;— Что если имя файла уже содержит &lt;code&gt;[Число_страниц]_&lt;/code&gt;? Убирать старый префикс и ставить новый? Или оставлять как есть?&lt;br /&gt;— По твоему правилу выживает PDF (он в &lt;code&gt;LIB\&lt;/code&gt;), а EPUB удаляется. Это правильно? Или EPUB всё-таки должен выжить — он ведь более редактируемый?&lt;br /&gt;— Дубликаты внутри &lt;code&gt;LIB\&lt;/code&gt; тоже идут в &lt;code&gt;_duplicates&lt;/code&gt;, а не в &lt;code&gt;_trash&lt;/code&gt;?&lt;/blockquote&gt;
  &lt;p id=&quot;9DbX&quot;&gt;Вопросы идут бесконечным, как кажется, потоком. Ответишь на двадцать — он найдёт слабые места в твоих ответах и сгенерирует ещё двадцать. Таких сессий вопросов-ответов может быть 20-30.&lt;/p&gt;
  &lt;p id=&quot;Hm52&quot;&gt;Главное — не давать себе слабину. Каждый раз, отправляя пачку ответов, снова и снова спрашивать: &lt;/p&gt;
  &lt;blockquote id=&quot;Oesk&quot;&gt;Если есть ещё уточняющие вопросы, задавай.&lt;/blockquote&gt;
  &lt;p id=&quot;7hIw&quot;&gt;• • •&lt;/p&gt;
  &lt;p id=&quot;7cuf&quot;&gt;В итоге Дипсик выдаёт заветное «&lt;strong&gt;Вопросов больше нет, я готов вывести код&lt;/strong&gt;» — и выкатывает код, который работает почти сразу. &lt;/p&gt;
  &lt;p id=&quot;DgH8&quot;&gt;У меня возник всего один косяк: библиотеку Дипсик импортировал чуть позже, чем положено. Исправилось легко.&lt;/p&gt;
  &lt;p id=&quot;IXCC&quot;&gt;Эксперимент с таким допросом я провёл дважды: апдейтил скрипт для извлечения глоссария и вот этот, для дедупликации книг в библиотеке. Результатом пока доволен (на скриншоте лог работы скрипта, работает уже 12 часов без единой ошибки, со скоростью обработки примерно 32 файла/книги в минуту): &lt;/p&gt;
  &lt;figure id=&quot;P5EP&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/81/7b/817ba77c-70a6-416c-a987-43018b04895a.png&quot; width=&quot;1137&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;zKkf&quot;&gt;Таким образом, хоть ИИ и выкатывает за 2-3 минуты — это, как вы понимаете, самая финальная операция. А вот обсуждение параметров, ограничений и уточнений занимает 2-3 часа и больше даже на мелкий скрипт.&lt;/p&gt;
  &lt;p id=&quot;nuWB&quot;&gt;Надеюсь, кому-нибудь мой опыт помог. &lt;/p&gt;

</content></entry><entry><id>bartov:-_ct4xeC7JQ</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/-_ct4xeC7JQ?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Про автоматизацию набора книг через ИИ</title><published>2026-09-22T22:40:12.614Z</published><updated>2026-09-25T19:35:02.331Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/fe/28/fe28ef11-356e-4f8a-af89-0140a059a385.png"></media:thumbnail><category term="it" label="IT"></category><tt:hashtag>nlp</tt:hashtag><summary type="html">&lt;img src=&quot;https://img3.teletype.in/files/64/6a/646a9451-97cd-42e8-8d85-0055571e3234.png&quot;&gt;Наверное, я бы не скоро ещё добрался до этого скрипта, но моя коллега меня к этому подтолкнула. Дело в том, что я ленивый, и книжки, которые перевожу, набираю не я, а моя коллега, Александра.</summary><content type="html">
  &lt;tt-tags id=&quot;9pTP&quot;&gt;
    &lt;tt-tag name=&quot;nlp&quot;&gt;#nlp&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;
  &lt;p id=&quot;UJQP&quot;&gt;Наверное, я бы не скоро ещё добрался до этого скрипта, но моя коллега меня к этому подтолкнула. Дело в том, что я ленивый, и книжки, которые перевожу, набираю не я, а моя коллега, Александра. &lt;/p&gt;
  &lt;p id=&quot;DsiW&quot;&gt;Я ей скидываю книги, которые мне присылает издательство на перевод, говорю что-то вроде: «&lt;strong&gt;Александра, я через пару дней буду пустой&lt;/strong&gt;», что в переводе означает, что у меня заканчиваются подготовленные тексты на перевод, и она мне высылает свои наработки по набору.&lt;/p&gt;
  &lt;p id=&quot;hPFU&quot;&gt;И вот три дня назад пишу ей эту заветную фразу, а она отвечает: никак, уехала ненадолго в другой город, буду у компьютера через несколько дней. Ну что ж. Допиливаю остатки. И вот я реально пустой: либо набирать самому, либо ждать.&lt;/p&gt;
  &lt;p id=&quot;dKnj&quot;&gt;Набирать самому — совершенно неохота. Распознавание через Finereader — это редкостный геморрой: сначала чистишь текст от артефактов распознавания, потом форматируешь. А у меня ещё в работе книжка по математике — та самая, которую я иногда упоминаю в заметках, — с многоэтажными и длинными формулами вот такого вида:&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;figure id=&quot;gPPb&quot; class=&quot;m_column&quot;&gt;
      &lt;img src=&quot;https://img3.teletype.in/files/64/6a/646a9451-97cd-42e8-8d85-0055571e3234.png&quot; width=&quot;1113&quot; /&gt;
    &lt;/figure&gt;
  &lt;/section&gt;
  &lt;p id=&quot;wb4i&quot;&gt;Такое даже файнридер не берет.&lt;/p&gt;
  &lt;p id=&quot;IVaN&quot;&gt;Но незадолго до всех этих событий я экспериментировал с DeepSeek: скормил ему копипасту из этих формул. Он их понял и вывел в LaTeX. Я поделился наблюдениями с Александрой — и она таким макаром подготовила мне несколько глав. Тоже, впрочем, муторно: в книге почти 750 страниц, обалдеешь столько копипастить и склеивать.&lt;/p&gt;
  &lt;h2 id=&quot;Ou7q&quot;&gt;ЭТАП 1: ПЕРВЫЙ СКРИПТ&lt;/h2&gt;
  &lt;p id=&quot;Zawa&quot;&gt;И я решил этот опыт автоматизировать. Взял книгу по программированию на Go — текст там примерно такого характера:&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(236, 74%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;figure id=&quot;AAIz&quot; class=&quot;m_column&quot;&gt;
      &lt;img src=&quot;https://img3.teletype.in/files/e1/b6/e1b61d6b-eb77-44b2-9570-9bad0929b73b.png&quot; width=&quot;885&quot; /&gt;
    &lt;/figure&gt;
  &lt;/section&gt;
  &lt;p id=&quot;smKI&quot;&gt; За несколько итераций навайбкодил скрипт. Он умеет следующее:&lt;/p&gt;
  &lt;ol id=&quot;T3v5&quot;&gt;
    &lt;li id=&quot;6Tfc&quot;&gt;принимает путь к PDF-файлу плюс начальную и конечную страницы;&lt;/li&gt;
    &lt;li id=&quot;sw4x&quot;&gt;вытаскивает текст не просто копипастой, а с метатегами: где курсив, где жирный, где заголовок, где код;&lt;/li&gt;
    &lt;li id=&quot;UwV9&quot;&gt;чтобы не поперхнуться объемом и не жрать ресурсы, работает батчами по три странички;&lt;/li&gt;
    &lt;li id=&quot;ZYH0&quot;&gt;дальше соединяется с &lt;a href=&quot;https://deepseek.chat/&quot; target=&quot;_blank&quot;&gt;DeepSeek.Chat&lt;/a&gt; по API и через промпт просит преобразовать копипасту с метаинформацией в Markdown-файл;&lt;/li&gt;
    &lt;li id=&quot;otJ0&quot;&gt;DeepSeek принимает под козырёк и выдаёт результат.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;FSA7&quot;&gt;Два часа отладки — и три минуты работы скрипта. На выходе примерно 200 страниц набранного и отформатированного текста в Маркдауне:&lt;/p&gt;
  &lt;figure id=&quot;fBno&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/aa/05/aa05b20f-4901-418e-aa25-0f23c9140f0b.png&quot; width=&quot;1108&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Kfr9&quot;&gt;Вот, собственно, и код на Python:&lt;/p&gt;
  &lt;pre id=&quot;fVhO&quot; data-lang=&quot;python&quot;&gt;
# %% [I. Imports]
import fitz  # PyMuPDF
import json
import os
import re
import base64
from openai import OpenAI
from PIL import Image
import io
print(&amp;quot;✅ Cell 1: Imports loaded successfully.&amp;quot;)

# ==============================================================================
# %% [CONFIGURATION CONSTANTS]
# ==============================================================================
PDF_PATH = r&amp;quot;H:\...\GoBook.pdf&amp;quot;
START_PAGE = 170  
END_PAGE = 374
PAGES_PER_CHUNK = 3
DEEPSEEK_API_KEY = &amp;quot;sk-...&amp;quot;
PROGRESS_FILE = &amp;quot;progress.json&amp;quot;

EXTRACT_IMAGES = True  
MIN_IMAGE_SIZE = 10000  
IMAGES_OUTPUT_FILE = &amp;quot;document_images_tables.md&amp;quot; # Explicit filename
print(&amp;quot;✅ Cell 2: Configuration constants set.&amp;quot;)

# ==============================================================================
# %% [API CLIENT INITIALIZATION]
# ==============================================================================
client = OpenAI(
    api_key=DEEPSEEK_API_KEY,
    base_url=&amp;quot;https://api.deepseek.com&amp;quot;
)
print(&amp;quot;✅ Cell 3: DeepSeek API client initialized.&amp;quot;)

# ==============================================================================
# %% [SYSTEM PROMPTS]
# ==============================================================================
SYSTEM_PROMPT_TEXT = &amp;quot;&amp;quot;&amp;quot;
You are an expert assistant specialized in converting raw PDF text into clean, perfectly formatted Markdown.

CRITICAL RULES:
1. MERGE BROKEN LINES: Remove arbitrary line breaks within paragraphs. Merge lines to form continuous, flowing prose. ONLY use newlines for actual paragraph breaks, headings, lists, or code blocks.
2. HEADERS/FOOTERS: Aggressively DELETE running headers and footers. If you see a standalone page number (e.g., &amp;quot;164&amp;quot;, &amp;quot;166&amp;quot;) or a chapter title (e.g., &amp;quot;Chapter 7 Errors in Go&amp;quot;) at the very beginning or end of a page chunk, REMOVE IT. It is not part of the main text.
3. CODE BLOCKS &amp;amp; CALLOUTS: PDF extraction often interleaves marginal notes (callouts) into the middle of code blocks. 
   - Reconstruct the code block as a SINGLE, valid Markdown code block (&amp;#x60;&amp;#x60;&amp;#x60;go ... &amp;#x60;&amp;#x60;&amp;#x60;). 
   - Move any interrupting descriptive text (e.g., &amp;quot;**Function returning only an error**&amp;quot;) to a bulleted list or blockquote IMMEDIATELY AFTER the code block.
4. HEADINGS &amp;amp; LISTINGS: Recognize patterns like &amp;quot;Listing 7.1&amp;quot; or &amp;quot;Chapter 7&amp;quot; and format them as Markdown headings (e.g., &amp;#x60;### Listing 7.1: main.go&amp;#x60;). Preserve &amp;#x60;**bold**&amp;#x60; formatting.
5. BULLETS: Convert weird bullet artifacts (like &amp;quot;¡&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;•&amp;quot;) into standard Markdown dashes (&amp;#x60;- &amp;#x60;).
6. NO SUMMARIZATION: Preserve 100% of the original information, logic, and technical depth.
7. NO META-TEXT: Output ONLY the cleaned Markdown. Do NOT output page markers like &amp;quot;[CONTEXT_MARKER: PAGE X]&amp;quot;.
&amp;quot;&amp;quot;&amp;quot;

SYSTEM_PROMPT_IMAGE = &amp;quot;&amp;quot;&amp;quot;
You are an expert OCR assistant specialized in extracting text from images and figures.

YOUR TASK:
1. Analyze the image and determine if it contains any text.
2. If text is present, extract it LINE BY LINE in the exact order it appears (top to bottom).
3. Format the output as a Markdown table with TWO columns:
   - Column 1: &amp;quot;Original Line&amp;quot; - the exact text from the image
   - Column 2: &amp;quot;Translation&amp;quot; - leave this column EMPTY (just a placeholder for future translation)

OUTPUT FORMAT EXAMPLE:
| Original Line | Translation |
|---------------|-------------|
| Compressor | |
| f: X\\to\\{0,1\\}^* | |

STRICT RULES:
- If NO text is detected, output exactly: &amp;quot;[NO_TEXT_DETECTED]&amp;quot;
- Do NOT describe the image. ONLY extract text.
- Preserve mathematical notation.
&amp;quot;&amp;quot;&amp;quot;
print(&amp;quot;✅ Cell 4: System prompts configured.&amp;quot;)

# ==============================================================================
# %% [HELPER FUNCTIONS]
# ==============================================================================

def load_progress(pdf_path: str) -&amp;gt; int:
    &amp;quot;&amp;quot;&amp;quot;Loads progress ONLY if the PDF path matches. Prevents cross-file conflicts.&amp;quot;&amp;quot;&amp;quot;
    if os.path.exists(PROGRESS_FILE):
        try:
            with open(PROGRESS_FILE, &amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
                data = json.load(f)
                if data.get(&amp;quot;pdf_path&amp;quot;) == pdf_path:
                    return data.get(&amp;quot;last_processed_page&amp;quot;, START_PAGE - 1)
        except (json.JSONDecodeError, IOError):
            pass
    return START_PAGE - 1


def save_progress(pdf_path: str, page_num: int) -&amp;gt; None:
    &amp;quot;&amp;quot;&amp;quot;Saves progress with the PDF path to ensure file-specific tracking.&amp;quot;&amp;quot;&amp;quot;
    with open(PROGRESS_FILE, &amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
        json.dump({&amp;quot;pdf_path&amp;quot;: pdf_path, &amp;quot;last_processed_page&amp;quot;: page_num}, f)


def clean_pdf_artifacts(text: str) -&amp;gt; str:
    &amp;quot;&amp;quot;&amp;quot;Pre-processes text to fix math artifacts and weird bullet points.&amp;quot;&amp;quot;&amp;quot;
    # Fix math
    text = text.replace(&amp;quot; ∗&amp;quot;, &amp;quot;^*&amp;quot;).replace(&amp;quot;∗&amp;quot;, &amp;quot;^*&amp;quot;)
    text = text.replace(&amp;quot;→&amp;quot;, &amp;quot;\\to &amp;quot;).replace(&amp;quot;←&amp;quot;, &amp;quot;\\gets &amp;quot;)
    
    # Fix weird bullets (inverted exclamation, dots, squares often found in PDFs)
    text = re.sub(r&amp;#x27;^\s*[¡•▪▫]\s*&amp;#x27;, &amp;#x27;- &amp;#x27;, text, flags=re.MULTILINE)
    
    # Clean excessive spaces
    text = re.sub(r&amp;#x27; {2,}&amp;#x27;, &amp;#x27; &amp;#x27;, text)
    return text


def extract_formatted_text_from_pages(pdf_path: str, start: int, end: int) -&amp;gt; tuple:
    &amp;quot;&amp;quot;&amp;quot;Extracts text, merges lines to prevent mid-sentence breaks, and extracts images.&amp;quot;&amp;quot;&amp;quot;
    doc = fitz.open(pdf_path)
    text_chunks = []
    images_data = []
    
    for i in range(start - 1, end):
        page_num = i + 1
        page = doc[i]
        
        text_chunks.append(f&amp;quot;\n[CONTEXT_MARKER: PAGE {page_num}]\n&amp;quot;)
        
        page_dict = page.get_text(&amp;quot;dict&amp;quot;)
        page_text_parts = []
        
        for block in page_dict.get(&amp;quot;blocks&amp;quot;, []):
            if block.get(&amp;quot;type&amp;quot;) == 0:  # Text block
                block_lines = []
                for line in block.get(&amp;quot;lines&amp;quot;, []):
                    line_parts = []
                    for span in line.get(&amp;quot;spans&amp;quot;, []):
                        text = span.get(&amp;quot;text&amp;quot;, &amp;quot;&amp;quot;)
                        if not text.strip():
                            continue
                            
                        flags = span.get(&amp;quot;flags&amp;quot;, 0)
                        is_italic = bool(flags &amp;amp; 2)
                        is_bold = bool(flags &amp;amp; 16)
                        
                        text = clean_pdf_artifacts(text)
                        
                        if is_bold and is_italic:
                            line_parts.append(f&amp;quot;***{text}***&amp;quot;)
                        elif is_bold:
                            line_parts.append(f&amp;quot;**{text}**&amp;quot;)
                        elif is_italic:
                            line_parts.append(f&amp;quot;*{text}*&amp;quot;)
                        else:
                            line_parts.append(text)
                    
                    # Join spans in a line with a space
                    block_lines.append(&amp;quot; &amp;quot;.join(line_parts))
                
                # Join lines within a block with a SPACE, not a newline.
                # This prevents mid-sentence line breaks. The LLM will handle paragraph splits.
                page_text_parts.append(&amp;quot; &amp;quot;.join(block_lines))
        
        text_chunks.append(&amp;quot;\n\n&amp;quot;.join(page_text_parts)) # Double newline separates distinct blocks
        
        # Image extraction
        if EXTRACT_IMAGES:
            print(f&amp;quot;   Scanning page {page_num} for images...&amp;quot;)
            image_list = page.get_images(full=True)
            valid_images = []
            
            for img_index, img_info in enumerate(image_list):
                try:
                    xref = img_info[0]
                    base_image = doc.extract_image(xref)
                    img = Image.open(io.BytesIO(base_image[&amp;quot;image&amp;quot;]))
                    if img.width * img.height &amp;gt;= MIN_IMAGE_SIZE:
                        valid_images.append((img, img_index))
                except Exception:
                    continue
            
            if valid_images:
                print(f&amp;quot;  📎 Found {len(valid_images)} valid image(s) on page {page_num}&amp;quot;)
                for img, img_idx in valid_images:
                    print(f&amp;quot;    🖼️ Processing image {img_idx + 1}...&amp;quot;)
                    try:
                        buffer = io.BytesIO()
                        img.save(buffer, format=&amp;quot;PNG&amp;quot;)
                        img_base64 = f&amp;quot;data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode()}&amp;quot;
                        
                        response = client.chat.completions.create(
                            model=&amp;quot;deepseek-chat&amp;quot;,
                            messages=[
                                {&amp;quot;role&amp;quot;: &amp;quot;system&amp;quot;, &amp;quot;content&amp;quot;: SYSTEM_PROMPT_IMAGE},
                                {&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: [{&amp;quot;type&amp;quot;: &amp;quot;image_url&amp;quot;, &amp;quot;image_url&amp;quot;: {&amp;quot;url&amp;quot;: img_base64}}, {&amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;, &amp;quot;text&amp;quot;: &amp;quot;Extract text to table.&amp;quot;}]}
                            ],
                            max_tokens=1000,
                            temperature=0.1
                        )
                        extracted_text = response.choices[0].message.content.strip()
                        
                        if extracted_text != &amp;quot;[NO_TEXT_DETECTED]&amp;quot;:
                            images_data.append({&amp;quot;page&amp;quot;: page_num, &amp;quot;image_index&amp;quot;: img_idx, &amp;quot;extracted_text&amp;quot;: extracted_text})
                            print(&amp;quot;    ✅ Text extracted.&amp;quot;)
                        else:
                            print(&amp;quot;    ⚪ No text detected.&amp;quot;)
                    except Exception as e:
                        print(f&amp;quot;    ❌ Vision API error: {e}&amp;quot;)
            else:
                print(f&amp;quot;  ✓ No valid images found on page {page_num}&amp;quot;)
        
    doc.close()
    return &amp;quot;&amp;quot;.join(text_chunks).strip(), images_data


def validate_length(original_text: str, markdown_text: str) -&amp;gt; tuple[bool, str]:
    &amp;quot;&amp;quot;&amp;quot;Validates the output length.&amp;quot;&amp;quot;&amp;quot;
    len_orig = len(original_text)
    len_md = len(markdown_text)
    if len_orig == 0: return True, &amp;quot;Empty original, skipping.&amp;quot;
    
    ratio = len_md / len_orig
    if ratio &amp;lt; 0.8: return False, f&amp;quot;DATA LOSS! Ratio: {ratio:.2f}&amp;quot;
    if ratio &amp;gt; 2.5: return False, f&amp;quot;HALLUCINATION! Ratio: {ratio:.2f}&amp;quot;
    return True, f&amp;quot;Length OK (ratio: {ratio:.2f})&amp;quot;

print(&amp;quot;✅ Cell 5: Helper functions defined.&amp;quot;)

# ==============================================================================
# %% [MAIN PROCESSING LOOP]
# ==============================================================================

def process_pdf() -&amp;gt; None:
    print(&amp;quot;=&amp;quot; * 60)
    print(&amp;quot;🚀 STARTING PDF TO MARKDOWN PIPELINE&amp;quot;)
    print(&amp;quot;=&amp;quot; * 60)
    
    last_processed = load_progress(PDF_PATH)
    current_start = last_processed + 1
    
    if current_start &amp;gt; END_PAGE:
        print(&amp;quot;✅ All requested pages for THIS FILE have been processed.&amp;quot;)
        return

    base_name = os.path.splitext(PDF_PATH)[0]
    md_filename = f&amp;quot;{base_name}_clean.md&amp;quot;
    
    print(f&amp;quot;📁 Text output: {md_filename}&amp;quot;)
    print(f&amp;quot;📁 Images output: {IMAGES_OUTPUT_FILE}\n&amp;quot;)

    all_images_data = []

    while current_start &amp;lt;= END_PAGE:
        current_end = min(current_start + PAGES_PER_CHUNK - 1, END_PAGE)
        print(&amp;quot;-&amp;quot; * 60)
        print(f&amp;quot;🔄 Processing chunk: Pages {current_start} to {current_end}&amp;quot;)
        print(&amp;quot;-&amp;quot; * 60)
        
        print(&amp;quot;  [1/4] Extracting text and images...&amp;quot;)
        raw_text, images_data = extract_formatted_text_from_pages(PDF_PATH, current_start, current_end)
        all_images_data.extend(images_data)
        
        if not raw_text.strip():
            print(&amp;quot;  ⚠️ Chunk empty. Skipping.&amp;quot;)
            current_start = current_end + 1
            save_progress(PDF_PATH, current_end)
            continue
        print(&amp;quot;  ✅ Extraction complete.&amp;quot;)

        print(&amp;quot;  [2/4] Sending text to DeepSeek API...&amp;quot;)
        try:
            response = client.chat.completions.create(
                model=&amp;quot;deepseek-chat&amp;quot;,
                messages=[
                    {&amp;quot;role&amp;quot;: &amp;quot;system&amp;quot;, &amp;quot;content&amp;quot;: SYSTEM_PROMPT_TEXT},
                    {&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: raw_text}
                ],
                temperature=0.1
            )
            markdown_result = response.choices[0].message.content.strip()
            print(&amp;quot;  ✅ Response received.&amp;quot;)
        except Exception as e:
            print(f&amp;quot;  ❌ API Error: {e}&amp;quot;)
            break

        print(&amp;quot;  [3/4] Validating output...&amp;quot;)
        is_valid, msg = validate_length(raw_text, markdown_result)
        if not is_valid:
            print(f&amp;quot;  🛑 HALTED: {msg}&amp;quot;)
            break
        print(f&amp;quot;  ✅ {msg}&amp;quot;)

        print(&amp;quot;  [4/4] Saving results...&amp;quot;)
        with open(md_filename, &amp;quot;a&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
            f.write(markdown_result)
            f.write(&amp;quot;\n\n&amp;quot;)
        
        save_progress(PDF_PATH, current_end)
        print(f&amp;quot;  ✅ Saved and checkpointed: pages [{current_start}-{current_end}]&amp;quot;)
        
        current_start = current_end + 1

    # Force save images file if any data exists
    if all_images_data:
        print(&amp;quot;\n&amp;quot; + &amp;quot;=&amp;quot; * 60)
        print(f&amp;quot;💾 Saving {len(all_images_data)} image table(s) to: {IMAGES_OUTPUT_FILE}&amp;quot;)
        print(&amp;quot;=&amp;quot; * 60)
        try:
            with open(IMAGES_OUTPUT_FILE, &amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
                f.write(&amp;quot;# Extracted Text from Images/Figures\n\n&amp;quot;)
                f.write(&amp;quot;Column 2 is intentionally left empty for translation.\n\n---\n\n&amp;quot;)
                for img_data in all_images_data:
                    f.write(f&amp;quot;## Page {img_data[&amp;#x27;page&amp;#x27;]}, Image {img_data[&amp;#x27;image_index&amp;#x27;] + 1}\n\n&amp;quot;)
                    f.write(img_data[&amp;#x27;extracted_text&amp;#x27;])
                    f.write(&amp;quot;\n\n---\n\n&amp;quot;)
            print(&amp;quot;✅ Image tables saved successfully!&amp;quot;)
        except Exception as e:
            print(f&amp;quot;❌ Failed to save images file: {e}&amp;quot;)
    else:
        print(&amp;quot;\n⚠️ No text-containing images were found in this run.&amp;quot;)

    print(&amp;quot;=&amp;quot; * 60)
    print(&amp;quot;🎉 PIPELINE COMPLETED SUCCESSFULLY&amp;quot;)
    print(&amp;quot;=&amp;quot; * 60)


if __name__ == &amp;quot;__main__&amp;quot;:
    # Clear old progress if you want to force re-run on the same file:
    # if os.path.exists(PROGRESS_FILE): os.remove(PROGRESS_FILE)
    process_pdf()
# %%&lt;/pre&gt;
  &lt;p id=&quot;yUzf&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;3gca&quot;&gt;ЭТАП 2: ПРОБЛЕМА МАТЕМАТИЧЕСКИХ ФОРМУЛ&lt;/h2&gt;
  &lt;p id=&quot;yMuv&quot;&gt;Дальше я решил таким же макаром подготовить математическую книгу. Указал путь, попробовал на одной главе — и обломался. DeepSeek почему-то отформатировал переменные в формулах просто как жирные или курсивные буковки: просто поставил вокруг них одну или две звёздочки в разметке Маркдауна, без всякой LaTeX разметки. Но блин, он же LaTeX-разметку формул мне уже делал. Что пошло не так?&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;KeRe&quot;&gt;После некоторого дебаггинга выяснилось: модель может делать что-то одно. Получает текст с метатегами форматирования — и буквы в формулах форматирует. Получает просто текст без разметки — видит формулы, ставит LaTeX, но понятия не имеет, где у меня жирный, где курсив: в простом тексте эти маркеры теряются.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;LKRF&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;DEWZ&quot;&gt;ЭТАП 3: РЕШЕНИЕ — ДВУХПОТОЧНЫЙ ПРОМПТ (DUAL-INPUT)&lt;/h2&gt;
  &lt;p id=&quot;M2WG&quot;&gt;Выкурив пару рюмок кофе, я нашел решение: отдавать в модель две копипасты — простой текст и с метатегами. &lt;/p&gt;
  &lt;ol id=&quot;EFpR&quot;&gt;
    &lt;li id=&quot;S3iK&quot;&gt;&lt;strong&gt;[RAW TEXT]&lt;/strong&gt;: Простой текст без разметки. Модель использует его &lt;em&gt;строго&lt;/em&gt; для идентификации математических формул, переменных и символов (чтобы понять, что &amp;quot;a 1 , a 2&amp;quot; — это &lt;code&gt;$a_1, a_2$&lt;/code&gt;).&lt;/li&gt;
  &lt;/ol&gt;
  &lt;ol id=&quot;eOvA&quot;&gt;
    &lt;li id=&quot;Wqwv&quot;&gt;&lt;strong&gt;[FORMATTED TEXT]&lt;/strong&gt;: Текст с правильной структурой абзацев, маркерами жирного/курсива и исправленными разрывами строк. Модель использует его как &lt;em&gt;базу&lt;/em&gt; для структуры.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;L3fu&quot;&gt;Дальше дело техники и часа вайбкодинга с тестированием. Потестил, получил главу с формулами и форматированием — и зарядил в скрипт. &lt;/p&gt;
  &lt;p id=&quot;48AG&quot;&gt;Вот как это выглядит в набранном виде — это скриншот текста в Маркдауне + LaTeX того куска, что я скриншотил выше:&lt;/p&gt;
  &lt;figure id=&quot;eam4&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/81/71/81711423-52f6-4007-9fab-84fa14c3aa3d.png&quot; width=&quot;1072&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;yvsS&quot;&gt;Ну и сам код, тоже на Python: &lt;/p&gt;
  &lt;pre id=&quot;Owus&quot; data-lang=&quot;python&quot;&gt;# %% [I. Imports]import 
import fitz  # PyMuPDF
import json
import os
import re
import base64
from openai import OpenAI
from PIL import Image
import io
print(&amp;quot;✅ Cell 1: Imports loaded successfully.&amp;quot;)

# ==============================================================================
# %% [CONFIGURATION CONSTANTS]
# ==============================================================================
PDF_PATH = r&amp;quot;H:\...\Information Theory.pdf&amp;quot;
START_PAGE = 225
END_PAGE = 713
PAGES_PER_CHUNK = 3
DEEPSEEK_API_KEY = &amp;quot;sk-...&amp;quot;
PROGRESS_FILE = &amp;quot;progress_math.json&amp;quot;

EXTRACT_IMAGES = True  
MIN_IMAGE_SIZE = 10000  
print(&amp;quot;✅ Cell 2: Configuration constants set.&amp;quot;)

# ==============================================================================
# %% [API CLIENT INITIALIZATION]
# ==============================================================================
client = OpenAI(
    api_key=DEEPSEEK_API_KEY,
    base_url=&amp;quot;https://api.deepseek.com&amp;quot;
)
print(&amp;quot;✅ Cell 3: DeepSeek API client initialized.&amp;quot;)

# ==============================================================================
# %% [SYSTEM PROMPTS]
# ==============================================================================
SYSTEM_PROMPT_TEXT = &amp;quot;&amp;quot;&amp;quot;
You are an expert assistant specialized in converting PDF text into perfect Markdown with LaTeX math.

YOU WILL RECEIVE TWO VERSIONS OF THE SAME TEXT:
1. [RAW TEXT]: Unformatted, preserves original character sequence. Use this STRICTLY to identify mathematical formulas, variables, and symbols (e.g., recognizing that &amp;quot;a 1 , a 2&amp;quot; means &amp;quot;$a_1, a_2$&amp;quot;).
2. [FORMATTED TEXT]: Contains correct paragraph structure, bold/italic markers, and fixed line breaks. Use this as the BASE for your output.

YOUR TASK:
Merge the best of both. Output a single, clean Markdown document that:
- Uses the paragraph structure, lists, and bold/italic formatting from [FORMATTED TEXT].
- Corrects ALL mathematical notation into proper LaTeX (e.g., &amp;#x60;$a_1$&amp;#x60;, &amp;#x60;$S^n$&amp;#x60;, &amp;#x60;$X = (S_1, \\dots, S_n)$&amp;#x60;) based on the context from [RAW TEXT].
- Aggressively removes running headers, footers, and isolated bold page numbers (e.g., &amp;quot;**196**&amp;quot;, &amp;quot;**Part II**&amp;quot;).
- Preserves 100% of the original information. NO SUMMARIZATION.
- Outputs ONLY the final Markdown. Do NOT output the raw text or any meta-commentary.
&amp;quot;&amp;quot;&amp;quot;

SYSTEM_PROMPT_IMAGE = &amp;quot;&amp;quot;&amp;quot;
You are an expert OCR assistant. Extract text LINE BY LINE from the image.
Format as a 2-column Markdown table: &amp;quot;Original Line&amp;quot; and &amp;quot;Translation&amp;quot; (leave empty).
If NO text is detected, output exactly: &amp;quot;[NO_TEXT_DETECTED]&amp;quot;. Do NOT describe the image.
&amp;quot;&amp;quot;&amp;quot;
print(&amp;quot;✅ Cell 4: System prompts configured.&amp;quot;)

# ==============================================================================
# %% [HELPER FUNCTIONS]
# ==============================================================================

def load_progress() -&amp;gt; int:
    if os.path.exists(PROGRESS_FILE):
        try:
            with open(PROGRESS_FILE, &amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
                data = json.load(f)
                if data.get(&amp;quot;pdf_path&amp;quot;) == PDF_PATH:
                    return data.get(&amp;quot;last_processed_page&amp;quot;, START_PAGE - 1)
        except (json.JSONDecodeError, IOError):
            pass
    return START_PAGE - 1

def save_progress(page_num: int) -&amp;gt; None:
    with open(PROGRESS_FILE, &amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
        json.dump({&amp;quot;pdf_path&amp;quot;: PDF_PATH, &amp;quot;last_processed_page&amp;quot;: page_num}, f)

def extract_raw_text_for_context(pdf_path: str, start: int, end: int) -&amp;gt; str:
    &amp;quot;&amp;quot;&amp;quot;Extracts plain text without any markdown formatting, preserving original spacing for math context.&amp;quot;&amp;quot;&amp;quot;
    doc = fitz.open(pdf_path)
    text_chunks = []
    for i in range(start - 1, end):
        page = doc[i]
        # &amp;quot;text&amp;quot; mode gives raw string, we just clean extreme artifacts
        raw = page.get_text(&amp;quot;text&amp;quot;)
        raw = raw.replace(&amp;quot;ﬁ&amp;quot;, &amp;quot;fi&amp;quot;).replace(&amp;quot;ﬂ&amp;quot;, &amp;quot;fl&amp;quot;)
        text_chunks.append(raw)
    doc.close()
    return &amp;quot;\n&amp;quot;.join(text_chunks).strip()

def merge_lines_smartly(block_lines: list) -&amp;gt; str:
    if not block_lines:
        return &amp;quot;&amp;quot;
    merged = [block_lines[0].strip()]
    for i in range(1, len(block_lines)):
        prev_line = block_lines[i-1].strip()
        curr_line = block_lines[i].strip()
        if not curr_line:
            continue
        if re.search(r&amp;#x27;[.!?]\s*$&amp;#x27;, prev_line):
            merged.append(&amp;quot;\n\n&amp;quot; + curr_line)
        else:
            merged.append(&amp;quot; &amp;quot; + curr_line)
    return &amp;quot;&amp;quot;.join(merged)

def remove_programmatic_headers_footers(text: str) -&amp;gt; str:
    text = re.sub(r&amp;#x27;\n\s*\*\*\d+\*\*\s*\n&amp;#x27;, &amp;#x27;\n&amp;#x27;, text)
    text = re.sub(r&amp;#x27;\n\s*\*\*Part II\*\*\s*\n\s*\*\*Lossless Data Compression\*\*\s*\n&amp;#x27;, &amp;#x27;\n&amp;#x27;, text, flags=re.IGNORECASE)
    text = re.sub(r&amp;#x27;\n{3,}&amp;#x27;, &amp;#x27;\n\n&amp;#x27;, text)
    return text.strip()

def extract_formatted_text_from_pages(pdf_path: str, start: int, end: int) -&amp;gt; str:
    &amp;quot;&amp;quot;&amp;quot;Extracts text with bold/italic markers and smart line merging.&amp;quot;&amp;quot;&amp;quot;
    doc = fitz.open(pdf_path)
    page_text_parts = []
    
    for i in range(start - 1, end):
        page = doc[i]
        page_dict = page.get_text(&amp;quot;dict&amp;quot;)
        block_texts = []
        
        for block in page_dict.get(&amp;quot;blocks&amp;quot;, []):
            if block.get(&amp;quot;type&amp;quot;) == 0:
                block_lines = []
                for line in block.get(&amp;quot;lines&amp;quot;, []):
                    line_parts = []
                    for span in line.get(&amp;quot;spans&amp;quot;, []):
                        text = span.get(&amp;quot;text&amp;quot;, &amp;quot;&amp;quot;)
                        if not text.strip():
                            continue
                            
                        # Fix ligatures early
                        text = text.replace(&amp;quot;ﬁ&amp;quot;, &amp;quot;fi&amp;quot;).replace(&amp;quot;ﬂ&amp;quot;, &amp;quot;fl&amp;quot;)
                        
                        flags = span.get(&amp;quot;flags&amp;quot;, 0)
                        is_italic = bool(flags &amp;amp; 2)
                        is_bold = bool(flags &amp;amp; 16)
                        
                        if is_bold and is_italic:
                            line_parts.append(f&amp;quot;***{text}***&amp;quot;)
                        elif is_bold:
                            line_parts.append(f&amp;quot;**{text}**&amp;quot;)
                        elif is_italic:
                            line_parts.append(f&amp;quot;*{text}*&amp;quot;)
                        else:
                            line_parts.append(text)
                    
                    block_lines.append(&amp;quot; &amp;quot;.join(line_parts))
                
                merged_block = merge_lines_smartly(block_lines)
                if merged_block:
                    block_texts.append(merged_block)
        
        page_text_parts.append(&amp;quot;\n\n&amp;quot;.join(block_texts))
    
    doc.close()
    raw_formatted = &amp;quot;\n\n&amp;quot;.join(page_text_parts)
    return remove_programmatic_headers_footers(raw_formatted)

def extract_formatted_text_and_images(pdf_path: str, start: int, end: int) -&amp;gt; tuple:
    &amp;quot;&amp;quot;&amp;quot;Wrapper that gets formatted text and handles images.&amp;quot;&amp;quot;&amp;quot;
    formatted_text = extract_formatted_text_from_pages(pdf_path, start, end)
    images_data = []
    
    if EXTRACT_IMAGES:
        doc = fitz.open(pdf_path)
        for i in range(start - 1, end):
            page_num = i + 1
            page = doc[i]
            print(f&amp;quot;   Scanning page {page_num} for images...&amp;quot;)
            image_list = page.get_images(full=True)
            valid_images = []
            for img_index, img_info in enumerate(image_list):
                try:
                    xref = img_info[0]
                    base_image = doc.extract_image(xref)
                    img = Image.open(io.BytesIO(base_image[&amp;quot;image&amp;quot;]))
                    if img.width * img.height &amp;gt;= MIN_IMAGE_SIZE:
                        valid_images.append((img, img_index))
                except Exception:
                    continue
            
            if valid_images:
                print(f&amp;quot;  📎 Found {len(valid_images)} valid image(s) on page {page_num}&amp;quot;)
                for img, img_idx in valid_images:
                    print(f&amp;quot;    🖼️ Processing image {img_idx + 1}...&amp;quot;)
                    try:
                        buffer = io.BytesIO()
                        img.save(buffer, format=&amp;quot;PNG&amp;quot;)
                        img_base64 = f&amp;quot;data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode()}&amp;quot;
                        response = client.chat.completions.create(
                            model=&amp;quot;deepseek-chat&amp;quot;,
                            messages=[
                                {&amp;quot;role&amp;quot;: &amp;quot;system&amp;quot;, &amp;quot;content&amp;quot;: SYSTEM_PROMPT_IMAGE},
                                {&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: [{&amp;quot;type&amp;quot;: &amp;quot;image_url&amp;quot;, &amp;quot;image_url&amp;quot;: {&amp;quot;url&amp;quot;: img_base64}}, {&amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;, &amp;quot;text&amp;quot;: &amp;quot;Extract text to table.&amp;quot;}]}
                            ],
                            max_tokens=1000,
                            temperature=0.1
                        )
                        extracted_text = response.choices[0].message.content.strip()
                        if extracted_text != &amp;quot;[NO_TEXT_DETECTED]&amp;quot;:
                            images_data.append({&amp;quot;page&amp;quot;: page_num, &amp;quot;image_index&amp;quot;: img_idx, &amp;quot;extracted_text&amp;quot;: extracted_text})
                            print(&amp;quot;    ✅ Text extracted.&amp;quot;)
                    except Exception as e:
                        print(f&amp;quot;    ❌ Vision API error: {e}&amp;quot;)
            else:
                print(f&amp;quot;  ✓ No valid images found on page {page_num}&amp;quot;)
        doc.close()
        
    return formatted_text, images_data

def validate_length(original_text: str, markdown_text: str) -&amp;gt; tuple[bool, str]:
    len_orig = len(original_text)
    len_md = len(markdown_text)
    if len_orig == 0: return True, &amp;quot;Empty original, skipping.&amp;quot;
    ratio = len_md / len_orig
    if ratio &amp;lt; 0.8: return False, f&amp;quot;DATA LOSS! Ratio: {ratio:.2f}&amp;quot;
    if ratio &amp;gt; 2.5: return False, f&amp;quot;HALLUCINATION! Ratio: {ratio:.2f}&amp;quot;
    return True, f&amp;quot;Length OK (ratio: {ratio:.2f})&amp;quot;

print(&amp;quot;✅ Cell 5: Helper functions defined.&amp;quot;)

# ==============================================================================
# %% [MAIN PROCESSING LOOP]
# ==============================================================================

def process_pdf() -&amp;gt; None:
    print(&amp;quot;=&amp;quot; * 60)
    print(&amp;quot;🚀 STARTING PDF TO MARKDOWN PIPELINE (DUAL-INPUT MODE)&amp;quot;)
    print(&amp;quot;=&amp;quot; * 60)
    
    last_processed = load_progress()
    current_start = last_processed + 1
    
    if current_start &amp;gt; END_PAGE:
        print(&amp;quot;✅ All requested pages for THIS FILE have been processed.&amp;quot;)
        return

    base_name = os.path.splitext(PDF_PATH)[0]
    md_filename = f&amp;quot;{base_name}_clean.md&amp;quot;
    images_md_filename = f&amp;quot;{base_name}_images_tables.md&amp;quot;
    
    print(f&amp;quot;📁 Text output: {md_filename}&amp;quot;)
    print(f&amp;quot;📁 Images output: {images_md_filename}\n&amp;quot;)

    all_images_data = []

    while current_start &amp;lt;= END_PAGE:
        current_end = min(current_start + PAGES_PER_CHUNK - 1, END_PAGE)
        print(&amp;quot;-&amp;quot; * 60)
        print(f&amp;quot;🔄 Processing chunk: Pages {current_start} to {current_end}&amp;quot;)
        print(&amp;quot;-&amp;quot; * 60)
        
        print(&amp;quot;  [1/4] Extracting DUAL text versions and images...&amp;quot;)
        raw_text = extract_raw_text_for_context(PDF_PATH, current_start, current_end)
        formatted_text, images_data = extract_formatted_text_and_images(PDF_PATH, current_start, current_end)
        all_images_data.extend(images_data)
        
        if not formatted_text.strip():
            print(&amp;quot;  ⚠️ Chunk empty. Skipping.&amp;quot;)
            current_start = current_end + 1
            save_progress(current_end)
            continue
        print(&amp;quot;  ✅ Extraction complete.&amp;quot;)

        print(&amp;quot;  [2/4] Sending DUAL-INPUT to DeepSeek API...&amp;quot;)
        try:
            # Combine both texts into a single, structured prompt
            combined_user_prompt = f&amp;quot;&amp;quot;&amp;quot;
[RAW TEXT - Use for math context only]:
{raw_text}

---

[FORMATTED TEXT - Use as base structure]:
{formatted_text}
&amp;quot;&amp;quot;&amp;quot;
            response = client.chat.completions.create(
                model=&amp;quot;deepseek-chat&amp;quot;,
                messages=[
                    {&amp;quot;role&amp;quot;: &amp;quot;system&amp;quot;, &amp;quot;content&amp;quot;: SYSTEM_PROMPT_TEXT},
                    {&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: combined_user_prompt}
                ],
                temperature=0.1
            )
            markdown_result = response.choices[0].message.content.strip()
            print(&amp;quot;  ✅ Response received.&amp;quot;)
        except Exception as e:
            print(f&amp;quot;  ❌ API Error: {e}&amp;quot;)
            break

        print(&amp;quot;  [3/4] Validating output...&amp;quot;)
        # Validate against formatted text length, as it&amp;#x27;s closer to the expected output
        is_valid, msg = validate_length(formatted_text, markdown_result)
        if not is_valid:
            print(f&amp;quot;  🛑 HALTED: {msg}&amp;quot;)
            break
        print(f&amp;quot;  ✅ {msg}&amp;quot;)

        print(&amp;quot;  [4/4] Saving results...&amp;quot;)
        with open(md_filename, &amp;quot;a&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
            f.write(markdown_result)
            f.write(&amp;quot;\n\n&amp;quot;)
        
        save_progress(current_end)
        print(f&amp;quot;  ✅ Saved and checkpointed: pages [{current_start}-{current_end}]&amp;quot;)
        current_start = current_end + 1

    if all_images_data:
        print(&amp;quot;\n&amp;quot; + &amp;quot;=&amp;quot; * 60)
        print(f&amp;quot;💾 Saving {len(all_images_data)} image table(s)&amp;quot;)
        print(&amp;quot;=&amp;quot; * 60)
        try:
            with open(images_md_filename, &amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
                f.write(&amp;quot;# Extracted Text from Images/Figures\n\n&amp;quot;)
                f.write(&amp;quot;Column 2 is intentionally left empty for translation.\n\n---\n\n&amp;quot;)
                for img_data in all_images_data:
                    f.write(f&amp;quot;## Page {img_data[&amp;#x27;page&amp;#x27;]}, Image {img_data[&amp;#x27;image_index&amp;#x27;] + 1}\n\n&amp;quot;)
                    f.write(img_data[&amp;#x27;extracted_text&amp;#x27;])
                    f.write(&amp;quot;\n\n---\n\n&amp;quot;)
            print(&amp;quot;✅ Image tables saved successfully!&amp;quot;)
        except Exception as e:
            print(filed to save images file: {e}&amp;quot;)
    else:
        print(&amp;quot;\n⚠️ No text-containing images were found in this run.&amp;quot;)

    print(&amp;quot;=&amp;quot; * 60)
    print(&amp;quot;🎉 PIPELINE COMPLETED SUCCESSFULLY&amp;quot;)
    print(&amp;quot;=&amp;quot; * 60)

if __name__ == &amp;quot;__main__&amp;quot;:
    # if os.path.exists(PROGRESS_FILE): os.remove(PROGRESS_FILE)
    process_pdf()
# %%
&lt;/pre&gt;
  &lt;p id=&quot;aavC&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;PP3z&quot;&gt;Итог обработки оставшихся 500 страниц:&lt;/p&gt;
  &lt;ul id=&quot;Ekpk&quot;&gt;
    &lt;li id=&quot;oCLl&quot;&gt;&lt;strong&gt;Время обработки:&lt;/strong&gt; ~15 минут.&lt;/li&gt;
    &lt;li id=&quot;gLjC&quot;&gt;&lt;strong&gt;Стоимость (токены API):&lt;/strong&gt; ~150 рублей.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;ZOcp&quot;&gt;Совсем неплохо, как считаете?&lt;/p&gt;
  &lt;p id=&quot;Owus&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;6m7O&quot;&gt;Спасибо а внимание! Надеюсь, было познавательно. &lt;/p&gt;
  &lt;p id=&quot;mo8a&quot;&gt;~&lt;/p&gt;

</content></entry><entry><id>bartov:oez6Vhvp_w1</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/oez6Vhvp_w1?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Post 09/22/2026</title><published>2026-09-22T21:23:30.994Z</published><updated>2026-09-22T21:23:30.994Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/74/ed/74ed3fb5-3076-462f-a7b8-091e60c7a276.png"></media:thumbnail><category term="it" label="IT"></category><tt:hashtag>портфолио</tt:hashtag><summary type="html">&lt;img src=&quot;https://img3.teletype.in/files/ef/ca/efca21fc-bf06-4a9a-9f8e-bbaa662a1422.jpeg&quot;&gt;Недавно я цитировал Линуса Торвальдса применительно  к нашим переводческим реалиям. Фраза мне эта попалась на глаза не потому, что я фанат Линуса, а потому что мы в это время с коллегой писали статью о любопытном эксперименте — один большой поклонник возможностей ИИ заявил, что программисты не нужны, т.к. ИИ способен нагенерить систему любой сложности за пару недель.</summary><content type="html">
  &lt;p id=&quot;SPkx&quot;&gt;Недавно я &lt;a href=&quot;https://teletype.in/@bartov/AkNGiC0Cfqt&quot; target=&quot;_blank&quot;&gt;цитировал Линуса Торвальдса&lt;/a&gt; применительно  к нашим переводческим реалиям. Фраза мне эта попалась на глаза не потому, что я фанат Линуса, а потому что мы в это время с коллегой писали статью о любопытном эксперименте — один большой поклонник возможностей ИИ заявил, что программисты не нужны, т.к. ИИ способен нагенерить систему любой сложности за пару недель. &lt;/p&gt;
  &lt;p id=&quot;HmgZ&quot;&gt;Моего клиента это задело за живое, он предложил этому поклоннику ИИ показать его слова в деле. &lt;/p&gt;
  &lt;p id=&quot;VUCF&quot;&gt;В целом, все, что написано в статье, в том или ином виде можно параллелить на нашу переводческую специфику (и в свое заметке я именно это и сделал). &lt;/p&gt;
  &lt;p id=&quot;gwSU&quot;&gt;&lt;a href=&quot;https://habr.com/ru/articles/1085382/&quot; target=&quot;_blank&quot;&gt;Даю ссылку на Хабре, чтобы вы могли сами сделать свои выводы — https://habr.com/ru/articles/1085382/&lt;/a&gt;. Там нет особо технических деталей, там больше размышления о том, в каких местах иллюзия о возможностях ИИ начинает трещать по швам и разваливаться, а также куда нужно тыкать и смотреть, чтобы реально оценивать потолок возможностей ИИ. &lt;/p&gt;
  &lt;tt-tags id=&quot;mRyP&quot;&gt;
    &lt;tt-tag name=&quot;портфолио&quot;&gt;#портфолио&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;

</content></entry><entry><id>bartov:apME1jkrpzPumHLV</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/apME1jkrpzPumHLV?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>18 оттенков термина 'distance' (из теории информации)</title><published>2026-09-21T19:26:27.264Z</published><updated>2026-09-21T19:26:27.264Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/07/f6/07f6fdaf-452f-4676-8488-c465b3af582b.png"></media:thumbnail><category term="it" label="IT"></category><tt:hashtag>заметки_на_полях</tt:hashtag><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/3f/1d/3f1d510b-e834-4ae0-882b-6bd910885616.png&quot;&gt;Продираясь через смыслы книги по теории информации из моей прошлой заметки, я заметил, что в этой дисциплине много разных &quot;дистанций&quot; (distance). Я насчитал 18 штук, пришлось отложить перевод в сторону и разложить их по полочкам у себя в голове. Поехали.</summary><content type="html">
  &lt;tt-tags id=&quot;0qKq&quot;&gt;
    &lt;tt-tag name=&quot;заметки_на_полях&quot;&gt;#заметки_на_полях&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;
  &lt;p id=&quot;Rhh4&quot;&gt;Продираясь через смыслы книги по теории информации из моей &lt;a href=&quot;https://dzen.ru/a/aosbFpDlW2RvJf0x&quot; target=&quot;_blank&quot;&gt;прошлой заметки&lt;/a&gt;, я заметил, что в этой дисциплине много разных &amp;quot;дистанций&amp;quot; (distance). Я насчитал 18 штук, пришлось отложить перевод в сторону и разложить их по полочкам у себя в голове. Поехали.&lt;/p&gt;
  &lt;h2 id=&quot;1_pairwise_distance_poparnoe_rasstoyani&quot;&gt;1. Попарное расстояние / Pairwise Distance &lt;/h2&gt;
  &lt;p id=&quot;gy5z&quot;&gt;Допустим, мы хотим организовать форум переводчиков и нужно узнать, насколько далеко участники живут друг от друга, чтобы выбрать оптимальное место проведения. Для этого мы не выводим какую-то общую цифру, а делаем таблицу-шахматку, где пересечении каждых двух участников указано расстояние между ними.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;AhtN&quot;&gt;Это и есть &lt;strong&gt;попарное расстояние&lt;/strong&gt; — не какой-то особый способ измерения, а сам принцип оценивания дистанции между &lt;em&gt;каждой парой&lt;/em&gt; объектов в наборе данных.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;scVd&quot;&gt;В теории информации это один из базовых шагов: прежде чем анализировать систему или кодировать сообщения, ученые смотрят, как соотносятся друг с другом все элементы по отдельности.&lt;/p&gt;
  &lt;figure id=&quot;a6GH&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/3f/1d/3f1d510b-e834-4ae0-882b-6bd910885616.png&quot; width=&quot;607&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;2_evklidovo_rasstoyanie_euclidean_dista&quot;&gt;2. Евклидово расстояние / Euclidean Distance&lt;/h2&gt;
  &lt;p id=&quot;FCic&quot;&gt;Окей. Как измерять это расстояние? Самое первое, что приходит в голову - померять расстояние линейкой по карте. То есть берем карту страны, проводим прямую линию от одного участника к другому. Ну или берем глобус - и тоже проводим мысленно линию. Вот эта дистанция по прямой в двухмерном или трехмерном пространстве - которую мы так часто считали в школе на уроках геометрии — и будет Евклидово расстояние.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;ALru&quot;&gt;В теории информации оно воспринимается чуток иначе — данные представляются в виде точек в пространстве (ну типа, по осям можно отложить рост, вес и возраст участника). &lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;cekG&quot;&gt;Евклидово расстояние тогда будет считаться между точками и в таком случае покажет, насколько участники антропометрически &lt;em&gt;похожи &lt;/em&gt;друг на друга. &lt;/p&gt;
  &lt;p id=&quot;0mnp&quot;&gt;Тут правда есть большой минус — если один параметр сильно вырастет, он перетянет все внимание на себя.&lt;/p&gt;
  &lt;h2 id=&quot;3_kvadrat_evklidova_rasstoyaniya_squared&quot;&gt;3. Квадрат евклидова расстояния / Squared L2-Distance&lt;/h2&gt;
  &lt;p id=&quot;24Zg&quot;&gt;Иногда в математике прямая линия — это неудобно из-за знака корня в формуле Пифагора. Так вот если мы просто не будем извлекать корень, мы получим &lt;strong&gt;квадрат евклидова расстояния &lt;/strong&gt;(или &lt;strong&gt;squared L2&lt;/strong&gt;). В чем плюс такого подхода?&lt;/p&gt;
  &lt;p id=&quot;zLCt&quot;&gt;Представьте, что вы бросаете дротики в мишень: вам важно не просто, насколько вы промахнулись, а насколько ваш промах штрафуется.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;pyJT&quot;&gt;Вот в теории информации этот квадрат используется постоянно: он сильнее наказывает за большие ошибки, чем за маленькие. Если вы промахнулись на 2 см, штраф будет 4, а если на 5 см — штраф уж 25.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;86o8&quot;&gt;Такой подход помогает алгоритмам более агрессивно искать возможности, чтобы избежать огромных сбоев при передаче сигналов.&lt;/p&gt;
  &lt;h2 id=&quot;4_rasstoyanie_l1_manheettenskoe_rasstoya&quot;&gt;4. Манхэттенское расстояние (Расстояние L1) / L1-Distance&lt;/h2&gt;
  &lt;p id=&quot;0nqW&quot;&gt;Но что делать, если «по прямой» ходить нельзя? Представьте, что вы бродите по улицам Нью-Йорка, которые образуют ровные кварталы.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;O3Da&quot;&gt;Чтобы дойти до нужного дома, придется идти строго зигзагами: три квартала на север, два квартала на восток. Сумма этих шагов называется расстоянием L1 (или Манхэттенским расстоянием).&lt;/p&gt;
    &lt;p id=&quot;vL7S&quot;&gt;В теории информации эта концепция часто используется, когда параметры нельзя «смешивать» через квадратный корень: мы просто складываем абсолютные модули различий по каждому признаку по отдельности, как шаги по пиксельной сетке.&lt;/p&gt;
  &lt;/section&gt;
  &lt;figure id=&quot;zDn3&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f9/14/f914937f-4b3f-4a9d-b018-636880671d42.png&quot; width=&quot;340&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;5_geodezicheskoe_rasstoyanie_geodesic&quot;&gt;5. Геодезическое расстояние / “Geodesic” Distance&lt;/h2&gt;
  &lt;p id=&quot;eToz&quot;&gt;Усложним задачу: нам нужно долететь на самолете из Москвы во Владивосток. Если мы приложим линейку к плоской карте, мы явно ошибемся — ведь Земля — круглая. Кратчайший путь по искривленной поверхности сферы (дуга большого круга) — это и есть геодезическое расстояние.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;rqpq&quot;&gt;В теории информации «геодезия» возникает, когда наши данные лежат не на плоском столе, а зажаты сложными правилами (например, законами физики). Если мы плавно меняем настройки нейросети, её состояния движутся по хитрой «кривой» поверхности, и «геодезическое расстояние» показывает истинную длину этого пути без срезания углов через пустоту.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;6_vzveshennoe_rasstoyanie_weighted_di&quot;&gt;6. Взвешенное «расстояние» / Weighted “Distance”&lt;/h2&gt;
  &lt;p id=&quot;yvhM&quot;&gt;Вернемся на переводческую тусовку: мы выбираем, с кем договориться о встрече кулуарно, оценивая «дистанцию интересов» с тем или иным участником. Но для вас любовь участника к техническим дисциплинам имеет огромный вес, а то, какой рисунок кавычек предпочитает коллега — почти нулевой.&lt;/p&gt;
  &lt;p id=&quot;K4N8&quot;&gt;Вот вы и умножаете важные различия на большой коэффициент, а неважные — на крошечный. Это называется взвешенным расстоянием.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;c4y1&quot;&gt;В теории кодирования и связи разным битам информации присваивают разные веса: ошибка в бите, отвечающем за знак числа (+ или -), гораздо критичнее, чем ошибка в последнем знаке после запятой. Взвешивание помогает алгоритмам защищать самые важные участки данных.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;7_rasstoyanie_heemminga_hamming_distance&quot;&gt;7. Расстояние Хэмминга / Hamming Distance&lt;/h2&gt;
  &lt;p id=&quot;GJCy&quot;&gt;Перейдем от чисел к тексту. Вы переписываетесь в чате и делаете страшную опечатку: вместо слова «извИните» отправляете «извЕните». К счастью, ваш собеседник не граммар-наци и без проблем понимает вас. Почему?&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;Mn3w&quot;&gt;Потому что между этими словами &lt;strong&gt;расстояние Хэмминга &lt;/strong&gt;равно единице — ровно в одной позиции буквы различаются. Расстояние Хэмминга считает количество несовпадающих символов в двух строках одинаковой длины. Мы часто это видим в «кошках» типа Trados/Smartcat, когда вызываем функцию Concordance.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;KKjb&quot;&gt;Это база в теории кодирования: когда файлы летят по интернету, из-за помех «10110» может превратиться в «10010». Посчитав расстояние Хэмминга между отправленным и полученным кодом, принимающий компьютер может рассчитать, сколько битов исказилось, и исправить ошибку.&lt;/p&gt;
  &lt;figure id=&quot;5P29&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d5/2c/d52c1192-659d-46e5-beeb-e2b194cd2a2b.png&quot; width=&quot;517&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;8_rasstoyanie_polnoii_variacii_tv_distan&quot;&gt;8. Расстояние полной вариации / TV-Distance (Total Variation Distance)&lt;/h2&gt;
  &lt;p id=&quot;7Lyb&quot;&gt;Представьте, что вы с другом по дороге на форум решили зайти в клуб игровых автоматов. У вас шансы выиграть золото, серебро или бронзу составляют [60%, 30%, 10%], а у друга — [40%, 40%, 20%]. Насколько различаются ваши игровые условия в целом?&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;kXtI&quot;&gt;&lt;strong&gt;Расстояние полной вариации &lt;/strong&gt;находит самое большое возможное различие в вероятностях для одного и того же события (в нашем случае — для золота: 60% - 40% = 20%). Это самый простой и строгий способ сказать: «&lt;strong&gt;Два этих случайных процесса отличаются максимум на столько-то процентов&lt;/strong&gt;».&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;9_rasstoyanie_hellingera_hellinger_dist&quot;&gt;9. Расстояние Хеллингера / Hellinger Distance&lt;/h2&gt;
  &lt;p id=&quot;tHao&quot;&gt;Иногда нам нужно сравнить два вероятностных распределения, но так, чтобы редкие события не ломали математику, а само расстояние всегда красиво укладывалось в рамки от 0 до 1 (где 0 — близнецы, 1 — вообще не пересекаются). Для этого ученые берут квадратные корни из вероятностей и считают между ними аналог евклидова расстояния. Это и есть &lt;strong&gt;расстояние Хеллингера&lt;/strong&gt;.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;fw4y&quot;&gt;Если менее абстрактно, то представьте, что вы с другом сравниваете свои библиотеки по жанрам. Если вы читаете только иронические детективы, а он — только Стругацких и Лема, то &lt;strong&gt;расстояние Хеллингера&lt;/strong&gt; будет равно 1.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;3y1D&quot;&gt;В теории информации это расстояние используется в системах распознавания речи или текста, где нужно быстро понять, похожи ли два случайных подбора слов друг на друга.&lt;/p&gt;
  &lt;h2 id=&quot;10_rasstoyanie_bhattachari_bhattacharyy&quot;&gt;10. Расстояние Бхаттачарьи / Bhattacharyya Distance&lt;/h2&gt;
  &lt;p id=&quot;YXxX&quot;&gt;Это близкий родственник расстояния Хеллингера, но он скорее оценивает &lt;em&gt;степень наложения&lt;/em&gt; (overlap).&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;GSTO&quot;&gt;Например, врач-диагност сравнивает графики распределения плотности тканей на снимках здорового органа и органа с патологией. &lt;strong&gt;Расстояние Бхаттачарьи&lt;/strong&gt; покажет, насколько эти графики пересекаются.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;WRkn&quot;&gt;Если графики распределения вероятностей вообще не пересекаются, это расстояние уходит в &lt;em&gt;бесконечность&lt;/em&gt;. &lt;/p&gt;
  &lt;p id=&quot;87MD&quot;&gt;В теории информации и компьютерном зрении его используют, чтобы оценить, насколько легко алгоритм может перепутать два класса объектов (например, кошку и собаку на размытом фото).&lt;/p&gt;
  &lt;h2 id=&quot;11_divergenciya_distanciya_rashojdeniya&quot;&gt;11. Дивергенция (Дистанция расхождения) / Divergence Distance&lt;/h2&gt;
  &lt;p id=&quot;YnNN&quot;&gt;Отойдем немного в сторону непривычных вещей. Дело в том, что в теории информации часто нужны «расстояния», которые не работают как обычная линейка.&lt;/p&gt;
  &lt;p id=&quot;OHzF&quot;&gt;Мы привыкли, что от головы удава до хвоста удава расстояние будет тем же, что и от хвоста до головы — 38 попугаев. &lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;UjbR&quot;&gt;В дивергенциях путь туда и путь обратно — это &lt;strong&gt;разные вещи&lt;/strong&gt; (т. е. нарушается закон симметрии), поэтому слово «расстояние» тут частенько пишется в кавычках.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;t2Y0&quot;&gt;Возьмем, скажем, дистанцию между ошибками. Если вы переводите незнакомую тему, то ошибиться в термине, котором вы видите впервые — это нормально. Но ошибиться в термине из темы, с которой ты работаешь 30 лет — это катастрофа.&lt;/p&gt;
  &lt;p id=&quot;iAxn&quot;&gt;Другой пример, из медицины. Принять редкое заболевание за обычную простуду при анализе симптомов — это огромная статистическая ошибка. Принять простуду за редкое заболевание — ошибка другого масштаба и стоимости. &lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;sbEL&quot;&gt;Дивергенции учитывают эту &lt;em&gt;асимметрию &lt;/em&gt;направления и &lt;em&gt;расстояние &lt;/em&gt;расхождения между реальностью и нашими представлениями.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;12_rasstoyanie_kylbaka__leiiblera_kl_d&quot;&gt;12. Расстояние Кульбака — Лейблера / KL Distance (Kullback–Leibler Divergence&lt;/h2&gt;
  &lt;p id=&quot;yIHM&quot;&gt;Самый знаменитый пример дивергенции и самый частый гость в ИТ-текстах про нейросети и LLM (включая ChatGPT). Представьте, что вы решили закодировать все свои сообщения другу, используя короткие символы для частых слов (типа «привет», «как») и длинные для редких. Но вы взяли статистику частоты слов из словаря Даля, а не из Тик-тока.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;5OMg&quot;&gt;&lt;strong&gt;Расстояние Кульбака-Лейблера&lt;/strong&gt; (KL-дивергенция) измерит, сколько &lt;em&gt;лишних битов&lt;/em&gt; вы потратите на передачу сообщений из-за того, что ваша модель представлений о языке не совпадает с истинным распределением слов у молодежи (Тик-ток).&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;P26l&quot;&gt;Она показывает плату за наше незнание или неточную оценку вероятностей.&lt;/p&gt;
  &lt;figure id=&quot;pzQJ&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/83/4c/834cc625-1f15-4fe1-995e-be1dabd94835.png&quot; width=&quot;554&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;13_minimalnoe_rasstoyanie_yatrakosa_yat&quot;&gt;13. Минимальное расстояние Ятракоса . Yatracos’ Minimum-Distance&lt;/h2&gt;
  &lt;p id=&quot;MLho&quot;&gt;Представьте, что перед вами кусок текста со случайными словами, и вы пытаетесь угадать, по какому закону они там перемешиваются/складываются. У вас есть несколько математических моделей-кандидатов. Как выбрать лучшую, если слов слишком много и они слишком запутанные?&lt;/p&gt;
  &lt;p id=&quot;iKzk&quot;&gt;Или же у вас есть огромный массив хаотичных медицинских данных (например, показатели тысяч датчиков реанимации) и вам нужно подобрать для них идеальную математическую модель, стандартные методы могут давать сбой из-за &lt;em&gt;тяжелых хвостов&lt;/em&gt; распределений.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;lHJn&quot;&gt;&lt;strong&gt;Минимальное расстояние Ятракоса&lt;/strong&gt; — это специальный инструмент для подбора идеальной модели. Оно строит особую коллекцию «тестовых вопросов» (кубиков данных) и смотрит, какая из ваших моделей дает &lt;em&gt;наименьшее &lt;/em&gt;отклонение от реальности (эмпирических данных) по всем тестам одновременно.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;14_minimalnoe_rasstoyanie_fitingofa_fi&quot;&gt;14. Минимальное расстояние Фитингофа / Fitingof Minimal Distance&lt;/h2&gt;
  &lt;p id=&quot;aDRw&quot;&gt;Этот термин из мира сжатия данных и архиваторов (вроде ZIP, RAR и т. п.). Представьте, что вы набрали на клавиатуре случайный набор букв «АААБББААА». Насколько этот текст далек от абсолютно хаотичного, несжимаемого шума?&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;8GlC&quot;&gt;&lt;strong&gt;Расстояние Фитингофа&lt;/strong&gt; измеряет, насколько структура вашего сообщения отличается от идеального «хаоса» (энтропии). По сути, оно показывает, можно ли сжать этот текст сильнее, найдя в нем скрытые закономерности и повторяющиеся паттерны, о которых вы —создатель текста — даже не задумывались.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;15_rasstoyanie_vassershteiina_metrika_z&quot;&gt;15. Расстояние Вассерштейна (Метрика «землекопа») / Wasserstein Distance (Earth Mover&amp;#x27;s Distance)&lt;/h2&gt;
  &lt;p id=&quot;aPC8&quot;&gt;Представьте, что во дворе насыпаны кучи песка, а вам нужно пересыпать их так, чтобы получился песчаный замок определенной формы. Каждую песчинку нужно перенести на какое-то расстояние. Вы по природе ленивы и не хотите делать эту работу или сделать с минимальными усилиями. Варианта не делать у вас нет, а чтобы сделать с минимальными усилиями, надо умножить массу песка на дистанцию переноса.&lt;/p&gt;
  &lt;figure id=&quot;hQxQ&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/65/16/6516d75e-b7af-4bf5-9bbb-64a21e7f3d04.png&quot; width=&quot;522&quot; /&gt;
  &lt;/figure&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;Hp0O&quot;&gt;Это и есть &lt;strong&gt;расстояние Вассерштейна&lt;/strong&gt; (в английском языке у этого термина более народный синоним — &lt;em&gt;Earth Mover&amp;#x27;s Distance&lt;/em&gt; — дистанция землекопа).&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;aslv&quot;&gt;В более научном применении, можно представить, что у вас есть одно распределение пикселей (скан МРТ пациента год назад) и второе распределение (скан сегодня). За год ткани немного сместились, опухоль уменьшилась. Обычные метрики скажут, что картинки «абсолютно разные», потому что пиксели не совпадают один в один. &lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;T5O3&quot;&gt;Поэтому в ИТ-медицине это это расстояние используется, чтобы проанализировать старый снимок органа с новым, учитывая деформацию.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;16_rasstoyanie_sinkhorna_sinkhorn_dista&quot;&gt;16. Расстояние Синкхорна / Sinkhorn Distance&lt;/h2&gt;
  &lt;p id=&quot;TTJC&quot;&gt;Посчитать классическое расстояние Вассерштейна для миллионов пикселей в современных нейросетях — это адски долгая и дорогая по ресурсам задача, компьютер просто «задымится» и «прожжет» дыру в вашем бюджете.&lt;/p&gt;
  &lt;p id=&quot;Q7XU&quot;&gt;Чтобы решить эту проблему, ученые добавили в алгоритм Вассерштейна капельку случайного хаоса (&lt;em&gt;энтропии&lt;/em&gt;), которая разрешает песчинкам лететь чуть-чуть неидеально, зато супербыстро.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;VOiV&quot;&gt;Это сглаженное и гораздо более быстро вычисляемое расстояние называется &lt;strong&gt;расстоянием Синкхорна&lt;/strong&gt;.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;2wSz&quot;&gt;Именно благодаря ему алгоритмы в машинном обучении могут довольно быстро, нередко в реальном времени, сопоставлять огромные массивы информации, переводить тексты и обрабатывать графику. Например, с его помощью сопоставляются белки, ищутся молекулы-кандидаты для лекарств и алайнятся многоязычные параллельные тексты.&lt;/p&gt;
  &lt;h2 id=&quot;17_rasstoyanie_ornshteiina_ornsteins_dis&quot;&gt;17. Расстояние Орнштейна / Ornstein’s Distance (d-bar distance)&lt;/h2&gt;
  &lt;p id=&quot;DT87&quot;&gt;Теперь усложним задачу для алгоритма Вассерштейна. Представьте, что вы сравниваете не просто статичные кучи песка, а целые кинофильмы или непрерывные кардиограммы, где буквы/символы/данные зависят друг от друга (например, в английском после Q часто идет U). Как измерить разницу между двумя такими &lt;em&gt;процессами во времени&lt;/em&gt;?&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;prRD&quot;&gt;&lt;strong&gt;Расстояние Орнштейна&lt;/strong&gt; (или &lt;strong&gt;d-метрика&lt;/strong&gt;) — это способ применить идею «минимальной переделки» к случайным последовательностям. Оно определяет, какой минимальный процент букв в длинном изменяющемся потоке данных нужно перезаписать, чтобы один случайный процесс стал неотличим от другого с сохранением всей его внутренней структуры и зависимостей.&lt;/p&gt;
  &lt;/section&gt;
  &lt;h2 id=&quot;gQmz&quot;&gt;&lt;strong&gt;18. Расстояние Ле Кама / Le Cam Distance&lt;/strong&gt;&lt;/h2&gt;
  &lt;p id=&quot;4KZV&quot;&gt;Представьте, что у вас есть два разных дизайна клинических исследований лекарства, которые работают со случайными выборками пациентов. &lt;strong&gt;Расстояние Ле Кама&lt;/strong&gt; измеряет дистанцию между самими этими &lt;em&gt;экспериментами&lt;/em&gt;.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(323, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;YzUz&quot;&gt;Если расстояние Ле Кама между двумя моделями экспериментов близко к нулю, это значит, что они содержат одинаковое количество полезной информации и следовательно — &lt;em&gt;асимптотически эквивалентны&lt;/em&gt;.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;Fn2b&quot;&gt;На практике это означает математический потолок: какой бы крутой алгоритм вы ни создали, вы не сможете выжать из данных второго эксперимента больше информации, чем из первого.&lt;/p&gt;
  &lt;p id=&quot;LWvF&quot;&gt;Иными словами, это расстояние показывает границы нашей способности познавать мир по &lt;em&gt;неполным &lt;/em&gt;данным.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;XpON&quot;&gt;Можно сделать вывод, что термин «distance» в ИТ и теории информации — это не физическое «расстояние», а &lt;strong&gt;мера различия, ошибки или информационного расхождения&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;tJNj&quot;&gt;Спасибо за внимание! Надеюсь было познавательно.&lt;/p&gt;
  &lt;p id=&quot;96HX&quot;&gt;~&lt;/p&gt;

</content></entry><entry><id>bartov:ang0JXWaKXQ5SNby</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/ang0JXWaKXQ5SNby?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Про локализацию нерелевантных данных</title><published>2026-09-21T19:17:19.398Z</published><updated>2026-09-21T19:17:19.398Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/c6/45/c6453cbf-c951-47b5-8ec7-1f871ea0354b.png"></media:thumbnail><category term="other" label="Other"></category><tt:hashtag>локализация</tt:hashtag><summary type="html">&lt;img src=&quot;https://img3.teletype.in/files/24/6a/246a6093-75bc-4c0e-84e2-c93e1d4edb48.png&quot;&gt;В книге про программы-вымогатели, перевод которой уже близится к концу, я был вынужден прибегнуть к одному приему, который многим коллегам-переводчикам может показаться спорным.</summary><content type="html">
  &lt;tt-tags id=&quot;6KVX&quot;&gt;
    &lt;tt-tag name=&quot;локализация&quot;&gt;#локализация&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;
  &lt;p id=&quot;pMWk&quot;&gt;В книге про программы-вымогатели, перевод которой уже близится к концу, я был вынужден прибегнуть к одному приему, который многим коллегам-переводчикам может показаться спорным.&lt;/p&gt;
  &lt;p id=&quot;4Ywz&quot;&gt;И тем не менее, учитывая склонность автора к постоянным повторам одних и тех же мыслей (типичная черта американских авторов), мне приходится то там, то сям как-то урезать эту «болтовню».&lt;/p&gt;
  &lt;figure id=&quot;IvSV&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/24/6a/246a6093-75bc-4c0e-84e2-c93e1d4edb48.png&quot; width=&quot;409&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;r8H6&quot;&gt;Речь о перечислении локальных организаций, которые могут помочь бизнесу в восстановлении после атак или в противодействии им. Автор явно рассчитывал свою книгу на аудиторию западных стран, Россию там он в качестве целевой аудитории не рассматривал (более того, там страной происхождения почти всех программ-вымогателей указана Россия, хорошо, хоть «Осью Зла» нас не называет), поэтому он часто эти организации перечисляет. Вот как это выглядит:&lt;/p&gt;
  &lt;figure id=&quot;NJlq&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/62/73/62731af4-80f2-497f-beeb-684c5a9c7263.png&quot; width=&quot;768&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;UoxL&quot;&gt;или так:&lt;/p&gt;
  &lt;figure id=&quot;hEyS&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/47/b2/47b2d23a-5424-4ebf-ac6f-5f9d913ed125.png&quot; width=&quot;916&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Xw9b&quot;&gt;или так:&lt;/p&gt;
  &lt;figure id=&quot;H4QE&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/e7/11/e711c63c-8ad8-4a28-b72c-eb9651bd391f.png&quot; width=&quot;901&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;tCde&quot;&gt;Поскольку эти организации россиянам явно помогать не будут, да и в целом они не особо релевантны для целевой аудитории, я принял решение переводить подобные заходы примерно так (это перевод последнего примера):&lt;/p&gt;
  &lt;blockquote id=&quot;vHaV&quot;&gt;Малый бизнес в США может обратиться в CISA за бесплатной поддержкой через Cloud Hygiene Scanner (888-863-8656). Аналогичные бесплатные инструменты и консультации есть в Великобритании (NCSC), Австралии (ACSC) и ЕС (ENISA).&lt;/blockquote&gt;
  &lt;p id=&quot;ZoPS&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;6AjW&quot;&gt;В таких текстах переводчику приходится выжимать «воду», но делать это нужно внимательно и  после отмашки заказчика или редактора, чтобы избежать претензий за «неполный перевод».&lt;/p&gt;
  &lt;p id=&quot;AMiR&quot;&gt;Спасибо за внимание! Надеюсь было познавательно.&lt;/p&gt;
  &lt;p id=&quot;j0JD&quot;&gt;~&lt;/p&gt;

</content></entry><entry><id>bartov:andRVVEk-R0SiwzT</id><link rel="alternate" type="text/html" href="https://teletype.in/@bartov/andRVVEk-R0SiwzT?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=bartov"></link><title>Про избыточную локализацию</title><published>2026-09-21T18:40:48.965Z</published><updated>2026-09-21T19:13:16.919Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/56/3f/563fd595-971d-4128-9a4c-1dcd3e6471bc.png"></media:thumbnail><category term="other" label="Other"></category><tt:hashtag>заметки_на_полях</tt:hashtag><tt:hashtag>локализация</tt:hashtag><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/bf/dc/bfdc5ca8-2248-4067-be8b-0ad1cb28940b.png&quot;&gt;В теории перевода есть такой критерий качества перевода/локализации — перевод должен вызывать те же эмоции, ощущения и образы у читателя, что и оригинал. Так получилось, что в некоторых ситуациях эту рекомендацию можно считать вредной. Поясню.</summary><content type="html">
  &lt;tt-tags id=&quot;l6Ob&quot;&gt;
    &lt;tt-tag name=&quot;заметки_на_полях&quot;&gt;#заметки_на_полях&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;
  &lt;tt-tags id=&quot;1vPM&quot;&gt;
    &lt;tt-tag name=&quot;локализация&quot;&gt;#локализация&lt;/tt-tag&gt;
  &lt;/tt-tags&gt;
  &lt;p id=&quot;v9c0&quot;&gt;В теории перевода есть такой критерий качества перевода/локализации — перевод должен вызывать те же эмоции, ощущения и образы у читателя, что и оригинал. Так получилось, что в некоторых ситуациях эту рекомендацию можно считать вредной. Поясню.&lt;/p&gt;
  &lt;p id=&quot;IJSt&quot;&gt;В силу профдеформации я часто обращаю внимание на локализацию в переводах. Недавно дочь купила книгу «Куколки» (оригинал: «Chrysalids» / Джон Уиндем) — про постапокалипсис, я такое люблю.&lt;/p&gt;
  &lt;p id=&quot;XTYW&quot;&gt;Да, сразу оговорюсь: я ни в коем разе не критикую тут переводчиков. Перевод книг — это весьма каторжное дело, и сделать перевод совершенно без ошибок — задача из области фантастики (по моему убеждению, безошибочных текстов вообще не бывает, бывают недопроверенные). Отвлекся, продолжаю.&lt;/p&gt;
  &lt;figure id=&quot;P95n&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/bf/dc/bfdc5ca8-2248-4067-be8b-0ad1cb28940b.png&quot; width=&quot;800&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;G7IG&quot;&gt;Почему издательство выбрало такое название - для меня загадка, ничего похожего ни по идее, ни по форме на куклы или личинки насекомых там нет. Но локализация названий - это отдельное большое болото, туда я не полезу.&lt;/p&gt;
  &lt;p id=&quot;do6Q&quot;&gt;Зато в одном фрагменте мой взгляд зацепился за довольно странный выбор имен:&lt;/p&gt;
  &lt;blockquote id=&quot;EW2N&quot;&gt;Снова появилась миссис Уэндер и увела Софи в другую комнату. Когда мистер Уэндер зашел за тюками в следующий раз, я двинулся за ним.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;sCJ5&quot;&gt;Две лошади, Пятнышко и Рыжик, терпеливо стояли около дома, часть вещей уже была навьючена на них.&lt;/blockquote&gt;
  &lt;p id=&quot;p6JD&quot;&gt;На мой взгляд здесь локализация получилась не очень удачно. Чтобы лучше пояснить свои соображения, приведу цитату из книги Корнея Чуковского (он не только для детей писал книжки, но и для переводчиков):&lt;/p&gt;
  &lt;blockquote id=&quot;XvRi&quot;&gt;Переводчица Диккенса М.А. Шишмарева напрасно влагает в уста англичанам русские простонародные слова. Странно читать, как британские джентльмены и леди говорят друг другу: «И мы не лыком шиты…», «Батюшки!», «Пропала моя головушка!», «Тю-тю!» И даже (незаметно для себя) цитируют стихи Грибоедова, вряд ли очень популярные в Англии: «Я это делаю с толком, с чувством, с расстановкой».&lt;/blockquote&gt;
  &lt;blockquote id=&quot;9YDv&quot;&gt;Когда герои Диккенса поют: «Иппи-дол-ли-дол, иппи-дол-ли-дол, иппи-ди», – Е.Г. Бекетова переводит: «Ай люли! ай люли! Разлюлюшеньки мои».&lt;/blockquote&gt;
  &lt;p id=&quot;1enS&quot;&gt;Здесь я увидел похожую ситуацию: имена лошадей локализованы — &amp;quot;Пятнышко&amp;quot; (Spot) и &amp;quot;Рыжик&amp;quot; (Sandy) — вряд ли их так англичане называют, но при этом имя девочки нет (она же Софи, а не София). Если так хочется сказать по раскраску лошадей, можно локализовать в духе пятнистый Спот и рыжая Сэнди. Но в книге они упоминаются один раз, поэтому можно было бы вообще их не переводить (просто сказать - две лошади) или пренебречь их окраской (она тоже никак на сюжет не влияет).&lt;/p&gt;
  &lt;figure id=&quot;2CZc&quot; class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/43/98/43980978-f7cf-4bc7-90b6-6391cd12c532.png&quot; width=&quot;768&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;XeAD&quot;&gt;В другой переведенной книге этого же автора «Кукушки Мидвича»/«Кукушата Мидвича» (The Midwich Cuckoos) мое внимание привлекло еще одно несколько странное предложение:&lt;/p&gt;
  &lt;blockquote id=&quot;CIEq&quot;&gt;Вот наша убыль: мистер Уильям Транк, батрак, его жена и ребенок, сгоревшие вместе со своим домом; пожилая чета Стигфилдов, также погибшая в результате пожара; еще один батрак, Герберт Флэгг, найден умершим от переохлаждения...&lt;/blockquote&gt;
  &lt;p id=&quot;6FeQ&quot;&gt;Простите, кто? Батраки? Они-то откуда в Великобритании взялись? Это чисто российская/советская реалия. С таким же успехом можно и фермера колхозником называть. В оригинале &lt;strong&gt;farm-hand&lt;/strong&gt; - работник фермы.&lt;/p&gt;
  &lt;p id=&quot;xdjC&quot;&gt;&lt;em&gt;Продолжение следует. &lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;TJxD&quot;&gt;P.S. Кстати, видите странность в локализации этой фразы? Какую? Подсказка: пропущенный перевод &lt;em&gt;police cars &lt;/em&gt;я вижу, но я не про него сейчас, тут нюанс поинтереснее.&lt;/p&gt;
  &lt;blockquote id=&quot;EAJe&quot;&gt;On both the Oppley and Stouch roads there was a great starting up and warming of engines. Presently two streams of ambulances, fire appliances, police cars, jeeps, and military trucks started to converge on Midwich.&lt;/blockquote&gt;
  &lt;blockquote id=&quot;zOoT&quot;&gt;На дорогах в Оппли и Стауч громко ревели моторы — их прогревали. Двумя потоками в Мидвич вливались машины «скорой», пожарные машины, джипы и военные грузовики.&lt;/blockquote&gt;
  &lt;p id=&quot;jglY&quot;&gt;Спасибо за внимание! Надеюсь было познавательно.&lt;/p&gt;
  &lt;p id=&quot;pjmj&quot;&gt;~&lt;/p&gt;

</content></entry></feed>