Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
- Telegram Web
Telegram Web
🤯 Мы больше года строим мультиагентные системы

Грабли, находки, паттерны, эксперименты — всё это накопилось и в какой-то момент стало жалко держать только у себя.

Никита — рассказывает (и показывает) базу: токенизация, LLM, SFT, PEFT, локальный инференс + RAG и как оценивать его качество.
Диана — как строят мультиагентные системы, какие есть паттерны проектирования и библиотеки.
Макс — про инференс в проде + разберет CoPilot, соберет с вами из кусочков свой копайлот, а затем его сломает через prompt injection. // Макс фанат autogen (а если нет — он вас разубедит в своем классном канале)
Финальным аккордом Дима углубится в MCP и соберет несколько кейсов повзрослее.

Курс тут: https://clc.to/47pgYA
Промокод: datarascals действует до 23:59 29 июня
Какую роль выполняют функции make и new в Go, и чем они отличаются

В Go, несмотря на наличие сборщика мусора, разработчикам всё равно необходимо выделять память для новых переменных. Для этого язык предоставляет две встроенные функции: new и make.

➡️ Функция new(T) выделяет память для переменной типа T, инициализирует её нулевым значением этого типа и возвращает указатель на эту переменную.
p := new(int)

В этом примере p будет указателем на новый int, инициализированный значением 0.

➡️ Функция make используется для инициализации срезов, карт и каналов, которые являются встроенными типами данных в Go и требуют дополнительной инициализации перед использованием.

• Срезы: при создании среза с помощью
make, задаются начальная длина и ёмкость, что позволяет Go заранее выделить нужное количество памяти
s := make([]int, 5, 10) 

Map: при создании map с помощью make осуществляется его инициализация перед добавлением элементов
m := make(map[string]int)

• Каналы:
make также используется для создания каналов.
ch := make(chan int)


Основные различия между
make и new

➡️ new возвращает указатель на тип, инициализированный нулевым значением этого типа.
➡️ make возвращает инициализированный экземпляр встроенного типа (среза, map или канала), готовый к использованию.
➡️ Обычно, для выделения памяти под переменные используют литералы типа или make. new используется реже и в основном в более специфичных случаях.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥1
В чем заключается механизм embedding в Go

В Go нет традиционного наследования, как в ООП, но есть механизм embedding (встраивание), который позволяет одному типу включать поля и методы другого типа, обеспечивая схожий эффект.
Важно знать следующее:

➡️ Композиция вместо наследования
В Go предпочтение отдается композиции, а не наследованию. Один тип может включать другой, расширяя его функциональность.

➡️ Простота использования
Встроить один тип в другой очень просто — достаточно определить один тип внутри другого.

➡️ Поведение и интерфейсы
Если встроенный тип реализует интерфейс, то и тип, в который он встроен, автоматически реализует этот интерфейс.
type Engine struct {
Power int
Type string
}

type Car struct {
Engine
Brand string
Model string
}

func main() {
c := Car{
Engine: Engine{Power: 150, Type: "Petrol"},
Brand: "Ford",
Model: "Fiesta",
}
fmt.Println(c.Power) // Выведет: 150
}


Встраивание интерфейсов:
type Writer interface {
Write([]byte) (int, error)
}

type Logger struct {
Writer
}

Теперь Logger автоматически реализует интерфейс Writer, если его встроенное поле Writer реализует этот интерфейс.

Важные моменты:

Конфликты имен
Если у встроенного и внешнего типов одинаковые поля или методы, приоритет отдается внешнему типу.

Неявное поведение
Методы встроенного типа становятся частью внешнего, что может быть неочевидно при чтении кода.

Интерфейсы
В Go можно встраивать интерфейсы, создавая сложные интерфейсы на основе существующих.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍5😁1
Какие изменения в В Go 1.24 коснулись срезов

В Go 1.24 были улучшены возможности работы с срезами через пакет slices. Вот некоторые новые функции:

Поиск элемента: slices.Contains(s, v) проверяет наличие элемента в срезе.

