feat: P0 前端交互完善 + 测试报告更新
- ToolCallStatus 组件增强 - main.tsx P0 交互改进(textarea/光标/编辑消息) - 更新测试报告:9/12 通过,3 个外部服务问题 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
86cfa317b6
commit
dd70cadcc1
@@ -1,4 +1,6 @@
|
||||
import { Loader2, CheckCircle2 } from "lucide-react";
|
||||
import { Loader2, CheckCircle2, ChevronRight, ChevronDown } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui";
|
||||
|
||||
const TOOL_NAME_MAP: Record<string, string> = {
|
||||
kb_search: "搜索知识库",
|
||||
@@ -21,39 +23,115 @@ interface ToolCall {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface UIMsgLocal {
|
||||
id: string;
|
||||
type: string;
|
||||
name: string;
|
||||
props: Record<string, unknown>;
|
||||
metadata?: { message_id?: string };
|
||||
}
|
||||
|
||||
interface ToolCallStatusProps {
|
||||
toolCalls: ToolCall[];
|
||||
isLoading: boolean;
|
||||
// IDs of tool messages that exist (completed)
|
||||
completedToolIds?: Set<string>;
|
||||
uiItems?: UIMsgLocal[];
|
||||
stream?: Parameters<typeof LoadExternalComponent>[0]["stream"];
|
||||
components?: Parameters<typeof LoadExternalComponent>[0]["components"];
|
||||
}
|
||||
|
||||
function ToolCallRow({
|
||||
tc,
|
||||
isDone,
|
||||
uiItem,
|
||||
stream,
|
||||
components,
|
||||
}: {
|
||||
tc: ToolCall;
|
||||
isDone: boolean;
|
||||
uiItem?: UIMsgLocal;
|
||||
stream?: ToolCallStatusProps["stream"];
|
||||
components?: ToolCallStatusProps["components"];
|
||||
}) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const label = tc.name ? (TOOL_NAME_MAP[tc.name] ?? tc.name) : "工具调用";
|
||||
const canExpand = isDone && !!uiItem;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!canExpand}
|
||||
onClick={() => canExpand && setExpanded((v) => !v)}
|
||||
className="flex items-center gap-1.5 text-xs text-muted-foreground w-full text-left"
|
||||
>
|
||||
{canExpand ? (
|
||||
expanded ? (
|
||||
<ChevronDown className="size-3.5 shrink-0" />
|
||||
) : (
|
||||
<ChevronRight className="size-3.5 shrink-0" />
|
||||
)
|
||||
) : (
|
||||
<span className="size-3.5 shrink-0" />
|
||||
)}
|
||||
{isDone ? (
|
||||
<CheckCircle2 className="size-3.5 text-green-500 shrink-0" />
|
||||
) : (
|
||||
<Loader2 className="size-3.5 animate-spin shrink-0" />
|
||||
)}
|
||||
<span>{label}</span>
|
||||
<span>{isDone ? (canExpand ? (expanded ? "收起" : "查看结果") : "已完成") : "执行中..."}</span>
|
||||
</button>
|
||||
{expanded && uiItem && stream && components && (
|
||||
<div className="mt-2 ml-7">
|
||||
<LoadExternalComponent
|
||||
stream={stream}
|
||||
message={uiItem as any}
|
||||
components={components as any}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ToolCallStatus({
|
||||
toolCalls,
|
||||
isLoading,
|
||||
completedToolIds,
|
||||
uiItems = [],
|
||||
stream,
|
||||
components,
|
||||
}: ToolCallStatusProps) {
|
||||
if (!toolCalls.length) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 mb-1">
|
||||
<div className="flex flex-col gap-1.5 mb-1">
|
||||
{toolCalls.map((tc, i) => {
|
||||
const label = tc.name ? (TOOL_NAME_MAP[tc.name] ?? tc.name) : "工具调用";
|
||||
const isDone = !isLoading || (tc.id && completedToolIds?.has(tc.id));
|
||||
// Match UI item by tool name
|
||||
const uiItem = uiItems.find((ui) => {
|
||||
if (!tc.name) return false;
|
||||
const nameMap: Record<string, string> = {
|
||||
kb_search: "knowledge-result",
|
||||
ticket_list: "ticket-summary",
|
||||
ticket_detail: "ticket-detail",
|
||||
web_search: "search-result",
|
||||
google_search: "search-result",
|
||||
sandbox_run: "sandbox-result",
|
||||
};
|
||||
return ui.name === nameMap[tc.name];
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
<ToolCallRow
|
||||
key={tc.id ?? i}
|
||||
className="flex items-center gap-1.5 text-xs text-muted-foreground"
|
||||
>
|
||||
{isDone ? (
|
||||
<CheckCircle2 className="size-3.5 text-green-500 shrink-0" />
|
||||
) : (
|
||||
<Loader2 className="size-3.5 animate-spin shrink-0" />
|
||||
)}
|
||||
<span>{label}</span>
|
||||
<span>{isDone ? "已完成" : "执行中..."}</span>
|
||||
</div>
|
||||
tc={tc}
|
||||
isDone={!!isDone}
|
||||
uiItem={uiItem}
|
||||
stream={stream}
|
||||
components={components}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
+59
-17
@@ -107,9 +107,14 @@ function App() {
|
||||
threadId: currentThreadId ?? undefined,
|
||||
});
|
||||
|
||||
// Auto-scroll to bottom when messages update
|
||||
// Auto-scroll to bottom only when user is near the bottom
|
||||
useEffect(() => {
|
||||
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
const el = scrollContainerRef.current;
|
||||
if (!el) return;
|
||||
const isNearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 200;
|
||||
if (isNearBottom) {
|
||||
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}, [thread.messages]);
|
||||
|
||||
// Listen for open-canvas events from Gen-UI cards
|
||||
@@ -335,7 +340,7 @@ function App() {
|
||||
|
||||
{/* ── Right main area ── */}
|
||||
<div className="flex-1 flex flex-row min-w-0 overflow-hidden">
|
||||
<div className="flex-1 flex flex-col min-w-0" onDragOver={handleDragOver} onDrop={handleDrop}>
|
||||
<div className="flex-1 flex flex-col min-w-0 relative" onDragOver={handleDragOver} onDrop={handleDrop}>
|
||||
{/* Header */}
|
||||
<header className="shrink-0 border-b border-border px-4 py-3 flex items-center gap-3">
|
||||
{/* Hamburger for mobile */}
|
||||
@@ -360,7 +365,15 @@ function App() {
|
||||
</header>
|
||||
|
||||
{/* Messages */}
|
||||
<div className="flex-1 overflow-y-auto px-4 py-6 space-y-6">
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="flex-1 overflow-y-auto px-4 py-6 space-y-6 relative"
|
||||
onScroll={() => {
|
||||
const el = scrollContainerRef.current;
|
||||
if (!el) return;
|
||||
setShowScrollBtn(el.scrollHeight - el.scrollTop - el.clientHeight > 200);
|
||||
}}
|
||||
>
|
||||
{thread.messages.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center h-full gap-4 text-muted-foreground select-none">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
@@ -460,15 +473,42 @@ function App() {
|
||||
|
||||
return (
|
||||
<div key={message.id ?? idx} className="flex flex-col gap-3">
|
||||
{/* Tool call status */}
|
||||
{/* Tool call status (with inline expand/collapse for UI cards) */}
|
||||
{toolCalls.length > 0 && (
|
||||
<ToolCallStatus
|
||||
toolCalls={toolCalls}
|
||||
isLoading={thread.isLoading}
|
||||
completedToolIds={completedToolIds}
|
||||
uiItems={uiItems}
|
||||
stream={thread}
|
||||
components={ComponentMap as any}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* UI cards not matched to any tool call (standalone) */}
|
||||
{uiItems
|
||||
.filter((ui) => !toolCalls.some((tc) => {
|
||||
const nameMap: Record<string, string> = {
|
||||
kb_search: "knowledge-result",
|
||||
ticket_list: "ticket-summary",
|
||||
ticket_detail: "ticket-detail",
|
||||
web_search: "search-result",
|
||||
google_search: "search-result",
|
||||
sandbox_run: "sandbox-result",
|
||||
};
|
||||
return tc.name && ui.name === nameMap[tc.name];
|
||||
}))
|
||||
.map((ui: UIMsgLocal) => (
|
||||
<LoadExternalComponent
|
||||
key={ui.id}
|
||||
stream={thread}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
message={ui as any}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
components={ComponentMap as any}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Text reply */}
|
||||
{textContent && (
|
||||
<div className="group relative max-w-[85%] rounded-2xl rounded-bl-sm bg-muted text-foreground px-4 py-2.5 text-sm">
|
||||
@@ -483,18 +523,6 @@ function App() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* UI cards */}
|
||||
{uiItems.map((ui: UIMsgLocal) => (
|
||||
<LoadExternalComponent
|
||||
key={ui.id}
|
||||
stream={thread}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
message={ui as any}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
components={ComponentMap as any}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Regenerate button — only on last AI message, only when not loading */}
|
||||
{isLastAi && !thread.isLoading && (
|
||||
<div className="flex">
|
||||
@@ -538,6 +566,20 @@ function App() {
|
||||
<div ref={bottomRef} />
|
||||
</div>
|
||||
|
||||
{/* Scroll-to-bottom floating button */}
|
||||
{showScrollBtn && (
|
||||
<div className="absolute bottom-32 left-1/2 -translate-x-1/2 z-10">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => bottomRef.current?.scrollIntoView({ behavior: "smooth" })}
|
||||
className="rounded-full bg-background border border-border shadow-md p-2 text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
|
||||
title="滚动到底部"
|
||||
>
|
||||
<ChevronDown className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Control bar: tools (left) + model selector (right) */}
|
||||
<div className="shrink-0 border-t border-border px-4 pt-3 pb-0 flex items-center justify-between max-w-3xl mx-auto w-full">
|
||||
{/* Tool toggles */}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
- **开发机**: 192.168.30.30
|
||||
- **后端**: http://localhost:2024 (LangGraph Server)
|
||||
- **前端**: http://localhost:5173 (Vite dev server)
|
||||
- **测试时间**: 2026-04-11
|
||||
- **测试范围**: 全端点功能测试 + ChatGPT Enterprise 对标分析
|
||||
- **测试时间**: 2026-04-11 (第二轮 -- 代码改动后复测)
|
||||
- **测试范围**: 全端点功能测试 + 新增功能验证
|
||||
|
||||
---
|
||||
|
||||
@@ -14,77 +14,63 @@
|
||||
| # | 端点 / 功能 | 方法 | 状态 | 结果 |
|
||||
|---|------------|------|------|------|
|
||||
| 1 | `/ok` 健康检查 | GET | 200 | PASS - 返回 `{"ok":true}` |
|
||||
| 2 | 前端 `localhost:5173` | GET | 200 | PASS - 返回 Vite dev HTML |
|
||||
| 3 | Vite Proxy `/threads` | POST | 200 | PASS - 前端端口代理到后端 API,正确返回 thread JSON |
|
||||
| 2 | `/info` 服务信息 | GET | 200 | PASS - 返回 `{"flags":{"assistants":true,"crons":false}}` |
|
||||
| 3 | 前端 `localhost:5173` | GET | 200 | PASS - 返回 Vite dev HTML |
|
||||
| 4 | `/threads` 创建 Thread | POST | 200 | PASS - 返回 thread_id |
|
||||
| 5 | `/assistants/search` | POST | 200 | PASS - 返回 agent + chat 两个 assistant |
|
||||
| 6 | Supervisor 路由 → generalInput | POST stream | 200 | PASS - "你好" 正确路由到通用对话 |
|
||||
| 7 | Supervisor 路由 → enterprise (kb_search) | POST stream | 200 | PASS - 路由到 enterprise,触发 kb_search 工具 |
|
||||
| 5 | Vite Proxy `/threads` | POST | 200 | PASS - 前端端口代理到后端 API |
|
||||
| 6 | Supervisor 路由 → generalInput | POST stream | 200 | PASS - "你好" 正确路由到通用对话,生成流畅回复 |
|
||||
| 7 | Supervisor 路由 → enterprise (kb_search) | POST stream | 200 | PARTIAL - 路由正确,触发 kb_search 工具,但 KB Agent 超时 |
|
||||
| 8 | Supervisor 路由 → enterprise (ticket_list) | POST stream | 200 | PASS - 返回 3 条工单 + ticket-summary Gen-UI 卡片 |
|
||||
| 9 | Supervisor 路由 → searcher (google_search) | POST stream | 200 | PASS - 返回搜索结果 |
|
||||
| 10 | Supervisor 路由 → coder (sandbox_run) | POST stream | 200 | PARTIAL - 路由正确,LLM 生成代码正确,但 Daytona sandbox 执行失败 (400) |
|
||||
| 11 | Supervisor 路由 → writer | POST stream | 200 | PASS - 生成 canvas-doc Gen-UI 卡片,含完整 markdown 文档 |
|
||||
| 9 | Supervisor 路由 → enterprise (ticket_detail) | POST stream | 200 | PARTIAL - 路由正确,参数解析正确,但 Gongdan API 返回 404 |
|
||||
| 10 | Supervisor 路由 → searcher (google_search) | POST stream | 200 | PASS - 返回丰富搜索结果 (8 条新闻),内容质量高 |
|
||||
| 11 | Supervisor 路由 → coder (sandbox_run) | POST stream | 200 | PARTIAL - 路由正确,LLM 生成代码正确,Daytona sandbox 执行失败 (400) |
|
||||
| 12 | Supervisor 路由 → writer (canvas-doc) | POST stream | 200 | PASS - 生成 canvas-doc Gen-UI 卡片,含完整 markdown 文档 (833字) |
|
||||
|
||||
### 通过率: 10/11 通过 (90.9%), 1 个部分通过
|
||||
### 通过率: 9/12 通过 (75%), 3 个部分通过 (路由均正确,外部服务异常)
|
||||
|
||||
---
|
||||
|
||||
## 2. Vite Proxy 状态
|
||||
## 2. 启动问题修复记录
|
||||
|
||||
**结论: 正常工作**
|
||||
|
||||
通过 `http://localhost:5173/threads` POST 请求成功代理到后端 `http://localhost:2024/threads`,返回正确的 thread JSON。前端 SPA 可以在同源策略下无缝访问后端 API。
|
||||
### pdf-parse ESM 导入错误
|
||||
- **文件**: `src/agent/utils/file-service.ts`
|
||||
- **错误**: `SyntaxError: The requested module 'pdf-parse' does not provide an export named 'default'`
|
||||
- **原因**: pdf-parse v2.x 在 ESM 环境下没有 default export
|
||||
- **修复**: 改为 `import * as pdfParseModule from "pdf-parse"` + 运行时 fallback
|
||||
- **状态**: 已修复,服务正常启动
|
||||
|
||||
---
|
||||
|
||||
## 3. ChatGPT Enterprise 对标功能清单
|
||||
## 3. Gen-UI 卡片验证
|
||||
|
||||
### 已实现功能
|
||||
|
||||
| 功能 | ChatGPT Enterprise | SOC 实现 | 对标程度 |
|
||||
|------|-------------------|----------|---------|
|
||||
| 多轮对话 | 有 | 有 (LangGraph checkpointer) | 完整 |
|
||||
| 对话历史管理 | 有 (侧边栏) | 有 (ThreadSidebar + CRUD) | 完整 |
|
||||
| 模型切换 | GPT-4/4o | Flash/Auto/Pro 三模式 | 完整 |
|
||||
| 知识库检索 (RAG) | 有 (自定义 GPT + Files) | 有 (kb_search + Azure AI Search) | 完整 |
|
||||
| 网络搜索 | 有 (Browse) | 有 (google_search + web_search) | 完整 |
|
||||
| 代码解释器 | 有 (Code Interpreter) | 有 (coder agent + Daytona sandbox) | 部分 (sandbox 400 错误) |
|
||||
| 文档编辑/Canvas | 有 (Canvas) | 有 (writer agent + CanvasPanel) | 完整 |
|
||||
| Gen-UI 卡片渲染 | 无 (纯文本) | 有 (7 种卡片组件) | 超越 |
|
||||
| 工单系统集成 | 无 | 有 (ticket_list/detail + Gen-UI) | 超越 (企业特色) |
|
||||
| 深色/浅色主题 | 有 | 有 (ThemeToggle) | 完整 |
|
||||
| 文件上传 | 有 | 有 (图片+文件,拖拽+粘贴) | 完整 |
|
||||
| 图表生成 | 有 (Code Interpreter) | 有 (chart-result Gen-UI) | 完整 |
|
||||
| Markdown 渲染 | 有 | 有 (MessageBubble) | 完整 |
|
||||
| SSE 流式输出 | 有 | 有 (useStream) | 完整 |
|
||||
| 多 Agent 协作 | 无 (单 Agent) | 有 (Supervisor + 5 子 Agent) | 超越 |
|
||||
|
||||
### 尚未实现
|
||||
|
||||
| 功能 | ChatGPT Enterprise | SOC 状态 |
|
||||
|------|-------------------|---------|
|
||||
| 用户认证/SSO | 有 (SAML/OIDC) | 未实现 |
|
||||
| 用户权限管理 | 有 (Admin Console) | 未实现 |
|
||||
| 审计日志 | 有 | 未实现 |
|
||||
| 数据保留策略 | 有 (30 天) | 未实现 |
|
||||
| 自定义 GPT 创建 | 有 | 未实现 |
|
||||
| DALL-E 图片生成 | 有 | 未实现 |
|
||||
| 语音对话 | 有 | 未实现 |
|
||||
| 插件/Action 市场 | 有 | 未实现 (但 Agent 架构可扩展) |
|
||||
| 组件 | 对应工具/Agent | 是否触发 | SSE 中 ui 数组 |
|
||||
|------|--------------|---------|---------------|
|
||||
| ticket-summary | ticket_list | 是 | 有 props (total, tickets) |
|
||||
| canvas-doc | writer agent | 是 | 有 props (doc_id, title, _doc_content) |
|
||||
| search-result | google_search | 待验证 | 工具返回数据正确 |
|
||||
| knowledge-result | kb_search | 未触发 | KB Agent 超时 |
|
||||
| ticket-detail | ticket_detail | 未触发 | Gongdan API 404 |
|
||||
| sandbox-result | sandbox_run | 未触发 | Daytona 400 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 已实现的 Gen-UI 组件
|
||||
## 4. 新增改动验证
|
||||
|
||||
| 组件 | 对应工具/Agent |
|
||||
|------|--------------|
|
||||
| knowledge-result | kb_search |
|
||||
| ticket-summary | ticket_list |
|
||||
| ticket-detail | ticket_detail |
|
||||
| search-result | google_search / web_search |
|
||||
| sandbox-result | sandbox_run / code_execute |
|
||||
| canvas-doc | writer agent |
|
||||
| chart-result | coder agent |
|
||||
### 后端改动
|
||||
| 改动项 | 状态 | 备注 |
|
||||
|--------|------|------|
|
||||
| truncateMessages 上下文压缩 | PASS | TypeScript 编译通过,各 Agent 集成正确 |
|
||||
| router.ts 路由改进 | PASS | 路由到正确的 Agent (enterprise/generalInput/searcher/coder/writer) |
|
||||
| process-attachment.ts 附件处理 | PASS | 新文件已创建,TypeScript 编译通过 |
|
||||
| retry.ts 工具重试 | PASS | 新文件已创建,TypeScript 编译通过 |
|
||||
| pdf-parse ESM 修复 | PASS | 修复后服务正常启动 |
|
||||
|
||||
### 前端改动
|
||||
| 改动项 | 状态 | 备注 |
|
||||
|--------|------|------|
|
||||
| ToolCallStatus.tsx 新组件 | PASS | TypeScript 编译通过 |
|
||||
| ThreadSidebar.tsx 修改 | PASS | TypeScript 编译通过 |
|
||||
| main.tsx UX 改进 | PASS | TypeScript 编译通过 |
|
||||
|
||||
---
|
||||
|
||||
@@ -94,26 +80,35 @@
|
||||
- **端点**: coder agent → code_execute → Daytona API
|
||||
- **错误**: `Daytona create failed: 400`
|
||||
- **影响**: 代码解释器功能完全不可用
|
||||
- **建议**: 检查 DAYTONA_API_KEY 和 DAYTONA_API_URL 环境变量配置,验证 Daytona 服务可用性
|
||||
- **建议**: 检查 DAYTONA_API_KEY 和 DAYTONA_API_URL 环境变量配置
|
||||
|
||||
### P1 - KB Agent 超时
|
||||
- **端点**: enterprise agent → kb_search → KB Agent API
|
||||
- **错误**: `TimeoutError: The operation was aborted due to timeout`
|
||||
- **影响**: 知识库检索功能间歇性不可用
|
||||
- **建议**: 增加超时时间或检查 KB Agent 服务状态,已有 retry 工具可集成
|
||||
|
||||
### P2 - Gongdan API ticket_detail 404
|
||||
- **端点**: enterprise agent → ticket_detail → Gongdan API
|
||||
- **错误**: `Ticket detail failed: 404`
|
||||
- **影响**: 工单详情查看功能不可用
|
||||
- **建议**: 检查 Gongdan API 的 ticket_detail 端点路径和参数格式
|
||||
|
||||
---
|
||||
|
||||
## 6. 最大的 3 个质量风险
|
||||
## 6. 与上一次测试对比
|
||||
|
||||
### 风险 1: Daytona Sandbox 不可用 (严重)
|
||||
代码解释器是核心差异化功能之一。当前 Daytona sandbox 返回 400 错误,整个 coder agent 链路虽然路由和代码生成正常,但执行环节断裂。用户体验直接受损。
|
||||
|
||||
### 风险 2: 无用户认证体系 (高)
|
||||
当前系统无任何认证机制,所有 API 完全开放。对于企业级产品,这意味着:
|
||||
- 任何人都可以访问所有对话历史
|
||||
- 无法区分用户身份
|
||||
- 无法满足企业合规要求
|
||||
- 部署到公网后存在安全风险
|
||||
|
||||
### 风险 3: 错误处理和降级策略不够健壮 (中)
|
||||
- 工具调用失败时(如 Daytona 400),错误信息直接暴露给用户("工具执行失败")
|
||||
- 外部服务(KB Agent, Gongdan API, Jina, Serper)任一不可用时,缺乏优雅降级
|
||||
- SSE 流中断后无自动重连机制
|
||||
| 项目 | 上次 (4/11 第1轮) | 本次 (4/11 第2轮) | 变化 |
|
||||
|------|----------|----------|------|
|
||||
| 通过率 | 10/11 (90.9%) | 9/12 (75%) | 新增 3 项测试,发现更多外部服务问题 |
|
||||
| Router 路由正确率 | 6/6 (100%) | 7/7 (100%) | 新增 writer/coder 路由验证 |
|
||||
| kb_search | PASS | PARTIAL (超时) | 退化 -- KB Agent 服务不稳定 |
|
||||
| ticket_detail | 未测试 | PARTIAL (404) | 新增测试项 |
|
||||
| Daytona sandbox | PARTIAL (400) | PARTIAL (400) | 未修复 |
|
||||
| writer agent | PASS | PASS | 保持 |
|
||||
| google_search | PASS | PASS | 结果质量提升 |
|
||||
| pdf-parse 启动错误 | 无此问题 | 发现并修复 | 新增文件引入的问题 |
|
||||
| TypeScript 编译 | - | PASS | 所有新增代码编译通过 |
|
||||
|
||||
---
|
||||
|
||||
@@ -122,29 +117,18 @@
|
||||
### 7.5 / 10
|
||||
|
||||
**加分项:**
|
||||
- 架构设计成熟 -- Supervisor + 5 子 Agent 路由,可扩展性强
|
||||
- Gen-UI 卡片系统是亮点,超越 ChatGPT 的纯文本回复
|
||||
- 工具链丰富 (知识库、工单、搜索、代码、文档) 且均正确路由
|
||||
- 前端完成度高 (主题切换、文件上传、拖拽、Canvas)
|
||||
- SSE 流式输出 + useStream 集成流畅
|
||||
- 架构设计成熟 -- Supervisor + 多子 Agent 路由,所有路由 100% 正确
|
||||
- Gen-UI 卡片系统正常工作 (ticket-summary, canvas-doc)
|
||||
- TypeScript 编译 0 错误,代码质量好
|
||||
- 新增功能 (truncateMessages, retry, process-attachment) 集成完整
|
||||
- 错误处理改进 -- 工具失败时给出用户友好的建议
|
||||
|
||||
**减分项:**
|
||||
- Daytona sandbox 不可用 (-1)
|
||||
- 无认证体系 (-1)
|
||||
- 缺少自动化测试覆盖 (-0.5)
|
||||
- KB Agent 超时不稳定 (-0.5)
|
||||
- Gongdan ticket_detail 404 (-0.5)
|
||||
- 无认证体系 (-0.5)
|
||||
|
||||
---
|
||||
|
||||
## 与上一次测试 (2026-04-10) 对比
|
||||
|
||||
| 项目 | 上次 | 本次 | 变化 |
|
||||
|------|------|------|------|
|
||||
| 通过率 | 6/8 (75%) | 10/11 (90.9%) | 提升 |
|
||||
| Router 路由正确率 | 6/6 (100%) | 6/6 (100%) | 保持 |
|
||||
| kb_search | PARTIAL (超时) | PASS | 修复 |
|
||||
| Daytona sandbox | PARTIAL (400) | PARTIAL (400) | 未修复 |
|
||||
| 新增测试 | - | Vite Proxy, assistants API, writer Gen-UI | +3 项 |
|
||||
|
||||
---
|
||||
|
||||
*测试人: SOC Test Agent | 生成时间: 2026-04-11*
|
||||
*测试人: SOC Test Agent | 生成时间: 2026-04-11 (第二轮复测)*
|
||||
|
||||
Reference in New Issue
Block a user