<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrew Markhai</title>
    <description>The latest articles on DEV Community by Andrew Markhai (@andrew_markhai_27ffd3a6b8).</description>
    <link>https://dev.to/andrew_markhai_27ffd3a6b8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2185957%2F8dd609fd-2628-446d-8eb3-96b9624efb4e.png</url>
      <title>DEV Community: Andrew Markhai</title>
      <link>https://dev.to/andrew_markhai_27ffd3a6b8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andrew_markhai_27ffd3a6b8"/>
    <language>en</language>
    <item>
      <title>AI-ассистент для документации из wiki Яндекса с использованием RAG и LangChain</title>
      <dc:creator>Andrew Markhai</dc:creator>
      <pubDate>Sun, 26 Oct 2025 16:40:33 +0000</pubDate>
      <link>https://dev.to/andrew_markhai_27ffd3a6b8/ai-assistient-dlia-dokumientatsii-iz-wiki-iandieksa-s-ispolzovaniiem-rag-i-langchain-1169</link>
      <guid>https://dev.to/andrew_markhai_27ffd3a6b8/ai-assistient-dlia-dokumientatsii-iz-wiki-iandieksa-s-ispolzovaniiem-rag-i-langchain-1169</guid>
      <description>&lt;p&gt;Собрал &lt;a href="https://github.com/amarkhai/yandex-wiki-ai-assistant" rel="noopener noreferrer"&gt;небольшой прототип&lt;/a&gt; чат-ассистента, который умеет отвечать на вопросы по внутренней документации.&lt;/p&gt;

&lt;p&gt;Под капотом — классическая схема Retrieval-Augmented Generation (RAG): данные хранятся в векторной базе, а при каждом запросе к OpenAI подмешивается контекст из релевантных документов.&lt;/p&gt;

