0Pricing
AI Engineering Academy · Урок

Общая память и взаимодействие между агентами

Реализуйте уровень общей памяти с хранилищем ключей и значений, из которого агенты читают данные и записывают их, обеспечивая асинхронное сотрудничество без жёсткой связанности.

«Общая память и взаимодействие между агентами» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Engineering Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Engineering Academy содержит 4 уроков всего.

Проблема изоляции агентов

В мультиагентной системе каждый агент работает в собственном контексте и не знает, что делают или сделали другие агенты. Если агент researcher обнаружил важный факт, как об этом узнает агент writer? Как агент мониторинга поймёт, что агент coder столкнулся с ошибкой? Без общего механизма коммуникации агенты остаются изолированными хранилищами, которые не могут эффективно сотрудничать.

Общая память: модель Blackboard

Классическое решение для взаимодействия между агентами — модель Blackboard: общее хранилище данных (Blackboard), из которого любой агент может читать и в которое может записывать. Агенты публикуют свои выводы, читают вклад других и неявно координируют работу через общее состояние. Эта модель устраняет связанность агентов друг с другом: им не нужно знать о существовании друг друга, достаточно знать структуру общей памяти.

# Simple in-memory blackboard using a dictionary
from threading import Lock

class Blackboard:
    def __init__(self):
        self._data = {}
        self._lock = Lock()  # thread-safe for parallel agents

    def write(self, key: str, value, agent_id: str):
        with self._lock:
            self._data[key] = {'value': value, 'written_by': agent_id}
            print(f'[{agent_id}] wrote: {key}')

    def read(self, key: str):
        with self._lock:
            return self._data.get(key, {}).get('value')

    def keys(self):
        with self._lock:
            return list(self._data.keys())

blackboard = Blackboard()

Постоянная общая память с Redis

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

import redis
import json

class RedisSharedMemory:
    def __init__(self, prefix='agent:'):
        self.redis = redis.Redis(host='localhost', port=6379, decode_responses=True)
        self.prefix = prefix

    def set(self, key: str, value, ttl_seconds=3600):
        full_key = self.prefix + key
        self.redis.setex(full_key, ttl_seconds, json.dumps(value))

    def get(self, key: str):
        full_key = self.prefix + key
        raw = self.redis.get(full_key)
        return json.loads(raw) if raw else None

    def append_to_list(self, key: str, item):
        full_key = self.prefix + key
        self.redis.rpush(full_key, json.dumps(item))

    def get_list(self, key: str):
        full_key = self.prefix + key
        return [json.loads(x) for x in self.redis.lrange(full_key, 0, -1)]

memory = RedisSharedMemory(prefix='research_project:')

Пространства имён в общей памяти

В сложных мультиагентных системах агенты записывают множество разных типов данных, и плоское пространство имён ключей быстро превращается в хаос. Используйте иерархические пространства имён, чтобы упорядочить общую память. Распространённый шаблон — project_id:agent_role:data_type, например proj_123:researcher:findings или proj_123:coder:error_log. Это упрощает получение всех данных проекта или всех результатов конкретного агента.

class NamespacedMemory:
    def __init__(self, project_id: str, agent_id: str, redis_client):
        self.base = f'{project_id}:{agent_id}'
        self.redis = redis_client

    def write_finding(self, topic: str, content: str):
        key = f'{self.base}:findings:{topic}'
        self.redis.set(key, content)

    def read_all_findings(self, project_id: str):
        # Read findings from ALL agents in this project
        pattern = f'{project_id}:*:findings:*'
        keys = self.redis.keys(pattern)
        return {k: self.redis.get(k) for k in keys}

# Usage
researcher_memory = NamespacedMemory('proj_123', 'researcher', redis_client)
researcher_memory.write_finding('competitors', 'OpenAI, Anthropic, Google...')

writer_memory = NamespacedMemory('proj_123', 'writer', redis_client)
all_findings = writer_memory.read_all_findings('proj_123')

Структурированная и неструктурированная память

