Дисклеймер: Автор признаёт, что приведённый ниже код может быть далёк от идеала. Его цель — показать, как легко создать подобную утилиту своими руками. Также автор не исключает, что при решении отдельных задач с кодом обращался за помощью к нейросетям.

Я — .NET-разработчик, и уже некоторое время прохожу собеседования. Иногда на них задают вопросы про такие «пыльные уголки» .NET, что если я и читал про них, то лишь в самом начале своего пути, листая книги Рихтера, Троелсена и Шилдта.

А ещё встречаются откровенно странные задачи, далёкие от реальной работы.

Зачем всё это? Очевидно, чтобы отсечь новичков из онлайн-курсов и тех, кто «вкатился» в IT без должной подготовки.

Ну а раз на собеседовании всё изначально настроено так сказать «враждебно», и интервьюер подозревает, что каждый мухлюет… почему бы действительно не начать мухлевать?

Сначала я полез в интернет в поисках готового решения и наткнулся на WiseWhisper.

Эта утилита создаёт полупрозрачный оверлей, слушает звуки с ПК и подсказывает ответы в реальном времени. Звучит идеально… но стоит $29 в месяц. Довольно дорого, согласитесь, а бесплатная версия предлагает всего 10 подсказок.

Тогда я подумал: а почему бы не написать своё решение?

Так началось моё R&D.

R&D

Поковыряв WiseWhisper, я понял, что программа не делает ничего сверхъестественного:

  1. Слушает звуки с ПК.

  2. Переводит их в текст.

  3. Отправляет текст в нейросеть и отображает ответ в оверлее.

Задачи вроде бы простые, но прежде чем их решать, я хотел убедиться, насколько легко скрыть окно программы от лишних глаз.

Спросил первым делом у ChatGPT — и он предоставил простой, рабочий вариант, который я тут же протестировал на WPF-приложении:

private const uint WDA_EXCLUDEFROMCAPTURE = 0x11;

[DllImport("user32.dll")]
private static extern bool SetWindowDisplayAffinity(IntPtr hWnd, uint dwAffinity);

protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);
}

Как видите, в OBS не видно окна
Как видите, в OBS не видно окна

Это, так сказать, вдохновило меня, и я принялся искать решения для следующих задач.

Работа со звуком

Для работы со звуком я выбрал NAudio.

Логика была довольно простой:

  1. Создаём WasapiLoopbackCapture.

  2. Подписываемся на событие DataAvailable, в котором записываем данные через WaveFileWriter в MemoryStream.

  3. Если звука не было в течение N времени, создаём файл .wav и передаём его дальше.

Примерно так выглядела вся инициализация:

_capture = new WasapiLoopbackCapture();

audioBuffer = new MemoryStream();
audioBufferWriter = new WaveFileWriter(_audioBuffer,
WaveFormat.CreateIeeeFloatWaveFormat(_capture.WaveFormat.SampleRate, capture.WaveFormat.Channels));
capture.StartRecording();

Проблема была в том, что винда отсылает "звук", даже когда его нету и поэтому мне пришлось написать вот такой метод для фильтрации:

private bool IsSilence(WaveInEventArgs e, float threshold = 0.0005f) { int samplesCount = e.BytesRecorded / 4; var samples = new float[samplesCount]; Buffer.BlockCopy(e.Buffer, 0, samples, 0, e.BytesRecorded); foreach (var sample in samples) { if (Math.Abs(sample) > threshold) return false; } return true; }

Речь в текст

Следующим этапом было преобразование речи в текст.

На моём ПК уже был установлен Whisper, который умеет это делать. Первым делом я попытался интегрировать его в программу через командную строку, но это оказалось не самым оптимальным вариантом: Whisper каждый раз при запросе подгружал модель, и процесс был медленным.

Решением стала библиотека Whisper.net, которая позволяет удобно и быстро взаимодействовать с моделью.

Для работы необходимо скачать модель и указать путь к ней при создании через WhisperFactory. Я использовал модель ggml-small.bin.

Примерно так выглядел весь код:

whisperProcessor = WhisperFactory.FromPath(modelPatch).CreateBuilder().WithLanguage("auto").Build(); var stringBuilder = new StringBuilder(); await using var fileStream = File.OpenRead(filePath); await foreach (var segment in whisperProcessor.ProcessAsync(fileStream)) { stringBuilder.Append(segment.Text); } return stringBuilder.ToString();

Правда, на этом этапе я столкнулся с проблемой: Whisper не поддерживает 32-битный звук. Поэтому на этапе записи аудио пришлось добавить конвертацию из 32 бит в 16 бит, примерно так:

private byte[] To16KSample() { using var memoryStream = new MemoryStream(_audioBuffer.ToArray()); using var reader = new WaveFileReader(memoryStream); using var resampler = new MediaFoundationResampler(reader, new WaveFormat(16000, 16, 1)) { ResamplerQuality = 60 }; using var ms16k = new MemoryStream(); WaveFileWriter.WriteWavFileToStream(ms16k, resampler); return ms16k.ToArray(); }

Отправка текста в нейросеть

И, наконец, заключительный этап — отправить текст в нейросеть и получить ответ.

Для этого я воспользовался Ollama и её удобной библиотекой OllamaSharp:

public void Start()
{
var http = new HttpClient
{
BaseAddress = new Uri("http://localhost:11434")
};

http.DefaultRequestHeaders.Add("x-api-key", "KEY");
client = new OllamaApiClient(http,"deepseek-v3.1:671b-cloud");
chat = new Chat(_client);
Task.Run(async () =>
{
await foreach (var in chat.SendAsync("Помоги мне с собеседованием. Отвечай коротко на вопросы"))
{
}
}).GetAwaiter().GetResult();
}

