fix: 修复 Agent 流式对话无响应和工具 schema 兼容性问题

- 在 `run_stream()` LLM 调用前 yield `think` 事件,前端即时显示"思考中..."
- 修复 tool schema 规范化逻辑:`{"function":{...}}` 格式缺少 `type` 字段导致 LLM API 拒绝
- 启动时从数据库加载自定义工具(`load_tools_from_db`),解决重启后工具丢失
- 前端 SSE 添加 60s 超时保护,任何事件类型均触发 `receivedFirstEvent`
- 流式失败自动降级到非流式 POST
- 添加 `scripts/seed_coding_agent.py` 和 `scripts/test_coding_agent.py`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
renjianbo
2026-05-02 00:38:41 +08:00
parent 342f3fcb16
commit 7aba0f9bc5
10 changed files with 662 additions and 36 deletions

View File

@@ -83,8 +83,9 @@ class AgentRuntime:
system_prompt=self.config.system_prompt,
user_id=self.config.user_id,
)
_mem_scope = self.config.memory_scope_id or self.config.user_id or self.config.name
self.memory = memory or AgentMemory(
scope_id=self.config.user_id or self.config.name,
scope_id=_mem_scope,
max_history=self.config.memory.max_history_messages,
persist=self.config.memory.persist_to_db,
)
@@ -398,6 +399,9 @@ class AgentRuntime:
"truncated": True}
return
# think 事件:告知前端 Agent 正在思考(让 UI 即时反馈,避免假死感)
yield {"type": "think", "content": "", "reasoning": None, "iteration": self.context.iteration}
# 调用 LLM
try:
response = await llm.chat(
@@ -687,7 +691,24 @@ class _LLMClient:
if self._config.extra_body:
kwargs["extra_body"] = self._config.extra_body
if tools:
kwargs["tools"] = tools
# Normalize tool schemas to OpenAI format: custom tools from the
# marketplace may be stored as {"name":..., "parameters":...}
# or {"function":{...}} without the required "type": "function".
normalized = []
for t in tools:
if isinstance(t, dict):
if t.get("type") == "function":
# Already in correct format: {"type":"function","function":{...}}
normalized.append(t)
elif "function" in t:
# Has function key but missing type: {"function":{...}}
normalized.append({"type": "function", "function": t["function"]})
else:
# Raw schema: {"name":..., "parameters":...}
normalized.append({"type": "function", "function": t})
else:
normalized.append(t)
kwargs["tools"] = normalized
kwargs["tool_choice"] = "auto"
start_time = time.perf_counter()