Сортировка: slices.Sort(s)сортирует срезы для любых типов, поддерживающих интерфейс ordered.

Поиск максимума: slices.Max(s)находит максимальное значение в срезе.

Удаление элементов: slices.Delete(letters, 1, 4)удаляет элементы в указанном диапазоне.

Сравнение срезов: slices.Compare(a, b)сравнивает два среза.

slices.Compact: удаляет дубликаты подряд идущих элементов.

slices.Clip: удаляет неиспользуемую емкость, возвращая срез с длиной и емкостью, равными длине.

slices.Clone: возвращает копию среза.

BinarySearch: выполняет бинарный поиск в отсортированном срезе.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍136👏1
Чем горутины в Go отличаются от потоков (threads)

Чтобы разобраться в том, как Go реализует параллельное выполнение задач, важно понимать различия между горутинами и потоками. Эти различия влияют на производительность, ресурсы и удобство работы с многозадачностью. Вот основные отличия горутин от потоков в Go:

➡️ Уровень абстракции
Горутины представляют собой абстракцию, встроенную в язык Go, которая позволяет выполнять функции или методы параллельно.
Потоки — это низкоуровневые объекты операционной системы, предназначенные для параллельного выполнения задач.


➡️ Размер стека
Горутины начинают с небольшого стека
(около 2 КБ), который может изменяться по мере необходимости.
Потоки обычно имеют фиксированный размер стека, который значительно больше, часто от 1 МБ и выше.


➡️ Создание и переключение
Горутины создаются с помощью ключевого слова go перед вызовом функции и требуют минимальных затрат на создание и переключение контекста.
Потоки стоят дороже как по времени создания, так и по затратам на переключение контекста, поскольку для этого необходимо взаимодействовать с операционной системой.


➡️ Планировщик
Горутины управляются планировщиком Go, работающим в пользовательском пространстве, который распределяет горутины по доступным потокам ОС
(обычно один поток на одно ядро процессора).
Потоки управляются планировщиком операционной системы.

➡️ Изоляция
Ошибка в одной горутине
(например, паника) может повлиять на остальные горутины в программе.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥1
В чём разница между следующими объявлениями массивов в Go

arr1 := [5]int{}
arr2 := [...]int{1, 2, 3}
arr3 := [...]int{}


1️⃣ вариант — arr1 := [5]int{}это явная инициализация массива длиной 5, где все элементы будут равны 0, потому что для типа int это значение по умолчанию.

2️⃣arr2 := [...]int{1, 2, 3}использует вывод длины массива: компилятор сам определяет, что длина должна быть 3, так как передано три элемента. Это удобно, когда длина очевидна из контекста.

А вот 3️⃣ arr3 := [...]int{} — это ошибка компиляции. Компилятор не сможет вывести длину массива из пустого литерала, и поэтому такой код не скомпилируется. Чтобы создать пустой массив, нужно использовать явно указанную длину, например: arr3 := [0]int{}.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥311
🔥 Последняя неделя перед стартом курса по AI-агентам

Старт курса уже 5го числа! Если вы планировали вписаться — сейчас ПОСЛЕДНИЙ шанс забронировать место

На курсе:
разложим LLM по косточкам: токенизация, SFT, PEFT, инференс
— соберём RAG и научимся оценивать его адекватно
— построим настоящую мультиагентную систему — архитектуру, которая умеет расти
— разберём CoPilot, сломаем через prompt injection (спасибо Максу)
— и наконец, посмотрим, как это работает в MCP и реальных кейсах

📍 Это 5 живых вебинаров + раздатка + домашки + чат с преподавателями

И главное — возможность реально разобраться, как проектировать системы на LLM, а не просто «поиграться с API»

👉 Курс здесь
В чём суть и контекст применения Singleton в Go

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

➡️Когда использовать:
• Централизованное управление ресурсами: пул соединений, логгеры, кэш
• Избегание дублирования состояния в памяти
• Работа в многопоточной среде и потокобезопасный доступ к общей структуре


