Головна / Статті

Практичні нотатки: Штучний інтелект з ефективним використанням пам’яті: створення двигуна контексту на Go

Покрокове керівництво з практичних нотаток: Ефективний за використанням пам’яті штучний інтелект: створення двигуна контексту на Go: контракти, перевірки та готові блоки коду для команд, які впроваджують цю схему.

Видавець: REACTAPP.TOP10 хв читання2311 слів

Джерела: The Memory-Efficient AI Agent: Building a Context Engine in Go

Наведені нижче примітки описують практичний підхід до роботи над проектом «Ефективний штучний інтелект: створення двигуна контексту на Go». Основна увага приділяється контрактам, перевіркам та шаблонам коду замість мотиваційних пояснень. Під час етапу огляду спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та наслідки часткової невдачі. Такий перелік допоможе зберегти чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду замість об’ємних скриптів. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на складну ієрархію операцій.

Початок роботи: імпорти та модель даних

Процеси імпорту та налагодження у режимі «Getting Started» найкраще розглядати як вимірювану поверхню для роботи. Збережіть один ідеальний зразок результату, один випадок невдачі та примітки щодо скасування змін перед розширенням обсягу завдань. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Придумайте назви для кожного елемента, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Встановіть ліміти на кількість операцій за раз та за сеанс. Інструменти типу агентів активно розширюють контекст; жорсткі обмеження запобігають тому, щоб демонстрації перетворювалися на несподівані рахунки.

import (
	"container/heap"
	"container/list"
	"encoding/gob"
	"fmt"
	"math"
	"os"
	"strings"
)
type MemoryItem struct {
	ID       int
	Content  string
	Priority int
	Vector   []float64
	index    int
}

Двигун пріоритетів: Max-Heap з використанням container/heap

Етап Max-Heap двигуна приоритетів працює найкраще, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних середовищ. Зберігайте стан графа у випрямленому та типованому форматі. Вкладені блоки приховують інформацію про те, який вузол записав яке поле, і ускладнюють продовження роботи після перерв.

type PriorityQueue []*MemoryItem

func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
	return pq[i].Priority > pq[j].Priority // Max-heap logic
}
func (pq PriorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index = i
	pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*MemoryItem)
	item.index = n
	*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	old[n-1] = nil
	item.index = -1
	*pq = old[0 : n-1]
	return item
}

Ковзне вікно: часовий контекст за допомогою container/list

Етап Sliding Window Temporal працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Тримайте конфігурацію поза кодом додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Тримайте стан графа простим та типованим. Вкладені блоки приховують інформацію про те, який вузол записав яке поле, і ускладнюють продовження роботи після перерв. Етап Sliding Window Temporal працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Віддавайте перевагу малим, тестованим одиницям перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій.

type SlidingWindow struct {
	queue    *list.List
	capacity int
}

func NewSlidingWindow(capacity int) *SlidingWindow {
	return &SlidingWindow{queue: list.New(), capacity: capacity}
}
func (sw *SlidingWindow) Add(item *MemoryItem) {
	if sw.queue.Len() >= sw.capacity {
		sw.queue.Remove(sw.queue.Front())
	}
	sw.queue.PushBack(item)
}

Векторизація та схожість: семантичний пошук за допомогою math та strings

На етапі семантичного аналізу через векторизацію та схожість необхідно спочатку визначити вхідні дані, виконавця кроку та критерії завершення, перш ніж змінювати код. Оператори повинні мати можливість перезапустити цей крок з відомої точки контролю, не намагаючись відгадати прихований стан. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Призначте назви для кожних елементів, визначте критерії успіху та не допускайте мовчазного часткового завершення роботи. Необхідно забезпечити людське схвалення для операцій, які призводять до витрат грошей чи змін у продакшн-даних. Компіляційна налаштування не є гарантією повності виконання завдань у бізнес-сенсі.

func TextToVector(text string) []float64 {
	vocab := []string{"database", "connection", "fix", "leak", "system", "user"}
	vec := make([]float64, len(vocab))
	words := strings.Fields(strings.ToLower(text))
	for _, word := range words {
		for i, v := range vocab {
			if strings.Contains(word, v) {
				vec[i]++
			}
		}
	}
	return vec
}
func CosineSimilarity(a, b []float64) float64 {
	var dot, normA, normB float64
	for i := range a {
		dot += a[i] * b[i]
		normA += a[i] * a[i]
		normB += b[i] * b[i]
	}
	if normA == 0 || normB == 0 {
		return 0
	}
	return dot / (math.Sqrt(normA) * math.Sqrt(normB))
}

Стабільність зберігання даних: бінарна серіалізація за допомогою encoding/gob

Для бінарної серіалізації з підтримкою стану необхідно перед зміною коду визначити вхідні дані, власника кроку та критерії завершення. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Відображення витрат на ранньому етапі запобігає несподіваним рахункам під час переходу з демо-середовища у спільні. Встановлюйте людське схвалення для кроків, які витрачають гроші або змінюють дані в продакшені. Підключення на етапі компіляції не є гарантією повності бізнес-функціоналу.

type AgentMemory struct {
	LongTerm []*MemoryItem
}
func SaveMemory(filename string, mem *AgentMemory) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	return gob.NewEncoder(file).Encode(mem)
}

Виконання: main()

Для основної стадії The Execution необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функціоналу мають знаходитися в одному місці, яке оператори можуть перевіряти, не читаючи весь алгоритм. Встановлюйте людське схвалення для дій, які спричиняють витрати грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повноти бізнес-процесу. Для основної стадії The Execution необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Віддавайте перевагу невеликим, тестованим одиницям перед об’ємними скриптами. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу.

pq := &PriorityQueue{}
heap.Init(pq)
sw := NewSlidingWindow(3)
rawItems := []struct {
	content  string
	priority int
}{
	{"System: You are a helpful assistant.", 10},
	{"User: How do I fix the database connection?", 5},
	{"CRITICAL: Fix database connection leak.", 9},
}

for _, ri := range rawItems {
	item := &MemoryItem{
		Content:  ri.content,
		Priority: ri.priority,
		Vector:   TextToVector(ri.content),
	}
	heap.Push(pq, item)
	sw.Add(item)
}
fmt.Println("--- Sliding Window (capacity 3) ---")
for e := sw.queue.Front(); e != nil; e = e.Next() {
	fmt.Printf("  Window holds: '%s'\n", e.Value.(*MemoryItem).Content)
}
for i := 0; i < 2; i++ {
	sw.Add(&MemoryItem{
		ID:       i + 1,
		Content:  fmt.Sprintf("Stream item %d: recent event", i+1),
		Priority: 1,
		Vector:   TextToVector("recent event"),
	})
}
fmt.Println("  After 2 more stream items added:")
for e := sw.queue.Front(); e != nil; e = e.Next() {
	fmt.Printf("  Window holds: '%s'\n", e.Value.(*MemoryItem).Content)
}
query := "database connection fix"
queryVec := TextToVector(query)

var bestMatch *MemoryItem
maxSim := -1.0

fmt.Printf("Querying for: '%s'\n", query)
for _, item := range *pq {
	sim := CosineSimilarity(item.Vector, queryVec)
	fmt.Printf("Comparing with: '%s' (Sim: %.2f)\n", item.Content, sim)
	if sim > maxSim {
		maxSim = sim
		bestMatch = item
	}
}
fmt.Printf("\nBest match found: '%s' (Sim: %.2f)\n", bestMatch.Content, maxSim)
mem := &AgentMemory{LongTerm: *pq}
SaveMemory("agent_memory.gob", mem)
fmt.Println("Memory state saved to agent_memory.gob")
PS C:\Users\User\ai-memory> go run .\ai_memory_experiment.go
--- Sliding Window (capacity 3) ---
  Window holds: 'System: You are a helpful assistant.'
  Window holds: 'User: How do I fix the database connection?'
  Window holds: 'CRITICAL: Fix database connection leak.'
  After 2 more stream items added:
  Window holds: 'CRITICAL: Fix database connection leak.'
  Window holds: 'Stream item 1: recent event'
  Window holds: 'Stream item 2: recent event'