public IAsyncEnumerable<string?> AskAsync(string question)
{
return _chat.SendAsync(question);
}

Ключ можно легко и бесплатно получить на их официально сайте.

В итоге все выглядит как-то так:

SetWindowDisplayAffinity закомитил, чтобы захват был
SetWindowDisplayAffinity закомитил, чтобы захват был

Весь код проекта я пока не прикладываю — прежде хочу его немного «причесать» и сделать более аккуратным.

Комментарии (12)


  1. navferty
    03.02.2026 13:28

    А потом в комментариях видим нытьё про то, что интервьюер задаёт сложные вопросы, или предлагает вживую писать код. Не то чтобы я винил в этом автора (не он - так другой бы сделал такое же, штука довольно очевидная и спрос на неё немалый). Но это очередной шаг в борьбе "меча и щита", а интервьюерам придётся выдумывать ещё более изощрённые вопросы и способы детектировать "пассажиров" на собеседованиях.


    1. withkittens
      03.02.2026 13:28

      интервьюерам придётся выдумывать ещё более изощрённые вопросы и способы детектировать "пассажиров" на собеседованиях

      Забьют на онлайн-собеседования. Или будут просить включить вторую камеру, которая бы смотрела в монитор


    1. DazzleBizzareAdventure Автор
      03.02.2026 13:28

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


  1. octoMax
    03.02.2026 13:28

    1/ старый добрый вариант - позвать в офис и поставить у доски не работает уже?
    2/ на самом деле интересная ситуация. С одной стороны ВСЕ (за исключением особо отбитых) пользуются ИИ. Но на собесах воротят нос и требуют писать самому. Это или снобизм или идиотизм (любой другой "изм" тоже подойдет). Почему бы наоборот не поставить условие - пользуйся ИИ обязательно! Только собеседование должно быть на решение комплексной задачи и проверка должна быть именно комплексная. Если кандидат "силен" - то он решит задачу с ИИ не хуже (а скорее лучше) и уж точно быстрее. Если слаб - ну и ответ даже с ИИ будет соответствующий (а скорее всего и хуже)
    Единственная проблема тут - тот, кто задает задачки должен быть с головой и уметь оценивать ответы - а таких не очень много. Похоже ИИ вскрыл проблему о которой все давно и так знали - собеседующие такие же идиоты как и собеседуемые


    1. DazzleBizzareAdventure Автор
      03.02.2026 13:28

      Ну да. Если правильно поставить задачу на србеседовании, то нейросеть не способна дать рабочий вариант.

      Я вот когда эту пирогу делал, то задавал вопросы конкрктно про wpf, так как работал с ним лет 5 назад и не помню уже, как там все делается.

      Но я знал чего хочу: забиндить vm и настроить изменение полей.

      Поэтому нейросеть помогла мне быстро вспомнить, как это сделать.


  1. WhiteBehemoth
    03.02.2026 13:28

    Тут надо еще уметь не "спалиться"...
    Не так давно менеджер соседней команды (компания в Канаде) спросил, насколько типично для русскоязычного человека обдумывать вопрос несколько секунд, перед тем как отвечать. Я сперва не понял, что он имел в виду, оказалось, что кандидат на сеньёрскую позицию на многие вопросы как бы "подвисал", иногда на несколько секунд и у него закралось впечатление, что он использует какого-то суфлёра. Но хотел быть уверенным, что это не "национальная черта".


    1. DazzleBizzareAdventure Автор
      03.02.2026 13:28

      Ну поэтому я рассматриваю подобные утилиты, как небольшой допинг для уверенности.

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

      В этом случае помошник по дискорде оперативнее ответы давать будет, но даже этот вариант хреново будет работать если человек не знает элементарную базу.


  1. donRumatta
    03.02.2026 13:28

    Очевидно, чтобы отсечь новичков из онлайн-курсов и тех, кто «вкатился» в IT без должной подготовки.

    Очевидно, что это самое простое, что может заучить новичок, и с этой целью спрашивать нет смысла. Скорее наоборот, отсеятся опытные разрабы, которые много лет работают, а не проходят собесы)


    1. DazzleBizzareAdventure Автор
      03.02.2026 13:28

      Ну вот да.

      Когда я выхожу на рынок труда, то не знаю какие вопросы сейчас у обороте и n-первых собесов я не прохожу, хотя есть реальный опыт.

      Но потом запомнив основные вопросы на которые не смог ответить и проработал их, большенство собесов проходится без проблем.


  1. donRumatta
    03.02.2026 13:28

    P. S. Но утилита прикольная, почему не закодили ее агентом от и до?


    1. DazzleBizzareAdventure Автор
      03.02.2026 13:28

      Не доводилось писать ии агенты.

      Подумал, что так быстрее сделать


  1. DazzleBizzareAdventure Автор
    03.02.2026 13:28

    Есть мысля еще использовать локальную модель для фильтрации, которой постоянно будет поток текста отправляться, а она будет из него вычленять нужные вопросы и после этого слать их в нейронку, которая в облаке, так как сейчас я думаю, что в реальном собеседовании много мусора отправляться будет, например "Расскажите о себе".
    Но боюсь, что если юзать 2 нейросети, то может общая перфа упасть + комп должен быть достаточно мощным, чтобы толковую нейронку поднять + повысится вероятность глюков, так как сами по себе нейронки могут глючить.

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

    В общем, как я писал выше:
    Оверлей рабочий, но имеет смысл использовать только для подстраховки, так как может выдаваться откровенная дичь и прочие приколюхи.