Backend:
- config.ts: unified startup env validation (throw on missing critical vars)
- tool-executor: structured logToolCall() JSON, ToolStatus enum (success/partial_success/fallback_success/error), preValidateToolCall() hardcoded guards for ticket_detail/chart_generate, durationMs/inputSummary/resultCount on all execution log entries
- agent.ts: tool selection decision tree, chart-as-default-path prompt rules, source labels [知识库][工单][网络][推断]
- tool-defs.ts: applicable/not-applicable guidance on all 4 tools
- soc-client.ts: sandbox hardening (10k char limit, 15s timeout, output truncation, error classification), config.* accessors
- router.ts: preCheckRoute() rules — TK-xxx/工单/知识库 → enterprise direct; greetings → generalInput
- supervisor/types.ts: removed dead config fields (model/temperature/maxTokens/systemPrompt)
- Remove chat-agent (legacy entry point)
Frontend:
- MessageBubble: source badge rendering [知识库][工单][网络][推断], CitationChip [1][2] → clickable chips
- ThreadSidebar: auto-title from first message, collapsible search, long title truncation
- ToolCallStatus: tool-specific loading labels, collapse-all toggle for multi-tool
- main.tsx: conclusion-first layout (AI text above artifacts), draft persistence, IME fix, auto chip, multi-tool AnalysisBlock container, sort_key ordering, retry via soc:retry-tool
- chart-result: empty guard, chart/table toggle, multi-chart format support
- ticket-summary/knowledge-result: work-card quick actions (prefill with context)
- ActionBar: structured payload {text, taskType, sourceCardId}, source label UI
- index.css: card-enter slide animation, dot-bounce loading
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
13 KiB
13 KiB
SOC 8项优化 端到端验收测试报告
测试环境
- 后端: http://192.168.30.30:2024
- 前端: http://192.168.30.30:5173
- 代码版本: commit
4ad169e(main) - 测试时间: 2026-04-11
基础端点测试
| # | 端点 | 方法 | 状态码 | 响应 | 状态 |
|---|---|---|---|---|---|
| 1 | /ok | GET | 200 | {"ok":true} |
PASS |
| 2 | /info | GET | 200 | {"flags":{"assistants":true,"crons":false}} |
PASS |
| 3 | /threads | POST | 200 | 返回 thread_id, status: idle | PASS |
| 4 | 前端首页 | GET | 200 | 页面正常加载 | PASS |
第一轮:8项优化验收测试
| # | 测试项 | 状态 | 备注 |
|---|---|---|---|
| 1 | P0 Daytona sandbox 修复 | PASS | code_execute 工具成功调用,exit_code=0,stdout="5050",耗时1462ms |
| 2 | KB Agent retry 验证 | PASS | retry.ts 实现完整(3次重试,指数退避1s基准),已在 enterprise/tool-executor 中正确包裹 kb_search;本次测试 KB 正常响应无需重试 |
| 3 | ticket_detail 修复 | PASS | TK-xxxx 格式工单 ID 可正常查询详情,返回完整工单数据 |
| 4 | sourceType 字段 | PASS | 所有 ui items 均包含 sourceType 和 confidence 字段:sandbox-result(code_execution/high), ticket-summary(ticket_system/high), ticket-detail(ticket_system/high), canvas-doc(generated_doc/high), knowledge-result(internal_kb), search-result(external_web) |
| 5 | 自动工具路由准确性 | PASS (5/5) | "写报告"->writer, "网络安全新闻"->searcher, "Python排序算法"->coder, "公司网络安全规范"->enterprise(kb_search), "你好介绍自己"->generalInput |
| 6 | 企业风格输出 | PASS | 回复包含 [工单数据] 来源标注,结论/依据/建议三层结构完整,有风险提示和下一步建议 |
| 7 | 会话恢复 | PASS | 新 thread 发送工具触发消息后,GET /threads/{id}/state 返回 values.ui 数组包含 ticket-summary 卡片(含 sourceType/confidence),UI 状态正确持久化到 checkpoint |
第二轮:迭代修复验收测试
| # | 测试项 | 状态 | 备注 |
|---|---|---|---|
| R1 | ticket_detail 不同工单返回不同数据 | PASS | 查询 TK-2026-AE4DEA 和 TK-2026-9FA15E 分别返回正确的不同工单数据(P1-1 已修复) |
| R2 | sandbox 失败时友好提示 | PASS | 执行 import nonexistent_module_xyz 时 AI 给出中文分析(模块不存在、建议替代方案),未暴露原始技术栈错误 |
| R3 | 空工单列表友好提示 | PASS (代码验证) | ticket-summary 组件 tickets.length===0 时显示 "暂无工单" 居中提示文案 |
| R4 | Gen-UI 卡片 error 状态渲染 | PASS (代码验证) | 4个卡片组件均支持 errorMessage prop:有错误时边框变红(border-red-200),显示红色错误文案(text-red-700) |
| R5 | ToolCallStatus 失败状态红色X | PASS (代码验证) | 使用 XCircle + text-red-500 图标,失败时显示 "失败 . {工具名}" |
| R6 | ToolCallStatus 骨架屏过渡 | PASS (代码验证) | expanded && !uiItem 时渲染 animate-pulse 骨架屏(4行灰色占位条) |
详细测试记录
测试1:Daytona sandbox
- 请求: "帮我用Python计算1到100的和"
- 路由: supervisor -> coder
- 工具调用: code_execute, language=python, code=
sum(range(1, 101)) - 响应: exit_code=0, stdout="5050\n", duration_ms=1462
- UI 卡片: sandbox-result, props 含 sourceType="code_execution", confidence="high"
- 判定: PASS
测试2:KB Agent retry
- 代码验证:
src/agent/utils/retry.ts实现 executeWithRetry,支持指数退避 - 集成验证: enterprise/tool-executor.ts 中 kb_search 调用已包裹
executeWithRetry(() => kbSearch(query), 3, { backoffMs: 1000, exponential: true }) - 运行时验证: 发送 "我们公司的网络安全规范是什么",路由到 enterprise,调用 kb_search 成功返回 knowledge-result 卡片
- 日志检查: 未触发 retry 日志(KB 服务正常响应,未超时)
- 判定: PASS(代码实现正确,运行时无异常)
测试3:ticket_detail
- 请求: "查看最新工单的详情"
- 工具链: ticket_list(page=1) -> ticket_detail("TK-2026-296691")
- ticket_list 响应: 3条工单,TK-2026-296691/TK-2026-AE4DEA/TK-2026-9FA15E
- ticket_detail 响应: 返回完整工单详情(含 customer/engineer/urges/sla 等字段)
- UI 卡片: ticket-summary + ticket-detail,均含 sourceType="ticket_system"
- 判定: PASS
测试4:sourceType 字段
验证所有 ui 卡片 props 中的 sourceType 和 confidence 字段:
| 卡片类型 | sourceType | confidence |
|---|---|---|
| sandbox-result | code_execution | high |
| ticket-summary | ticket_system | high |
| ticket-detail | ticket_system | high |
| knowledge-result | internal_kb | (已确认存在) |
| search-result | external_web | (已确认存在) |
| canvas-doc | generated_doc | high |
- 判定: PASS
测试5:自动工具路由
| 输入 | 预期路由 | 实际路由 | 触发工具 | 状态 |
|---|---|---|---|---|
| "帮我写一个关于本季度运营情况的报告" | writer | writer | doc_create | PASS |
| "查一下最近的网络安全新闻" | searcher | searcher | google_search | PASS |
| "用Python写个排序算法" | coder | coder | (code_execute) | PASS |
| "我们公司的网络安全规范是什么" | enterprise | enterprise | kb_search | PASS |
| "你好,介绍一下自己" | generalInput | generalInput | (无工具) | PASS |
- 判定: PASS (5/5)
测试6:企业风格输出
- 请求: "分析最近3个工单的共同问题"
- 路由: enterprise
- 工具调用: ticket_list + 3x ticket_detail
- 输出结构验证:
- [工单数据] 来源标注: 出现多处,标注在每个关键结论前
- 结论段: 有,以"结论"开头
- 依据段: 有,以"依据"开头,按来源分组
- 建议段: 有,以"建议"开头,含建议行动/风险提示/下一步
- 判定: PASS
测试7:会话恢复
- 流程: 创建 thread -> 发送"查询最近的工单" -> 等待完成 -> GET /threads/{id}/state
- Thread ID: 8be68055-38a5-4c22-b614-601a2eaf206e
- 验证结果: values.ui 数组包含 1 个 ticket-summary 卡片,props 含 sourceType="ticket_system" 和 confidence="high"
- 判定: PASS(UI 状态正确持久化到 checkpoint,切换会话可恢复)
R1:ticket_detail UUID 映射修复验证
- 请求: "分别查看工单TK-2026-AE4DEA和TK-2026-9FA15E的详情"
- 路由: enterprise
- 结果: 2个 ticket-detail 卡片,id 分别为 TK-2026-AE4DEA (title="12e1") 和 TK-2026-9FA15E (title="123")
- toolStatus: 2x ticket_detail status=ok
- 判定: PASS(第一轮 P1-1 问题已修复,不同 TK-xxxx 正确返回不同工单)
R2:sandbox 失败友好提示
- 请求: "帮我运行这段Python代码:import nonexistent_module_xyz; print(nonexistent_module_xyz.hello())"
- 工具结果: exit_code=1, ModuleNotFoundError
- AI 回复: 中文分析 -- "模块 nonexistent_module_xyz 并不存在",给出表格化失败原因,提供修复建议(替换为真实模块名/自定义示例),未暴露原始 traceback
- UI 卡片: sandbox-result, exit_code=1
- 判定: PASS
R3:空工单列表友好提示
- 代码验证: ticket-summary/index.tsx 中
tickets.length === 0分支渲染<li className="px-4 py-6 text-center text-sm text-muted-foreground">暂无工单</li> - 判定: PASS(组件正确处理空状态)
R4:Gen-UI 卡片 error 状态
- 代码验证: 4个卡片组件均实现 errorMessage prop:
- knowledge-result: errorMessage 时 border-red-200/dark:border-red-900/40,显示红色文案
- ticket-summary: 同上
- search-result: 同上
- sandbox-result: 同上 + 失败时显示 "失败 (exit {code})"
- 后端集成: enterprise/tool-executor.ts 中 kb_search 失败时推送
sourceType: "error", confidence: "low", errorMessage: "知识库暂时无响应,已切换到网络搜索"并触发 google_search fallback - 判定: PASS
R5:ToolCallStatus 失败状态
- 代码验证: ToolCallStatus.tsx 中 isFailed 时渲染
<XCircle className="size-3.5 text-red-500 shrink-0" />,文案显示 "失败 . {工具中文名}" - 判定: PASS
R6:ToolCallStatus 骨架屏
- 代码验证: ToolCallStatus.tsx 中
expanded && !uiItem时渲染:<div className="mt-2 ml-7 space-y-2 animate-pulse"> <div className="h-3 bg-muted rounded w-3/4" /> <div className="h-3 bg-muted rounded w-1/2" /> <div className="h-3 bg-muted rounded w-5/6" /> <div className="h-8 bg-muted rounded w-full mt-3" /> </div> - 判定: PASS(展开时 UI 卡片未加载前显示骨架屏过渡动画)
第三轮:新功能验收测试
| # | 测试项 | 状态 | 备注 |
|---|---|---|---|
| R3-1 | chart-result 自动出图 | PASS | ticket-summary 后自动附带 chart-result 卡片,含2个图表:状态分布(已关闭:2, IN_PROGRESS:1) + 优先级分布(PRIORITY:2, NORMAL:1) |
| R3-2 | reply-draft 卡片 | PASS | 路由到 writer,调用 reply_draft 工具,返回 reply-draft 卡片(mode="customer",含专业客户回复内容) |
| R3-3 | next-actions 卡片 | PASS | 工单查询后自动附带 next-actions 卡片,含2条推荐动作;知识库查询后含"生成知识摘要"、"导出到文档" |
| R3-4 | citations 来源列表 | PASS | knowledge-result 含5条 citations(带 title/source);search-result 含8条 citations(带 title/source/url) |
| R3-5 | 卡片合并 deduplication | PASS (代码验证) | main.tsx 中 deduplicateUiItems() 按 card_id 去重,同 card_id 只保留最新态 |
R3-1:chart-result 自动出图
- 请求: "查询最近的工单"
- 路由: enterprise
- UI 卡片: 3个 -- ticket-summary + chart-result + next-actions
- chart-result 详情:
- title: "工单分布统计"
- charts[0]: title="状态分布", data=[{name:"已关闭", value:2}, {name:"IN_PROGRESS", value:1}]
- charts[1]: title="优先级分布", data=[{name:"PRIORITY", value:2}, {name:"NORMAL", value:1}]
- 判定: PASS
R3-2:reply-draft 卡片
- 请求: "帮我生成一份客户回复草稿,关于工单TK-2026-296691中LLM请求被拒绝的问题"
- 路由: writer
- 工具调用: reply_draft + doc_create
- UI 卡片: reply-draft (mode="customer") + canvas-doc
- reply-draft 内容: 专业客户回复,包含问题确认、排查方向(参数规范/鉴权/频率限制/额度)、建议步骤
- 判定: PASS
R3-3:next-actions 卡片
- 工单查询场景: actions = ["生成处理建议", "生成客户回复"],每条含 prompt 和 icon
- 知识库查询场景: actions = ["生成知识摘要", "导出到文档"]
- 判定: PASS
R3-4:citations 来源列表
- knowledge-result: 5条 citations,格式 {index, title, source}
- 示例: {index:1, title:"Taiji Agent 文档中心 - Openclaw相关配置说明", source:null}
- search-result: 8条 citations,格式 {index, title, source, url}
- 示例: {index:1, title:"推出OpenAI 安全研究员计划", source:"openai.com", url:"https://openai.com/..."}
- 前端渲染: knowledge-result/index.tsx 和 search-result/index.tsx 均实现 Citation 接口渲染来源列表
- 判定: PASS
R3-5:卡片合并 deduplication
- 代码验证: main.tsx:30
deduplicateUiItems()函数:- 遍历 items 按
props.card_id建立 Map,记录每个 card_id 最后出现的 index - filter 时只保留 card_id 对应最后 index 的项,无 card_id 的项全部保留
- 在消息渲染和会话恢复两处调用(L439, L588)
- 遍历 items 按
- 判定: PASS
问题清单
已修复
- P1-1 (R2已修复): ticket_detail UUID 映射异常
遗留问题
无 P0/P1 遗留问题。
P2 建议
- KB retry 机制在正常情况下无法触发运行时验证,建议后续添加集成测试模拟超时场景
- 骨架屏和 error 态为代码验证(非运行时触发),建议前端 Storybook 添加对应 story
- knowledge-result citations 中 source 字段部分为 null,建议后端 kb_search 返回时补充来源文档名
- chart-result 中 charts 子项缺少 type 字段(pie/bar),前端需靠 fallback 推断图表类型
通过率
第一轮: 7/7 通过 | 第二轮: 6/6 通过 | 第三轮: 5/5 通过 | 总计: 18/18
整体质量评分
9.2 / 10
- 核心功能(sandbox、工单、知识库、搜索、路由)全部正常
- sourceType/confidence 字段在所有卡片中正确注入
- 企业风格输出格式规范,来源标注完整
- 会话恢复 checkpoint UI 持久化正常
- KB retry + fallback 机制代码实现完善
- ticket_detail UUID 映射问题已修复
- Gen-UI error 态、ToolCallStatus 失败/骨架屏均已实现
- chart-result 自动出图(状态分布+优先级分布)
- reply-draft 卡片含 customer/internal 模式
- next-actions 推荐动作按场景智能生成
- citations 来源列表完整(知识库+搜索均有)
- deduplicateUiItems 卡片去重逻辑正确
- 扣分项:chart-result 缺少 type 字段(-0.3);knowledge citations source 部分为 null(-0.2);部分前端状态为代码验证(-0.3)