&lt;p&gt;Проект заточен под wiki Яндекса, но если заменить парсер, можно использовать для любой другой базы знаний.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Как работает:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Парсинг документации — скрипт вытягивает нужные страницы из wiki и сохраняет их в .md.&lt;/li&gt;
&lt;li&gt;Ингест — Markdown-файлы превращаются в векторы (через Sentence-Transformers или OpenAI embeddings) и индексируются в FAISS.&lt;/li&gt;
&lt;li&gt;RAG-агент — при запросе ищет релевантные куски текста, добавляет их в промпт и отправляет в OpenAI API.&lt;/li&gt;
&lt;li&gt;Всё это обёрнуто в простой CLI-интерфейс, можно общаться с ботом прямо из терминала.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Что умеет:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Отвечает на вопросы по документации&lt;/li&gt;
&lt;li&gt;Находит и цитирует источники (&lt;a href="https://wiki.yandex.ru/.." rel="noopener noreferrer"&gt;https://wiki.yandex.ru/..&lt;/a&gt;.)&lt;/li&gt;
&lt;li&gt;Поддерживает русский язык (вопросы и ответы)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Как запустить расписывать здесь не буду, есть подробное README.md в репозитории.&lt;/p&gt;

&lt;p&gt;Полезность сомнительная, но для экспериментов с RAG и векторными базами — пойдет.&lt;/p&gt;

&lt;p&gt;Ну и, конечно, почти весь код написал ChatGPT, я только немного подкрутил под свои нужды.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>langchain</category>
      <category>yandexwiki</category>
      <category>ai</category>
    </item>
    <item>
      <title>PHP Traceroute</title>
      <dc:creator>Andrew Markhai</dc:creator>
      <pubDate>Wed, 13 Aug 2025 18:46:10 +0000</pubDate>
      <link>https://dev.to/andrew_markhai_27ffd3a6b8/php-traceroute-216i</link>
      <guid>https://dev.to/andrew_markhai_27ffd3a6b8/php-traceroute-216i</guid>
      <description>&lt;p&gt;A simple traceroute implementation in PHP, inspired by the article &lt;a href="https://www.slashroot.in/how-does-traceroute-work-and-examples-using-traceroute-command#google_vignette" rel="noopener noreferrer"&gt;How Does Traceroute Work&lt;/a&gt;.&lt;br&gt;
Result here: &lt;a href="https://github.com/amarkhai/php-traceroute" rel="noopener noreferrer"&gt;php-traceroute&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works&lt;/strong&gt;&lt;br&gt;
The idea is straightforward — send ICMP packets with increasing TTL values until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We receive a reply from the target IP, or&lt;/li&gt;
&lt;li&gt;The TTL reaches its maximum.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, the script supports two parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;host&lt;/code&gt; (required) — target hostname or IP&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max-hops&lt;/code&gt; (optional) — maximum TTL, defaults to &lt;code&gt;256&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Known Issues &amp;amp; Open Questions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Timeout at TTL=2&lt;/strong&gt;&lt;br&gt;
There’s a noticeable delay after sending a packet with &lt;code&gt;TTL=2&lt;/code&gt;. In Wireshark, I see no incoming data until an NBNS packet with &lt;code&gt;Registration NB SMB_NSCHECK&lt;/code&gt; appears. Cause unknown.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why UDP in standard traceroute?&lt;/strong&gt;&lt;br&gt;
Regular traceroute sends UDP packets, but UDP datagrams don’t carry TTL info themselves. How does TTL handling work in that case?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Notes &amp;amp; Implementation Details&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Packet inspection&lt;/strong&gt;
Used Wireshark to confirm packet similarity with standard traceroute.
Initially tried capturing only Docker container traffic to avoid noise, but &lt;code&gt;getprotobyname()&lt;/code&gt; failed inside the container (missing &lt;code&gt;/etc/protocols&lt;/code&gt;). Instead, I ran everything locally and filtered traffic with:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;udp or icmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Socket options&lt;/strong&gt;&lt;br&gt;
All &lt;code&gt;socket_set_option&lt;/code&gt; parameters were found via Google. Network programming in PHP isn’t very common, so examples are scarce.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CLI handling&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;symfony/console&lt;/code&gt; is optional — I just didn’t want to deal with raw input/output.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified packet count&lt;/strong&gt;&lt;br&gt;
Only one ICMP packet is sent per hop (instead of the usual three), so the output shows a single time in milliseconds.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example Usage&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php traceroute.php example.com --max-hops=30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script works, so feel free to try it out for fun — just don’t expect production-grade traceroute magic.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How Symfony Flex Works</title>
      <dc:creator>Andrew Markhai</dc:creator>
      <pubDate>Sun, 23 Mar 2025 15:46:10 +0000</pubDate>
      <link>https://dev.to/andrew_markhai_27ffd3a6b8/how-symfony-flex-works-4d62</link>
      <guid>https://dev.to/andrew_markhai_27ffd3a6b8/how-symfony-flex-works-4d62</guid>
      <description>&lt;h2&gt;
  
  
  What is it?
&lt;/h2&gt;

&lt;p&gt;Symfony Flex is a Composer plugin from Symfony that handles the configuration of installed packages. It adds configuration files, updates .gitignore and .env, registers bundles, and more.&lt;/p&gt;

&lt;p&gt;For example, when installing PHPUnit, Flex will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a default configuration file phpunit.dist.xml&lt;/li&gt;
&lt;li&gt;Create tests/bootstrap.php&lt;/li&gt;
&lt;li&gt;Add /phpunit.xml and /.phpunit.cache/ to .gitignore&lt;/li&gt;
&lt;li&gt;Create .env.test with environment variables for tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where does Flex get its instructions?
&lt;/h2&gt;

&lt;p&gt;In Flex terminology, these instructions are called &lt;strong&gt;recipes&lt;/strong&gt;. They are stored in a GitHub &lt;a href="https://github.com/symfony/recipes" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. You can check out the recipes for the current version of PHPUnit &lt;a href="https://github.com/symfony/recipes/tree/main/phpunit/phpunit/10.0" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this a complete list of recipes?
&lt;/h2&gt;

&lt;p&gt;No. The symfony/recipes repository is maintained by Symfony itself, while community-contributed recipes are stored in a separate &lt;a href="https://github.com/symfony/recipes-contrib" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. Anyone can contribute there.&lt;/p&gt;

&lt;p&gt;To allow Flex to apply community recipes, you need to add the following to composer.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"extra"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"symfony"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"allow-contrib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How does it work under the hood?
&lt;/h2&gt;

&lt;p&gt;When initialized, Flex loads links to all current recipes in &lt;a href="https://github.com/symfony/flex/blob/2.x/src/Downloader.php#L47" rel="noopener noreferrer"&gt;Downloader::index&lt;/a&gt;. By default, it fetches them from &lt;a href="https://github.com/symfony/flex/blob/2.x/src/Downloader.php#L31C1-L34C7" rel="noopener noreferrer"&gt;two repositories&lt;/a&gt; described above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_ENDPOINTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="s1"&gt;'https://raw.githubusercontent.com/symfony/recipes/flex/main/index.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="s1"&gt;'https://raw.githubusercontent.com/symfony/recipes-contrib/flex/main/index.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, you can add your own repositories by specifying them in composer.json under extra.symfony.endpoint.&lt;/p&gt;

&lt;p&gt;Since Flex is a &lt;a href="https://getcomposer.org/doc/articles/plugins.md" rel="noopener noreferrer"&gt;Composer plugin&lt;/a&gt;, it listens for various &lt;a href="https://github.com/symfony/flex/blob/2.x/src/Flex.php#L847" rel="noopener noreferrer"&gt;events&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;PackageEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;POST_PACKAGE_UPDATE&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'enableThanksReminder'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;PackageEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;POST_PACKAGE_INSTALL&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'recordFlexInstall'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;PackageEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;POST_PACKAGE_UNINSTALL&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'record'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;InstallerEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PRE_OPERATIONS_EXEC&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'recordOperations'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;PluginEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PRE_POOL_CREATE&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'truncatePackages'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;ScriptEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;POST_CREATE_PROJECT_CMD&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'configureProject'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;ScriptEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;POST_INSTALL_CMD&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'install'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;ScriptEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PRE_UPDATE_CMD&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'configureInstaller'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;ScriptEvents&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;POST_UPDATE_CMD&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'auto-scripts'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'executeAutoScripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During install and update commands, the &lt;a href="https://github.com/symfony/flex/blob/2.x/src/Flex.php#L357" rel="noopener noreferrer"&gt;Flex::install()&lt;/a&gt; method runs. It checks whether there are recipes for the current package by looking at both the package name and its version.&lt;/p&gt;

&lt;p&gt;Recipe versioning is handled using subdirectories inside the package directory. For example, looking at PHPUnit’s recipes, you’ll see &lt;a href="https://github.com/symfony/recipes/tree/main/phpunit/phpunit" rel="noopener noreferrer"&gt;folders&lt;/a&gt; for versions 10.0, 9.6, 9.3, and 4.7. Flex selects the highest available recipe version that is less than or equal to the installed package version.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHPUnit 12 will use the 10.0 recipe&lt;/li&gt;
&lt;li&gt;PHPUnit 9.9 will use the 9.6 recipe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a recipe is found, the instructions in its manifest are executed using various &lt;a href="https://github.com/symfony/flex/tree/2.x/src/Configurator" rel="noopener noreferrer"&gt;configurators&lt;/a&gt;. The console will display a message like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Symfony operations: 1 recipe &lt;span class="o"&gt;(&lt;/span&gt;13c8b05b36f03f13c596b688cb97d0da&lt;span class="o"&gt;)&lt;/span&gt;
  - Configuring phpunit/phpunit &lt;span class="o"&gt;(&amp;gt;=&lt;/span&gt;10.0&lt;span class="o"&gt;)&lt;/span&gt;: From github.com/symfony/recipes:main

Some files have been created and/or updated to configure your new packages.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Useful commands
&lt;/h2&gt;

&lt;p&gt;To check the current state of recipes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer symfony:recipes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a package has an updated recipe (marked as update available), you can update it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer recipes:update vendor/package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>symfony</category>
      <category>php</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
