fix(ai写作): 技能流式每步主动让出事件循环,custom 事件实时刷给前端

前端 skill_search_step 已是 pushImmediate(实时渲染),但事件在节点内的嵌套
agent.astream 里发出后没及时让出事件循环,被攒到整段跑完才一次性吐到 SSE,
前端看着像卡死。每个 chunk 处理完 await asyncio.sleep(0) 主动让出,促使刚 emit
的 custom 事件实时刷给前端(对齐 draft_chunk 的实时行为)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
fengxiangdebaoziandClaude Opus 4.8 committed 2026-06-25 00:48:57 +08:00
1 parent 8f773d3f1b
commit a4a67c1a00
1 file changed
+6
@@ -210,6 +210,7 @@ async def run_skill_via_real_agent(
init_state, config=run_config, context=run_context, stream_mode="values"
):
msgs = chunk.get("messages") or []
emitted = False
for m in msgs:
mid = getattr(m, "id", None)
if not mid or mid in seen_msg_ids:
@@ -221,11 +222,16 @@ async def run_skill_via_real_agent(
break
tname = (tc.get("name") or "").strip() or "工具"
emit_query(f"技能「{skill_name}」调用 {tname}:{_summarize_tool_args(tc.get('args') or {})}")
emitted = True
# 记录最后一条 AIMessage 正文(含最终 JSON 素材)
if isinstance(m, AIMessage):
text = _content_to_text(m.content)
if text.strip():
final_text = text
# 关键:每个 chunk 处理完主动让出事件循环,促使刚 emit 的 custom 事件**实时**
# 刷给前端 SSE;否则嵌套 astream 会把它们攒到整段跑完才一次性吐出(前端看着像卡死)。
if emitted:
await asyncio.sleep(0)
try:
await asyncio.wait_for(_drive(), timeout=max(timeout_s, _REAL_AGENT_TIMEOUT_SECONDS))