Files
socaichat/test.md
T
gongzhiyongandClaude Sonnet 4.6 9c96913b4d
Deploy LangGraph Server to Azure Web App / build-and-deploy (push) Failing after 13s
Deploy LangGraph UI to Azure Static Web Apps / build-and-deploy (push) Failing after 49s
feat: enterprise product sprint — log protocol, tool rules, UX polish
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>
2026-04-12 13:01:02 +08:00

245 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 时渲染:
```html
<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)
- **判定**: 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)