feat(agent): add module-level debug logging for canvas execution flow (#16200)

Summary

Add module-level debug logging to track Agent canvas execution flow
(Closes #9306), enabling developers to diagnose component invocation,
input/output states, and variable resolution without modifying
production code.

Also fix related bugs in message.py: re.sub backreference issue and
unawaited _save_to_memory coroutine causing silent memory save failures.

Changes

agent/canvas.py: log workflow start, component invocation, and component
completion
agent/component/agent_with_tools.py: log Agent parameter resolution and
LLM invocation path; standardize json.dumps usage
agent/component/base.py: log get_input() variable resolution branches
agent/component/message.py: fix re.sub backreference issue; properly
await _save_to_memory coroutine

Design

Uses module-level loggers (logging.getLogger(__name__)) to support
selective debugging: LOG_LEVELS=agent=DEBUG
Zero performance impact in production (INFO level by default)
Works with existing PUT /system/config/log API for runtime level changes

Closes #9306

Note: While adding debug logging, I discovered and fixed two related
bugs in message.py:
- re.sub replacement value was interpreted as regex backreference
instead of literal string
- _save_to_memory coroutine was not properly awaited, causing silent
failures

---------

Co-authored-by: wills <willsgao@163.com>
This commit is contained in:
Willsgao
2026-06-29 09:41:16 +08:00
committed by yzc
parent dc07b6ca8f
commit 78db4e949b
4 changed files with 61 additions and 5 deletions

View File

@@ -284,12 +284,18 @@ class Message(ComponentBase):
return
for n, v in kwargs.items():
content = re.sub(n, v, content)
if v is not None:
content = re.sub(n, str(v), content)
self.set_output("downloads", downloads)
self.set_output("content", content)
self._convert_content(content)
self._save_to_memory(content)
try:
loop = asyncio.get_running_loop()
except RuntimeError:
asyncio.run(self._save_to_memory(content))
else:
asyncio.run_coroutine_threadsafe(self._save_to_memory(content), loop)
def thoughts(self) -> str:
return ""