Содержимое общей памяти может быть неструктурированным (необработанные текстовые блоки, которые будет читать LLM) или структурированным (объекты JSON/Python с типизированными полями). Структурированная память предпочтительнее, поскольку она поддерживает программные запросы, проверку и объединение данных. Всегда определяйте схему данных, которые каждый агент записывает в общую память, документируйте её и проверяйте записи по этой схеме, чтобы один неисправный агент не повредил хранилище памяти.

from pydantic import BaseModel
from typing import Optional, list
from datetime import datetime

class ResearchFinding(BaseModel):
    topic: str
    summary: str
    sources: list[str]
    confidence: float  # 0.0 to 1.0
    written_by: str
    timestamp: datetime

# Validated write - bad data is caught before it enters shared memory
def write_finding(memory, finding_dict: dict):
    finding = ResearchFinding(**finding_dict)  # validates on creation
    memory.set(f'findings:{finding.topic}', finding.model_dump())
    print(f'Validated finding written for topic: {finding.topic}')

Событийное взаимодействие с помощью Pub/Sub

Вместо постоянного опроса общей памяти на наличие обновлений агенты могут использовать взаимодействие по схеме публикация/подписка (pub/sub), чтобы уведомлять других агентов о завершении задачи. Агент A публикует событие («research_complete»), а агент B, подписанный на это событие, просыпается и начинает обработку. Redis pub/sub и очереди сообщений, такие как RabbitMQ или Kafka, поддерживают этот шаблон.

import redis

# Publisher (researcher agent)
def researcher_agent(topic, redis_client):
    findings = do_research(topic)
    redis_client.set(f'findings:{topic}', findings)
    
    # Notify all subscribers that research is done
    redis_client.publish('agent_events', f'research_complete:{topic}')
    print(f'Research complete, published event for topic: {topic}')

# Subscriber (writer agent) - runs in separate process
def writer_agent_listener(redis_client):
    pubsub = redis_client.pubsub()
    pubsub.subscribe('agent_events')
    
    for message in pubsub.listen():
        if message['type'] == 'message':
            event = message['data']
            if event.startswith('research_complete:'):
                topic = event.split(':')[1]
                findings = redis_client.get(f'findings:{topic}')
                write_draft(findings)  # start writing immediately

Общая память в LangGraph

В LangGraph общей памятью между агентами является сам объект состояния графа. Каждый узел читает данные из одного и того же типизированного словаря состояния и записывает данные в него. LangGraph автоматически координирует чтение и запись. В более сложных сценариях Вы также можете внедрить внешний клиент памяти (Redis, базу данных) в функцию каждого узла с помощью внедрения зависимостей.

from langgraph.graph import StateGraph
from typing import TypedDict

class SharedState(TypedDict):
    # All shared data lives here - every node can read any field
    query: str
    research_findings: str   # written by researcher, read by writer
    written_draft: str       # written by writer, read by reviewer
    review_notes: str        # written by reviewer, read by writer (loop)
    final_output: str        # written by synthesizer

# Researcher writes to 'research_findings'
def researcher(state: SharedState) -> dict:
    findings = search_and_summarize(state['query'])
    return {'research_findings': findings}  # partial state update

# Writer reads 'research_findings', writes 'written_draft'
def writer(state: SharedState) -> dict:
    draft = write_from_findings(state['research_findings'])  # reads researcher output
    return {'written_draft': draft}

Конфликты и согласованность памяти

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

# Optimistic locking with Redis
def safe_write(redis_client, key, new_value, expected_version):
    with redis_client.pipeline() as pipe:
        try:
            pipe.watch(key + ':version')  # watch for concurrent modification
            current_version = int(pipe.get(key + ':version') or 0)
            
            if current_version != expected_version:
                raise ValueError(f'Version conflict: expected {expected_version}, got {current_version}')
            
            pipe.multi()  # start transaction
            pipe.set(key, new_value)
            pipe.set(key + ':version', current_version + 1)
            pipe.execute()  # atomic commit
            print('Write successful')
        except redis.WatchError:
            print('Conflict detected, retry write')

Время жизни памяти и очистка

