Практычныя нататкі: эканомны да памяці AI-агент — рухавік кантэксту на Go
Практычны разбор тэмы «эканомны да памяці AI-агент і рухавік кантэксту на Go»: кантракты, праверкі і гатовыя фрагменты кода для каманд, якія ўкараняюць гэты падыход.
Крыніцы: The Memory-Efficient AI Agent: Building a Context Engine in Go
Наступныя прыміткі паказваюць практычны шлях для розумэння кнігі «AI-агент з эфектываею памяцю: стварэнне двыжка кантэксту на Go». Акцэнт ставіцца на кантракты, пераконтроўкі і месца для коду, які можна легка адразу вставіць, а не на мотывацыйныя аспекты. Калі працуеце над стадзіяй агледжэння, спачатку запісайце кантракт: неабходныя вхідныя даны, сігнал успеху і тое, што выканаецца у разе частковага невыпалення. Такі спіс дапамагае заліцварыць пазнейшыя змены ў кодзе. Валіце маленькія, тэставаныя елементы замест амаль неканчатых скрыптав. Калі якісь крок не выйшоў, прычына невыпалення павінна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач.
Пачатак работы: імпорты і модэль дадзеных
Працэс «Пачатак» з імпортамі і наступным этапам работае найэфектывней, калі яго розглядаць як параметрызаваную плошчыну. Запісаце адна ідэальная версія рэзультата, адын случай неудачы і прыметкі па поверненню да пачатковага стану пры расшырэнні масштаба. Разглядзеце гэты этап як кантракт межа вхіднымі дадзеннямі і пераканаленымі выходнымі рэзультатамі. Даце назвы артыфактам, задаце крытэрыі успеху і не падзельвайцеся на частковыя рэшэння без адзінаго слова. Задаце ліміт токенав на кожны раунд і на кожную сесію. Інструменты-агенты агрэсывна расшырваюць кантэкст; строгі ліміты не дазволяюць дэм-версіям ператварыцца на неспакаваныя рахункі.
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», спачатку запісайце угоду: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі спіс контроля дапамагае заліцьварыць пазнейшыя змены ў кодзе. Спрэцьвачайце гэтую стадзію як угоду межа данымі і перакананымі выходамі. Дайце назву рэзультатам, задаце крытэрыя успеху і не падзволяйце частковаму выкананню без адазнаўлення. Зявіце лог з назвай інструмента, хэшам аргументаў, часам затрымкі і рэзультатам кожнага вызову. Без такога следу часы губяцца на адлагоджванні цыклаў агента.
Ключовыя выводы
Калі працюеце на стадыі «Ключовыя выводы», спачатку запісайце умовы контракту: неабяжлівыя даны, сігнал успеху і тое, што выканаецца у разы ўзельнага неякшага выкарыстання. Такі чарт дапамагае заліцвачваць змяны ў кодзе пазнейша.
Дадатковыя лінкі і рэсурсы
Калі працуеце на стадыі «Дапаможныя лінкі: рэсурсы», спачатку запішыце умовы працы: неабяжлівыя даныя, сігнал успеху і тое, што выходзіць пад частковым нявыпаннем. Такі чарт дапамагае залічваць пазнейшыя змены ў кодзе чыста.
Чарт для аператывных задач
У стадыі практычнага чэк-лісту неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтары завершэння пры змяне коду. Аперацыйныя працавнікі должны магчыма ўвайсці канкрэтны крок з вядомага пункта контролю, не спрабоўваючы здагадвацца пра схованы стан.
Неабяжна задокументаваць як шлях успеху, так і шлях вярнення да нормальнага стану. Перапрыбуткі, людзкія перакрыцця і апрацоўка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.
Неабяжна застаўляць людзкую апраўдку для тых крокаў, якія выкарыстоўваюць грошы або зміняюць даны ў працэсе віробніцтва. Компіляцыйныя налашчэння не ёсць гарантыяй полной адпаведнасці продукту выклікам.
Напісці кароткі посібнік: як роцыяваць клучы, як спрабаваць апустошыць чэргу, як вярнуць стан да пярэдніх значэнняў.
Валіць маленькія, тэставаныя елементы працэсу замест вялікіх скрыптав. Калі крок не выйшоў, прычына неабяжна мусі быць аднаковаю для всіх аспектаў, а не супярэчлівай у цэлым працэсе.
Неабяжна людская празначэнне для тых элементаў, якія выдваюць грошы або зменяюць даныя праработкі. Працаванне ў часе компілявання не абяжаецца пачынкамі бізнес-процэсаў.
Перш чым пераводзіць стэк, заморажуйце версіі, зафіксавайце «золаты» транскрыпты для крытычных ліній працы і паказвайце крокі для атрыбуцыі. У спадзяльных срэдах неабходны ліміты частоты запытоў, перакананні ў правах на выкарыстоўванне рэсурсаў і чысткі власнік для змены секрэтных даных. Валіце надзейнасць, а не крэатыўныя разовыя дамахі.
Запіс для пакета d4b7557c44d8: не кладзіце ключы прадаўцоў у рэпазітарый, задаце ліміт токена на кожную сесыю і зберагаце транскрыпты празаўседліва з фіксатамі для ацэнкі, каб пазнейшыя змены мадэляў заставаліся пораўнанневымі.
Для этапы 0 пры практыкаванні змецчэння неабяжна прадзерабатаваць параметры вхідных дадзеных, адпраўніка крока і крытэрыя завершэння пры змены коду. Аперацыйныя працавнікі павінны магчымае перазапускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Конфігурацыю трэба зберагаць паза кодам прыкладнення. Файлы сяродавішча, хранальнікі секрэтных дадзеных і флагі функцыйяў павінны знаходзіцца ў адном месцы, якое аперацыйныя працавнікі могу пераглядаць, не чытаючы весь ланцуг задач.
Дзеянне змецчэння 0/825: памерыць час выканання, класію адказоў і колькасць выкорыстоўваных токеноў для гэтага пункту, а потым вырашыць, чы робіць змену на адной пазначанай базе пытанняў, а не на адной толькі прыватнай інформацыі.
Калі працуеце над першым этапам зміцнення, спачатку запісайце умовы кантракта: неабяцковыя даны, сігнал успеху і тое, што выходзіць на частым неудачам. Такі спіс дапамагае залічыць пазнейшыя змены коду адкрыта і прозрачна. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выходзіць, неудача должна паказваць на адну конкрэтную відпаведальнасць, а не на заплутаны ланцюг задач.
Дзеянне зміцнення 1/825: вымерыце час выканання, класію памылак і колькасць токенаў, якія былі выкарыстоўваны для гэтага пункту, а потым выявіце, чы рашыцца застаўляць змену на адной фіксаванай сэтке пытанняў, а не на адзінаковых прыкладах.
Этап зміцнення 2 працюе лепей, калі яго спрыяжваць з мерыемымі показнікамі. Запісайце адны ідэальны прыклад працы, адзін кейс неудачы і прыказку па абратанні змян перад расшырэнням масштаба. Запісвайце часы выканання і кост токенаў або запытаў разам з функцыйнальнымі рэзултатамі. Відразувая візуабільнасць костаў запобегае неспакойным рашчыткам, калі працэс пераходзіць з дэмаверсіі ў спяльныя асяроддзя.
Дзеянне паўжасткі 2/825: звярніце увагу на час выканання, класы паказчаў і колькасць вікорыстоўваных токенаў для гэтага зазначэння, а пасля, на аднойчыне з фіксаваным наборам пытанняў, а не на асобістых спазырэннях, выявіце, чы хачаце застаўіць змяну.
Для 3-й стадзіі паўжасткі неабходна перад змянай коду чытко візначыць вхідныя даны, адпаведальнага за крок і крытэрыя завершэння. Аперацыйныя працавнікі должны магчымае перадзвануць гэты крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Неабходна адначасова задокументаваць шлях успеху і шлях вяснавання проблем. Практыкі павтарэння спробы, людзкія перакрыцця і обработка некорэктных паведамленняў ёсць часткай продукту, а не чымсь, што дадаецца пазней.
Дзеянне паўжасткі 3/825: звярніце увагу на час выканання, класы паказчаў і колькасць вікорыстоўваных токенаў для гэтага зазначэння, а пасля, на аднойчыне з фіксаваным наборам пытанняў, а не на асобістых спазырэннях, выявіце, чы хачаце застаўіць змяну.