Общая память и взаимодействие между агентами
Реализуйте уровень общей памяти с хранилищем ключей и значений, из которого агенты читают данные и записывают их, обеспечивая асинхронное сотрудничество без жёсткой связанности.
«Общая память и взаимодействие между агентами» — бесплатный урок 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 — локальная установка не требуется.
Все уроки этого курса
- Почему один агент упирается в пределы
- Шаблон «оркестратор — подчинённый агент»
- Создание мультиагентных конвейеров с LangGraph
- Общая память и взаимодействие между агентами