Querying for: 'database connection fix'
Comparing with: 'System: You are a helpful assistant.' (Sim: 0.00)
Comparing with: 'User: How do I fix the database connection?' (Sim: 0.87)
Comparing with: 'CRITICAL: Fix database connection leak.' (Sim: 0.87)

Best match found: 'User: How do I fix the database connection?' (Sim: 0.87)
Memory state saved to agent_memory.gob
PS C:\Users\User\ai-memory>

Агенти прискорення з MCP

Під час роботи над етапом «Агенти прискорення з MCP» спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Призначте назви елементів, визначте критерії успіху та не допускайте беззвучного часткового виконання. Фіксуйте назву інструменту, хеш аргументів, час виконання та результат кожного виклику. Без цих записів налагодження циклів агентів займає години.

Ключові висновки

Під час роботи на етапі ключових висновків спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-версії до спільних середовищ. Робіть контрольні пункти після дорогих кроків. Система повторного запуску не повинна знову стягувати плату за один і той самий виклик LLM, коли оператор перезапускає пізніший етап.

Додаткові посилання та ресурси

Під час роботи на етапі Додаткових посилань та ресурсів спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та наслідки часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Робіть контрольні пункти після дорогих кроків. Система повторного запуску не повинна знову стягувати плату за один і той самий виклик ШІ, коли оператор перезапускає пізніший етап. Під час роботи на етапі Додаткових посилань та ресурсів спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та наслідки часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Віддавайте перевагу невеликим, тестованим одиницям коду перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру обробки даних.

Чек-лист для експлуатації

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

Документуйте як успішний, так і відновлювальний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої доробки.

Встановлюйте людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Компіляційне підключення елементів не є гарантією повноти бізнес-функціоналу.

Напишіть короткий посібник: як змінювати ключі, як спорожнювати чергу, як скасовувати останнє завантаження даних.

Віддавайте перевагу невеликим, тестованим одиницям коду перед складними скриптами. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.

Встановіть людське схвалення для тих етапів, де витрачаються гроші або змінюються дані виробництва. Підключення під час компіляції не є гарантією повноти бізнес-функціоналу.

Перш ніж переходити на нову версію стеку, заморозьте існуючі версії, створіть остаточний запис для критичного шляху виконання та підтвердьте кроки для скасування змін. У спільних середовищах необхідні обмеження на частоту використання, перевірки прав доступу та чіткий власник для зміни секретних даних. Віддавайте перевагу надійності перед креативними одноразовими демонстраціями.

Примітка до d4b7557c44d8: не включайте ключі постачальника до репозиторію, встановіть ліміт на токени на кожну сесію та зберігайте записи поруч із фіксами для оцінки, щоб подальша заміна моделей залишалася порівнянною.

Для етапу 0 процедури зміцнення необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код.

Деталь зміцнення 0/825: вимірюйте час виконання, клас помилки та кількість витрачених токенів для цього етапу, а потім вирішуйте, чи залишити зміни, ґрунтуючись на фіксованому наборі питань, а не на індивідуальних спостереженнях.

Під час виконання першого етапу додаткових заходів зпрочнення спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на складну послідовність операцій.

Деталь зпрочнення 1/825: виміряйте час виконання, клас помилки та кількість витрачених токенів для цього етапу, а потім вирішіть, чи залишити зміни, ґрунтуючись на певному наборі критеріїв, а не на індивідуальних спостереженнях.

Другий етап додаткових заходів зпрочнення найкраще працює, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний зразок виконання, один випадок невдачі та запис про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним витратам під час переходу з демо-середовища до спільних середовищ.

Деталь посилення безпеки 2/825: виміряйте час виконання, клас помилки та кількість витрачених токенів для цього запису, а потім вирішіть, чи залишити зміни, ґрунтуючись на фіксованому наборі питань, а не на окремих випадках.

Для 3-го етапу запису про посилення безпеки визначте вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Документуйте як успішний, так і відновлювальний сценарії. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.

Деталь посилення безпеки 3/825: виміряйте час виконання, клас помилки та кількість витрачених токенів для цього запису, а потім вирішіть, чи залишити зміни, ґрунтуючись на фіксованому наборі питань, а не на окремих випадках.