Общая память агентов со временем накапливается и может расти без ограничений, если ею не управлять. Всегда устанавливайте время жизни (TTL) для записей памяти, чтобы они автоматически истекали. Для памяти в рамках проекта удаляйте все записи после завершения проекта. Выбирайте значения TTL в соответствии с предполагаемой длительностью рабочего процесса: короткие значения TTL (минуты) — для временных данных, более длительные (от нескольких часов до нескольких дней) — для результатов, которые могут использоваться повторно.

def cleanup_project_memory(redis_client, project_id: str):
    pattern = f'{project_id}:*'
    keys = redis_client.keys(pattern)
    if keys:
        redis_client.delete(*keys)
        print(f'Cleaned up {len(keys)} memory entries for project {project_id}')

# Set TTL when writing
def write_with_ttl(redis_client, key, value, ttl_hours=2):
    redis_client.setex(
        key,
        ttl_hours * 3600,  # convert to seconds
        json.dumps(value)
    )

# Register cleanup callback when workflow completes
def on_workflow_complete(project_id):
    cleanup_project_memory(redis_client, project_id)
    print(f'Workflow {project_id} complete, memory cleaned up')

Память как история агента

В общей памяти можно хранить не только результаты агентов, но и историю их действий. Запись сведений о том, какой агент, что, когда и почему сделал, создаёт незаменимый журнал аудита для отладки сбоев, понимания способа формирования итогового результата и возобновления прерванных рабочих процессов. Такой журнал действий является эквивалентом трассировки LangSmith, основанным на памяти.

import time
from dataclasses import dataclass

@dataclass
class AgentAction:
    agent_id: str
    action_type: str     # 'research', 'write', 'review', 'tool_call'
    input_summary: str
    output_summary: str
    timestamp: float
    success: bool

def log_action(memory, action: AgentAction):
    key = f'action_log:{action.agent_id}:{action.timestamp}'
    memory.set(key, vars(action))

# Usage in an agent
def researcher_with_logging(state, memory):
    start = time.time()
    findings = do_research(state['query'])
    log_action(memory, AgentAction(
        agent_id='researcher',
        action_type='research',
        input_summary=state['query'][:100],
        output_summary=findings[:100],
        timestamp=start,
        success=True
    ))
    return findings

Выбор архитектуры памяти

Подходящая архитектура общей памяти зависит от модели развёртывания. Для рабочих процессов LangGraph в одном процессе достаточно состояния графа. Для многопроцессных или распределённых агентов используйте Redis. Для долгоживущих проектов, которым требуется сохранность данных после перезапуска, используйте реляционную базу данных с надлежащей индексацией. Для координации между сервисами на основе событий добавьте pub/sub поверх выбранного хранилища.

Быстрая проверка

Проверьте своё понимание общей памяти и взаимодействия между агентами из этого урока.

Итоги урока

В этом уроке Вы узнали, что модель доски объявлений использует общее хранилище данных, из которого может читать и в которое может записывать любой агент, обеспечивая неявную координацию без прямой связанности агентов друг с другом; Redis — основной выбор для постоянной общей памяти в распределённых мультиагентных системах; а pub/sub обеспечивает событийное взаимодействие, благодаря которому агенты немедленно реагируют после выполнения зависимостей. Далее мы рассмотрим цикл выполнения кода для задач агентов.

Часто задаваемые вопросы

Урок «Общая память и взаимодействие между агентами» бесплатный?

Да — полный текст урока «Общая память и взаимодействие между агентами» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.

Чему я научусь в уроке «Общая память и взаимодействие между агентами»?

Реализуйте уровень общей памяти с хранилищем ключей и значений, из которого агенты читают данные и записывают их, обеспечивая асинхронное сотрудничество без жёсткой связанности. Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать AI Engineering Academy?

Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Общая память и взаимодействие между агентами»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке AI Engineering Academy?

Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Почему один агент упирается в пределы
  2. Шаблон «оркестратор — подчинённый агент»
  3. Создание мультиагентных конвейеров с LangGraph
  4. Общая память и взаимодействие между агентами
← Назад к AI Engineering Academy