➡️ Когда не стоит использовать:
• Singleton нарушает SRP (единственная ответственность)
• Затрудняет тестирование (невозможно подменить зависимости)
• Провоцирует на глобальные сингл-точки сбоя (если объект упал — всё развалилось)
• Препятствует масштабируемости при переходе к микросервисной архитектуре


➡️ Важно: в Go нет классов и private конструкторов как в OOP-языках, поэтому реализация синглтона — технически иной подход, обычно через package-level переменные, sync и функции.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🌚2👏1
Какой контекст выбрать: context.Background() или context.TODO()

Когда вы работаете с Go-контекстами, легко запутаться: оба выглядят одинаково, оба ничего не делают — но используются по-разному.

context.Background() — это ваш "нулевой" контекст, от которого принято строить всё остальное. Он используется в main(), в инициализации, в серверах и корневых вызовах. Это базовая точка отсчёта.

context.TODO() — маркер, который говорит: "я ещё не знаю, какой контекст здесь будет, но он точно появится".
Такой контекст часто используется в заготовках, шаблонах, временном коде, пока архитектура не определена.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱32
Почему ленивую инициализацию важно делать безопасно в многопоточном окружении

Для ленивой инициализации (lazy init) в многопоточной среде можно использовать sync.Mutex. Это даёт максимальный контроль, но требует аккуратности.

Рабочий пример:
package main

import (
"fmt"
"sync"
)

type singleton struct {
data string
}

var (
instance *singleton
mu sync.Mutex
)

func GetInstance() *singleton {
if instance == nil {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &singleton{data: "initialized"}
}
}
return instance
}

func main() {
s := GetInstance()
fmt.Println("Singleton data:", s.data)
}


Двойная проверка (Double-Checked Locking):
1️⃣ if instance == nil не требует блокировки и отсекает большинство вызовов
2️⃣ внутри mu.Lock() — необходима, чтобы избежать гонки между горутинами, прошедшими первую проверку одновременно

➡️ Рекомендуется применять только когда нужно встроить логику, контроль или откладку внутрь блокировки. В остальных случаях предпочтительнее sync.Once

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
4👏1
Какие основные типы context в Go вы знаете

Go предоставляет функции, которые позволяют создавать производные контексты на основе базового
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()


WithCancel — вручную отменяет контекст
WithTimeout — отменяет через заданный интервал времени
WithDeadline — отменяет к заданному моменту времени
WithValue — добавляет пару ключ/значение в контекст (использовать осторожно)

Каждая из этих функций создаёт дочерний контекст, и если родитель будет отменён — отменятся и все дочерние.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🥱1
Зачем в реализации Singleton использовать sync.Once, если уже есть мьютексы

Если вы хотите простую, лаконичную и безопасную реализацию — используйте sync.Once
package main

import (
"fmt"
"sync"
)

type singleton struct {
config string
}

var (
once sync.Once
instance *singleton
)

func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{config: "default"}
})
return instance
}

func main() {
s := GetInstance()
fmt.Println("Singleton config:", s.config)
}

sync.Once гарантирует, что переданная функция будет выполнена строго один раз, даже если GetInstance вызывается из множества горутин.

Потокобезопасность на уровне стандартной библиотеки
Лаконичность кода
Не требует мьютексов или ручных проверок

Не подходит, если вам нужно сбрасывать или пересоздавать Singleton (например, в тестах)
Once работает только один раз за весь жизненный цикл приложения

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍2
Как правильно использовать ctx.Done() внутри горутин и что произойдёт, если этого не делать

Контекст используется как сигнальный канал. Правильно обрабатывать ctx.Done() нужно всегда, особенно в горутинах
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("goroutine stopped:", ctx.Err())
return
case <-time.After(1 * time.Second):
fmt.Println("working...")
}
}
}(ctx)


Если не слушать ctx.Done(), вы получите:
• Утечки горутин
• Зависание приложения
• Недетерминированное поведение при отмене


🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
Как вам вопросы прошедшей недели

Оцените их по шкале 🔥,❤️,👍,😢, 🥱,
где 🔥 — это супер, а 🥱 — это скучно.

Также приветствуется фидбек в комментариях.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥137🥱1
Почему инициализация через init() не считается ленивой

Иногда экземпляр Singleton можно создавать заранее, при старте пакета. В Go для этого существует функция init().

Singleton с init() в действии
package main

import (
"fmt"
)

type singleton struct {
config string
}

var instance *singleton

func init() {
instance = &singleton{config: "preloaded"}
}

func GetInstance() *singleton {
return instance
}

func main() {
s := GetInstance()
fmt.Println("Singleton config:", s.config)
}


➡️ Особенности подхода:
• Инициализация происходит один раз, до выполнения main() — автоматически
• Синхронизация не требуется init() вызывается в однопоточном контексте
• Порядок инициализации между пакетами гарантирован Go-рантаймом

➡️ Подходит для простых случаев:
Объект всегда нужен в программе
Конфигурация не зависит от внешнего ввода
Важна простота, а не гибкость

➡️ Недостатки
Нарушает ленивую загрузку — объект создаётся даже если не используется
Затрудняет подмену или настройку из внешнего источника (например, через флаги, файлы, ENV)
Может ограничить тестируемость и повторную инициализацию

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
😁42👍1
Как правильно использовать context в юнит-тестах

context — мощный инструмент, но в тестах он может мешать, особенно если используется без ограничений по времени или отмене.

Чтобы избежать зависаний и утечек в юнит-тестах, всегда создавайте context с таймаутом:
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()


Это гарантирует:
Тест не будет висеть бесконечно
Ресурсы будут высвобождены
Горутины получат сигнал на завершение


Если ваш тест зависает — это может говорить о том, что где-то в коде игнорируется ctx.Done()

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Какой способ инициализации Singleton в Go выбрать: sync.Mutex, sync.Once или init()

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍2
Зачем использовать select, если можно просто читать из канала

В Go оператор select — это конкурентный аналог switch, предназначенный исключительно для работы с каналами.

С его помощью можно:
• Ждать сразу несколько операций с каналами (чтение/запись)
• Управлять конкурентными потоками без блокировок
• Не блокироваться, если добавить
default ветку

select {
case msg := <-ch1:
fmt.Println("Received from ch1:", msg)
case ch2 <- 42:
fmt.Println("Sent 42 to ch2")
default:
fmt.Println("Nothing ready")
}


Если ch1 или ch2 готовы — будет выполнен соответствующий case.
Если ни один канал не активен — выполняется default, и select не блокирует выполнение.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍42
Можно ли сделать Singleton тестируемым без нарушения его природы

Singleton может мешать тестированию, так как его состояние живёт весь runtime. Но есть обходные пути:

1️⃣ Вынос в интерфейс
type Storage interface {
Get(key string) string
}


2️⃣ Инъекция зависимости через параметр
func ProcessData(s Storage) { ... }


3️⃣ Сброс состояния Singleton (в тестах)
// В тестовых сборках
func resetSingleton() {
instance = nil
once = sync.Once{}
}


Warning: сброс Singleton — антипаттерн, но допустим в юнит-тестах

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Как реализовать приоритет между каналами, если select выбирает случайно

Одно из важных свойств select в Go — рандомность выбора, если сразу несколько каналов готовы к операции.
select {
case msg := <-ch1:
fmt.Println("ch1:", msg)
case msg := <-ch2:
fmt.Println("ch2:", msg)
}


Если и ch1, и ch2 доступны — Go случайным образом выберет один case.
Это исключает жёсткий приоритет каналов и распределяет нагрузку справедливо (не детерминированно).

Это нужно для предотвращения "голодания" менее приоритетных каналов, также это позволяет реализовать честные очереди и worker pool без ручного балансировщика

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
😁8👍2🌚2
2025/07/14 06:58:48
Back to Top
HTML Embed Code: