wip: first-chunk debug logging — paused waiting for platform-agent logs
This commit is contained in:
parent
2a23b30f83
commit
c34db0e6c0
5 changed files with 61 additions and 111 deletions
|
|
@ -1,90 +1,38 @@
|
||||||
{
|
{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"timestamp": "2026-04-23T11:46:45.938Z",
|
"timestamp": "2026-04-24T12:16:09.301Z",
|
||||||
"phase": "04",
|
"phase": "04",
|
||||||
"phase_name": "Matrix MVP: shared agent context and context management commands",
|
"phase_name": "matrix-mvp-shared-agent-context-and-context-management",
|
||||||
"phase_dir": ".planning/phases/04-matrix-mvp-shared-agent-context-and-context-management-comma",
|
"phase_dir": "04-matrix-mvp-shared-agent-context-and-context-management-comma",
|
||||||
"plan": 3,
|
"plan": null,
|
||||||
"task": 3,
|
"task": null,
|
||||||
"total_tasks": 3,
|
"total_tasks": null,
|
||||||
"status": "paused",
|
"status": "paused",
|
||||||
"completed_tasks": [
|
"completed_tasks": [
|
||||||
{
|
{"id": 1, "name": "docker-compose config mount + MATRIX_AGENT_REGISTRY_PATH", "status": "done"},
|
||||||
"id": 1,
|
{"id": 2, "name": "debug logging in sdk/real.py (_stream_agent_events)", "status": "done"},
|
||||||
"name": "Стабилизировать Matrix MVP runtime: numeric platform_chat_id mapping, staged attachments, clean vendored platform repos",
|
{"id": 3, "name": "debug logging in platform-agent service.py", "status": "done"}
|
||||||
"status": "done",
|
|
||||||
"commit": "4524a6a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "Локализовать missing-first-chunk bug, оформить финальный bug report и очистить runtime до thin upstream integration boundary",
|
|
||||||
"status": "done",
|
|
||||||
"commit": "0c2884c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Перейти на direct upstream AgentApi per request, убрать local wrapper из prod path и зафиксировать AGENT_BASE_URL как основной runtime contract",
|
|
||||||
"status": "done",
|
|
||||||
"commit": "7d58dd1"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"remaining_tasks": [
|
"remaining_tasks": [
|
||||||
{
|
{"id": 4, "name": "run docker compose up --build and get platform-agent logs with stream_event lines", "status": "not_started"},
|
||||||
"id": 4,
|
{"id": 5, "name": "analyze logs: content_type and langgraph_node to find where first chunk is lost", "status": "not_started"},
|
||||||
"name": "Решить, закрываем ли Phase 04 окончательно или продолжаем Matrix через live smoke в реальном окружении",
|
{"id": 6, "name": "fix in service.py based on findings (filter by node, handle list content, or capture subagent output)", "status": "not_started"}
|
||||||
"status": "not_started"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"name": "Если двигаемся дальше по Matrix, прогнать text/tool/file smoke на direct AgentApi per-request path и проверить отсутствие regressions",
|
|
||||||
"status": "not_started"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"name": "Если начинаем новую surface, открыть follow-up phase для prod messenger architecture без собственного transport layer",
|
|
||||||
"status": "not_started"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"blockers": [
|
|
||||||
{
|
|
||||||
"description": "В worktree остаётся посторонний локальный diff в core/handlers/message.py, не связанный с direct AgentApi fix",
|
|
||||||
"type": "technical",
|
|
||||||
"workaround": "Не смешивать с runtime/surface работой без отдельной задачи; handoff commit этот файл не включает"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
|
"blockers": [],
|
||||||
"human_actions_pending": [
|
"human_actions_pending": [
|
||||||
{
|
{"action": "run docker compose up --build and reproduce the alphabet/image truncation bug", "context": "Need platform-agent logs with DEBUG level to see stream_event lines", "blocking": true}
|
||||||
"action": "Выбрать следующий трек: Matrix live validation или планирование новой prod surface вроде Telegram/Max",
|
|
||||||
"context": "Кодовый фикс уже сделан и запушен; дальше работа зависит от продуктового направления, а не от transport-debug",
|
|
||||||
"blocking": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": "При следующем отдельном planning/cleanup коммите привести STATE/roadmap в полное соответствие с direct upstream per-request решением",
|
|
||||||
"context": "Локальный STATE уже обновлён как checkpoint, но handoff WIP commit включает только HANDOFF и .continue-here",
|
|
||||||
"blocking": false
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"decisions": [
|
"decisions": [
|
||||||
{
|
{"decision": "Bug is in platform-agent service.py __astream, not in surfaces bot", "rationale": "Logs show first text chunk already truncated at index=0 level", "phase": "04"},
|
||||||
"decision": "Для prod path Matrix surface больше не держит собственный transport layer; используется прямой upstream AgentApi с fresh connection per request",
|
{"decision": "deepagents uses dispatcher+subagent architecture", "rationale": "create_deep_agent wraps SubAgentMiddleware with general-purpose subagent", "phase": "04"},
|
||||||
"rationale": "Это убрало reuse-sensitive загрязнение между запросами, после чего missing-first-chunk симптом перестал воспроизводиться локально",
|
{"decision": "astream_events v2 processes on_chat_model_stream from ALL nodes without filtering", "rationale": "service.py has no namespace/node filtering", "phase": "04"}
|
||||||
"phase": "04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"decision": "AGENT_BASE_URL принят как основной runtime contract; AGENT_WS_URL оставлен только как backward-compat fallback в env wiring",
|
|
||||||
"rationale": "Так surface говорит с платформой по их реальному API contract, а не через локальный ws shim",
|
|
||||||
"phase": "04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"decision": "Для следующих surfaces не строить custom transport wrapper поверх платформы",
|
|
||||||
"rationale": "Surface должна владеть integration/session boundary, а не альтернативной stream semantics",
|
|
||||||
"phase": "04"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"uncommitted_files": [
|
"uncommitted_files": [
|
||||||
".planning/STATE.md",
|
"sdk/real.py (debug logging added)",
|
||||||
"core/handlers/message.py"
|
"docker-compose.yml (config volume mount added)",
|
||||||
|
"config/matrix-agents.example.yaml (label names updated)",
|
||||||
|
"external/platform-agent/src/agent/service.py (debug logging added, in submodule)"
|
||||||
],
|
],
|
||||||
"next_action": "При возобновлении сначала прочитать обновлённый handoff, затем выбрать один из двух треков: либо Matrix live smoke на direct AgentApi per-request path, либо новая phase/spec для prod surface без собственного transport layer",
|
"next_action": "Run: docker compose up --build. Send a message that triggers the bug (e.g. 'Напомни алфавит' after sending an image). Look for stream_event lines in platform-agent-1 logs. Check content_type and langgraph_node values for truncated responses.",
|
||||||
"context_notes": "Старый checkpoint про platform triage устарел. После диалога с платформой runtime переведён на прямой upstream AgentApi с fresh connection per request, локальный wrapper убран из prod path, tests прошли, commit 7d58dd1 запушен в origin/feat/matrix-direct-agent-prototype. Важный вывод для будущей архитектуры surfaces: upstream transport считать authoritative, а локально держать только lifecycle, serialization, attachment forwarding, error mapping и reconciliation."
|
"context_notes": "Investigating first-chunk truncation bug in Matrix bot responses. The bug appears when agent uses tools (image analysis) OR when images are in context. Platform-agent uses deepagents framework (dispatcher+subagent pattern). The hypothesis is that on_chat_model_stream events from multiple graph nodes are all forwarded as MsgEventTextChunk without filtering, OR that chunk.content is sometimes a list instead of str causing validation issues. Added logging to confirm. The fix will likely be in service.py: either filter by langgraph_node or handle list content type."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,53 @@
|
||||||
---
|
---
|
||||||
|
context: phase
|
||||||
phase: 04-matrix-mvp-shared-agent-context-and-context-management-comma
|
phase: 04-matrix-mvp-shared-agent-context-and-context-management-comma
|
||||||
task: 3
|
task: 4
|
||||||
total_tasks: 3
|
total_tasks: 6
|
||||||
status: paused
|
status: in_progress
|
||||||
last_updated: 2026-04-23T11:46:45.938Z
|
last_updated: 2026-04-24T12:16:09.301Z
|
||||||
---
|
---
|
||||||
|
|
||||||
<current_state>
|
<current_state>
|
||||||
Phase 04 кодово стабилизирована вокруг direct upstream `AgentApi` per request. Коммит `7d58dd1` уже запушен в `origin/feat/matrix-direct-agent-prototype`. Старый checkpoint в этом файле устарел: после обратной связи от платформы мы убрали extra wrapper из prod path, перестали переиспользовать один websocket между запросами и после этого missing-first-chunk симптом перестал воспроизводиться локально.
|
Debugging first-chunk truncation bug in Matrix bot. Logging added to both sdk/real.py and external/platform-agent/src/agent/service.py. Waiting for user to run docker compose up --build and share platform-agent logs with stream_event lines.
|
||||||
</current_state>
|
</current_state>
|
||||||
|
|
||||||
<completed_work>
|
<completed_work>
|
||||||
|
|
||||||
- Весь ранее собранный Matrix MVP контекст остаётся валидным: numeric `platform_chat_id`, staged attachments, shared workspace, context commands и Docker packaging уже на месте.
|
- docker-compose.yml: added `./config:/app/config:ro` volume mount so MATRIX_AGENT_REGISTRY_PATH works
|
||||||
- Продовый runtime path переведён на direct upstream client через `sdk/upstream_agent_api.py`; локальный `sdk/agent_api_wrapper.py` удалён из runtime path.
|
- config/matrix-agents.example.yaml: updated labels to Platform/Media
|
||||||
- `sdk/real.py` теперь на каждый `send_message` и `stream_message` создаёт новый `AgentApi`, делает `connect()`, читает stream и сразу `close()`.
|
- sdk/real.py: added structlog debug logging in _stream_agent_events (logs each chunk index + text[:40])
|
||||||
- `AGENT_BASE_URL` зафиксирован как основной runtime contract; `AGENT_WS_URL` оставлен только как backward-compat fallback в env wiring.
|
- external/platform-agent/src/agent/service.py: added logging of langgraph_node, content_type, content[:60] for every on_chat_model_stream event
|
||||||
- Добавлена регрессия на reuse-sensitive missing-first-chunk сценарий и обновлены runtime/integration tests; `uv run pytest tests -q` прошёл (`196 passed`), `ruff` на затронутых файлах clean.
|
|
||||||
- Кодовый фикс закоммичен и запушен: `7d58dd1` (`fix: use direct agent api per request`).
|
Bot is running and user confirmed it starts correctly with MATRIX_PLATFORM_BACKEND=real.
|
||||||
- В сессионных выводах зафиксирован новый архитектурный принцип для следующих surfaces: не строить свой transport layer, держать только thin integration/session boundary над upstream transport.
|
|
||||||
</completed_work>
|
</completed_work>
|
||||||
|
|
||||||
<remaining_work>
|
<remaining_work>
|
||||||
|
|
||||||
- Перед следующим кодом выбрать направление:
|
- Task 4: Get platform-agent debug logs (docker compose up --build, reproduce truncation, share stream_event lines)
|
||||||
- если продолжаем Matrix, прогнать live smoke в реальном окружении на text/tool/file flow и проверить отсутствие regressions на direct per-request path;
|
- Task 5: Analyze: check content_type (str vs list), check langgraph_node (which graph node produces the first chunk)
|
||||||
- если переходим к Telegram/Max-подобной работе, открыть новую phase/spec под prod surface architecture.
|
- Task 6: Fix service.py based on findings
|
||||||
- Привести `.planning/STATE.md` и roadmap в полностью каноничное состояние отдельным planning/cleanup шагом, если хотим закрепить этот checkpoint не только через handoff.
|
|
||||||
- Не смешивать дальнейшую surface/runtime работу с отдельным локальным diff в `core/handlers/message.py`, пока это не станет явной задачей.
|
|
||||||
</remaining_work>
|
</remaining_work>
|
||||||
|
|
||||||
<decisions_made>
|
<decisions_made>
|
||||||
|
|
||||||
- Matrix prod path должен использовать прямой upstream `AgentApi`, а не surface-owned wrapper с кастомной stream semantics.
|
- Bug confirmed to be in platform-agent, NOT in surfaces bot: our sdk/real.py logs show chunk index=0 already has truncated text (e.g. ' Д Е Ё...' instead of 'А Б В Г Д...')
|
||||||
- Fresh connection per request принят как дефолтный lifecycle для этой surface, потому что именно reuse websocket оказался чувствительной точкой для missing-first-chunk симптома.
|
- deepagents framework uses SubAgentMiddleware: main dispatcher agent + general-purpose subagent
|
||||||
- `AGENT_BASE_URL` это честный runtime contract; ws URL normalization допустим только как backward-compat env fallback.
|
- service.py processes ALL on_chat_model_stream events from astream_events v2 with no node filtering
|
||||||
- Для следующих surfaces надо думать терминами `integration boundary` и `runtime contract`, а не терминами "написать свой transport layer".
|
- Two leading hypotheses: (A) chunk.content is a list for some events (multimodal), causing silent skip/error; (B) events from wrong graph node are being captured/not captured
|
||||||
</decisions_made>
|
</decisions_made>
|
||||||
|
|
||||||
<blockers>
|
<blockers>
|
||||||
- Подтверждённого локального Matrix blocker после `7d58dd1` больше нет; дальше это вопрос product direction и live validation, а не active transport-firefight.
|
- Need user to run docker compose up --build and share platform-agent logs with DEBUG output
|
||||||
- В worktree остаётся посторонний локальный diff в `core/handlers/message.py`; не смешивать его с будущими surface/runtime изменениями без отдельной задачи.
|
|
||||||
</blockers>
|
</blockers>
|
||||||
|
|
||||||
<context>
|
<context>
|
||||||
Важная ментальная модель теперь такая:
|
The deepagents architecture: create_deep_agent creates a main orchestrator with SubAgentMiddleware wrapping a general-purpose subagent. When astream_events v2 runs, it may emit on_chat_model_stream from both the main agent's LLM call AND the subagent's LLM call. service.py captures ALL of them. The first chunk of the actual response might be from the subagent (not forwarded to client properly), while the main agent's response starts mid-sentence because it "sees" the subagent's output in its tool result context.
|
||||||
|
|
||||||
- upstream transport authoritative; `surfaces` владеет только lifecycle, serialization, attachment forwarding, error mapping и reconciliation.
|
Two key things to look for in logs:
|
||||||
- Старый narrative "ждём platform triage перед любыми transport changes" больше не актуален; transport change уже сделан и дал положительный эффект.
|
1. content_type=list → fix is `chunk.content[0].get("text", "")` or similar
|
||||||
- Предыдущий handoff и текущий `STATE.md` были написаны до этого решения, поэтому их надо читать как исторический контекст, а не как последнюю истину.
|
2. langgraph_node varies between chunks → fix is to filter to the correct node (e.g. only "agent" node)
|
||||||
- Проверка на false completion ничего критичного не показала: grep задел только фразу "compatibility placeholder" в `04-01-SUMMARY.md`, а не реальный незаполненный summary.
|
|
||||||
- Текущие локальные non-handoff diff: `.planning/STATE.md` и `core/handlers/message.py`.
|
|
||||||
</context>
|
</context>
|
||||||
|
|
||||||
<next_action>
|
<next_action>
|
||||||
Start with:
|
Start with: docker compose up --build. Then send a message with image context (e.g. send an image first, then ask 'Напомни алфавит'). Share platform-agent-1 logs — specifically the stream_event lines showing ns= and content_type= values.
|
||||||
|
|
||||||
1. Открыть этот обновлённый handoff, а не опираться на старый checkpoint про platform triage.
|
|
||||||
2. Выбрать трек: Matrix live smoke или новая prod-surface phase.
|
|
||||||
3. Если снова полезем в runtime, не возвращать custom transport wrapper и persistent shared websocket без очень сильной причины и регрессионных тестов.
|
|
||||||
</next_action>
|
</next_action>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
agents:
|
agents:
|
||||||
- id: agent-1
|
- id: agent-1
|
||||||
label: Analyst
|
label: Platform
|
||||||
- id: agent-2
|
- id: agent-2
|
||||||
label: Research
|
label: Media
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ services:
|
||||||
- platform-agent
|
- platform-agent
|
||||||
volumes:
|
volumes:
|
||||||
- workspace:/workspace
|
- workspace:/workspace
|
||||||
|
- ./config:/app/config:ro
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
||||||
10
sdk/real.py
10
sdk/real.py
|
|
@ -4,6 +4,8 @@ import asyncio
|
||||||
from collections.abc import AsyncIterator
|
from collections.abc import AsyncIterator
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import structlog
|
||||||
|
|
||||||
from sdk.interface import (
|
from sdk.interface import (
|
||||||
Attachment,
|
Attachment,
|
||||||
MessageChunk,
|
MessageChunk,
|
||||||
|
|
@ -16,6 +18,8 @@ from sdk.interface import (
|
||||||
from sdk.prototype_state import PrototypeStateStore
|
from sdk.prototype_state import PrototypeStateStore
|
||||||
from sdk.upstream_agent_api import AgentApi, MsgEventSendFile, MsgEventTextChunk
|
from sdk.upstream_agent_api import AgentApi, MsgEventSendFile, MsgEventTextChunk
|
||||||
|
|
||||||
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RealPlatformClient(PlatformClient):
|
class RealPlatformClient(PlatformClient):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -157,7 +161,13 @@ class RealPlatformClient(PlatformClient):
|
||||||
) -> AsyncIterator[object]:
|
) -> AsyncIterator[object]:
|
||||||
attachment_paths = self._attachment_paths(attachments)
|
attachment_paths = self._attachment_paths(attachments)
|
||||||
event_stream = chat_api.send_message(text, attachments=attachment_paths or None)
|
event_stream = chat_api.send_message(text, attachments=attachment_paths or None)
|
||||||
|
chunk_index = 0
|
||||||
async for event in event_stream:
|
async for event in event_stream:
|
||||||
|
if isinstance(event, MsgEventTextChunk):
|
||||||
|
logger.debug("agent_chunk", index=chunk_index, text=repr(event.text[:40]))
|
||||||
|
chunk_index += 1
|
||||||
|
else:
|
||||||
|
logger.debug("agent_event", index=chunk_index, type=type(event).__name__)
|
||||||
yield event
|
yield event
|
||||||
|
|
||||||
def _build_chat_api(self, chat_id: str):
|
def _build_chat_api(self, chat_id: str):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue