AgentCoreMemorySessionManager with batch_size > 1 restores agents without the changes made in their previous invocation
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 88/100
Hướng nghiên cứu
Bắt đầu tại src/bedrock_agentcore/memory/integrations/strands/session_manager.py: read_agent (~L474) lấy payload[0] của event AGENT mới nhất, trong khi flush (L1137-L1182) thêm SessionAgents theo thứ tự cũ trước, nên lần lưu cuối cùng không bao giờ được đọc. Dán script repro của issue (data plane in-memory, batch_size=10) để tái hiện model_calls mắc kẹt ở 1, sau đó đổi index và xác nhận agent được khôi phục đọc 3. Xong nghĩa là repro chạy qua cho state, sliding-window history, interrupt resume và overflow, với một regression test được thêm bên cạnh các test session_manager hiện có.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
With batch_size > 1, an agent restored by AgentCoreMemorySessionManager does not get the changes made in its previous invocation. This happens when the app builds a new Agent and session manager for each request, the usual way to serve many sessions since session_id is fixed in AgentCoreMemoryConfig. The restore raises and logs nothing: the agent just starts from an older state.
What breaks, with batch_size=10:
- Values written to
agent.stateduring an invocation are missing in the next one. In the repro below, a counter kept inagent.statenever goes past 1. - Since 1.23.1, Strands' default conversation manager,
SlidingWindowConversationManager, stops limiting the history across requests. Each restore comes back withremoved_message_count=0and every message of the session, so each request sends the whole conversation to the model. Withwindow_size=4, eight requests sent 1, 3, 5, 7, 9, 11, 13 and 15 messages. On 1.20.0 they sent 1, 3, 5, 5, 5, 5, 5 and 5. - Since 1.23.1, an interrupt cannot be resumed from a new agent: the call with the
interruptResponseraisesValueError: Received interrupt responses but agent is not in interrupt state. On 1.20.0, or withbatch_size=1, it resumes. - After a context-window overflow, Strands removes the oldest messages, but the next restore brings them back. This one also happens on 1.20.0.
Cause (links to v1.24.0):
Strands saves the agent as a SessionAgent, which holds agent.state, the conversation manager state and the interrupt state. It calls sync_agent after each new message and once more at the end of the invocation. With batch_size > 1, each SessionAgent is appended to a buffer (L400). The buffer is flushed as one AGENT event whose payload holds those SessionAgents, oldest first (L1137-L1182). On restore, read_agent takes the newest AGENT event and reads payload[0] (L474), the oldest SessionAgent in it.
The first sync_agent after a restore always writes, so each invocation's AGENT event starts with the SessionAgent the agent was restored from, and the changes saved after it are never read back.
Up to 1.23.0, the SessionAgent saved at the end of the invocation stayed in the buffer until the next flush, so it started a later event and was restored. Since 1.23.1 (#664), the flush at AfterInvocationEvent runs after that last sync_agent (L948-L954), so the end-of-invocation SessionAgent lands in the same event and is lost too. That is why the sliding-window and interrupt cases only fail since 1.23.1.
Repro, with no AWS account: the data plane is an in-memory fake that returns events newest first, which is what read_agent expects from ListEvents. Each turn builds a new agent on the same session, as a server does for each request, and a hook counts model calls in agent.state.
import copy, os
os.environ.update(AWS_ACCESS_KEY_ID="x", AWS_SECRET_ACCESS_KEY="x", AWS_DEFAULT_REGION="eu-west-1", AWS_ENDPOINT_URL="http://127.0.0.1:9")
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
from strands import Agent
from strands.hooks import BeforeModelCallEvent
from strands.models.model import Model
class InMemoryDataPlane:
def __init__(self):
self.events = []
def create_event(self, **params):
self.events.append(copy.deepcopy(params))
return {"event": {"eventId": f"e{len(self.events)}"}}
def list_events(self, **params):
def matches(event):
metadata = event.get("metadata") or {}
return all(
metadata.get(f["left"]["metadataKey"], {}).get("stringValue") == f["right"]["metadataValue"]["stringValue"]
for f in params.get("filter", {}).get("eventMetadata", [])
)
return {"events": [e for e in reversed(self.events) if matches(e)][: params["maxResults"]]}
class Session:
region_name = "eu-west-1"
def __init__(self, plane):
self.plane = plane
def client(self, name, **kwargs):
return self.plane
class EchoModel(Model):
def update_config(self, **kwargs): pass
def get_config(self): return {}
def structured_output(self, *args, **kwargs): raise NotImplementedError
async def stream(self, messages, *args, **kwargs):
yield {"messageStart": {"role": "assistant"}}
yield {"contentBlockDelta": {"contentBlockIndex": 0, "delta": {"text": "ok"}}}
yield {"contentBlockStop": {"contentBlockIndex": 0}}
yield {"messageStop": {"stopReason": "end_turn"}}
def count_model_calls(event):
event.agent.state.set("model_calls", (event.agent.state.get("model_calls") or 0) + 1)
def new_agent(plane):
manager = AgentCoreMemorySessionManager(
AgentCoreMemoryConfig(memory_id="m", session_id="s", actor_id="a", batch_size=10),
region_name="eu-west-1",
boto_session=Session(plane),
)
agent = Agent(model=EchoModel(), session_manager=manager, callback_handler=None)
agent.hooks.add_callback(BeforeModelCallEvent, count_model_calls)
return agent, manager
plane = InMemoryDataPlane()
for turn in (1, 2, 3):
agent, manager = new_agent(plane)
with manager:
agent(f"question {turn}")
print(f"after turn {turn}: model_calls={agent.state.get('model_calls')}") # expected 1, 2, 3
restored, _ = new_agent(plane)
print("restored:", restored.state.get("model_calls")) # expected 3
Expected: model_calls is 1, 2 and 3 after the three turns, and the restored agent reads 3, which is what batch_size=1 gives. Actual, on every version below: model_calls=1 after each turn, then restored: None. The newest AGENT event holds two SessionAgents whose state is {} and {"model_calls": 1}, and read_agent returns the first.
Suggested fix: read_agent reads payload[-1] instead of payload[0]. With that one-line change, the repro restores 3, and the sliding-window, interrupt and overflow cases behave as with batch_size=1. Sessions stored by affected versions then restore the last SessionAgent they saved, which keeping only the latest SessionAgent in the buffer would not do.
Versions tested: bedrock-agentcore 1.20.0 with strands-agents 1.45.0, 1.23.1 with 1.57.2, and 1.24.0 with 1.57.2 and 1.58.0, on Python 3.13. main has the same code as 1.24.0.
- Ngôn ngữ chính
- Python
- Star
- 776
- Fork
- 153
- Merge trung bình
- 1 ngày 8 giờ
- Pull request đã merge (30 ngày)
- 15
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của aws/bedrock-agentcore-sdk-python
-
AgentCoreMemorySessionManager discards the two boto3 clients MemoryClient builds; boto_session and boto_client_config are not passed throughCó thể đã có người làm @avneetbansal-aws đã nhận 10 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
aws/bedrock-agentcore-sdk-python#681 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug high-severity
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
aws/bedrock-agentcore-sdk-python#680 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[Bug] update_message fails with parameter validation error when SessionMessage.message_id is a Strands positional integer indexCó thể đã có người làm @citizen204 đã nhận 100 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
aws/bedrock-agentcore-sdk-python#556 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
CodeInterpreter should not require a region argumentCó thể đã có người làm @citizen204 đã nhận 115 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
aws/bedrock-agentcore-sdk-python#511 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
aws/bedrock-agentcore-sdk-python#496 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của aws/bedrock-agentcore-sdk-python
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
UKGovernmentBEIS/inspect_ai#5781 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Bump .cicd to wamp-cicd 4c2f9ac: `just land` refuses open A18 decisions, `just where` lists themĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 82/100
crossbario/cfxdb#139 ·
-
Bump .cicd to wamp-cicd 4c2f9ac: `just land` refuses open A18 decisions, `just where` lists themĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 84/100
crossbario/txaio#241 ·
-
UX
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
mediajunkie/piper-morgan-product#1963 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 64/100