CANN-LLM WebUI:打造国产 LLM 推理的"驾驶舱"
cann组织链接:https://atomgit.com/cann
ops-nn仓库链接:https://atomgit.com/cann/ops-nn
🎯 目标
- 实时显示:吞吐、延迟、显存、NPU 利用率
- 可视化:各优先级队列状态、KV Cache 分布
- 交互功能:查看/取消正在运行的请求
- 日志流:滚动显示最新推理日志
- 技术栈:纯 C++ 后端 + WebSocket + Vue3 前端(嵌入式)
✅ 所有数据来自 CANN Profiling API + 内部调度器状态
一、系统架构
Metrics
Request Events
Cancel Request
CANN-LLM Engine
Metrics Collector
Event Bus
WebSocket Server
Vue3 WebUI
HTTP DELETE
二、后端实现(C++)
1. 指标采集器(MetricsCollector)
cpp
// metrics_collector.h
struct SystemMetrics {
double tokens_per_sec = 0;
double avg_latency_ms = 0;
size_t gpu_memory_used = 0;
float npu_utilization = 0.0f;
struct QueueStats {
int pending = 0;
int running = 0;
} high, medium, low;
std::vector<std::string> recent_logs;
};
class MetricsCollector {
SystemMetrics metrics_;
std::mutex mtx_;
public:
void update_from_engine(const EngineState& state) {
std::lock_guard lock(mtx_);
// 从调度器获取队列状态
metrics_.high = state.scheduler.get_queue_stats(Priority::HIGH);
// 从 hcll 获取显存
metrics_.gpu_memory_used = hcllQueryUsedMemory();
// 从 CANN Profiling API 获取 NPU 利用率
metrics_.npu_utilization = cann::profiling::get_device_util(0);
// 计算吞吐(滑动窗口)
metrics_.tokens_per_sec = state.token_counter.rate_last_10s();
}
SystemMetrics get_snapshot() {
std::lock_guard lock(mtx_);
return metrics_;
}
};
🔑
cann::profiling::get_device_util()通过aclprofAPI 实现
2. WebSocket 服务(嵌入式)
使用轻量级库(如 uWebSockets 或 Boost.Beast):
cpp
// webui_server.cpp
void WebUIServer::start_websocket() {
server.listen(8081, [&](auto* ws) {
// 新客户端连接
auto metrics_json = json_serializer(metrics_collector_.get_snapshot());
ws->send(metrics_json);
// 每 500ms 推送更新
start_timer(500ms, [ws, this]() {
if (ws->is_open()) {
auto update = json_serializer(metrics_collector_.get_snapshot());
ws->send(update);
}
});
});
}
3. 活跃请求列表接口
cpp
// 提供 /api/requests?status=running
std::vector<RequestInfo> get_active_requests() {
std::vector<RequestInfo> list;
for (auto& seq : scheduler_.get_all_sequences()) {
if (!seq->is_finished() && !seq->is_cancelled()) {
list.push_back({
.id = seq->id(),
.priority = to_string(seq->priority()),
.prompt_len = seq->prompt_len(),
.generated_len = seq->generated_len(),
.kv_blocks = seq->block_table.size(),
.created_at = seq->timestamp()
});
}
}
return list;
}
三、前端实现(嵌入式 Vue3)
将前端资源编译为 C++ 字符数组(或打包进 binary):
webui/index.html(简化)
html
<template>
<div class="dashboard">
<!-- 核心指标卡片 -->
<div class="metric-card">
<h3>吞吐</h3>
<p>{{ metrics.tokens_per_sec.toFixed(1) }} tokens/s</p>
</div>
<!-- 队列状态 -->
<div class="queue-bar">
<div class="bar high" :style="{width: highRatio + '%'}">High</div>
<div class="bar medium" :style="{width: mediumRatio + '%'}">Medium</div>
<div class="bar low" :style="{width: lowRatio + '%'}">Low</div>
</div>
<!-- 活跃请求表 -->
<table>
<tr v-for="req in activeRequests" :key="req.id">
<td>{{ req.id }}</td>
<td>{{ req.priority }}</td>
<td>{{ req.generated_len }}</td>
<td>
<button @click="cancelRequest(req.id)">❌ Cancel</button>
</td>
</tr>
</table>
<!-- 实时日志 -->
<div class="log-panel">
<div v-for="log in logs" :key="log">{{ log }}</div>
</div>
</div>
</template>
<script>
export default {
data() {
return { metrics: {}, activeRequests: [], logs: [] };
},
mounted() {
// 连接 WebSocket
const ws = new WebSocket('ws://localhost:8081');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.metrics = data.metrics;
this.activeRequests = data.requests;
this.logs = data.logs.slice(-50); // 保留最近 50 行
};
},
methods: {
cancelRequest(id) {
fetch(`/requests/${id}`, { method: 'DELETE' })
.then(() => this.refresh()); // 重新拉取列表
}
}
}
</script>
💡 使用
xxd -i index.html > webui_embedded.h将前端嵌入 C++ 二进制
四、关键可视化组件
1. KV Cache 热力图
- X 轴:Block ID
- Y 轴:Layer ID
- 颜色:引用计数(红色=高共享,蓝色=独占)
- 点击 block 可查看所属请求
2. NPU 利用率时间序列
- 折线图显示过去 5 分钟利用率
- 阈值线(80%)标红预警
3. 请求生命周期甘特图
- 每个请求显示:排队时间 + 推理时间 + 生成 token 数
- 支持按 priority 着色
五、部署与访问
启动服务后:
bash
./llm_server --enable-webui --port 8080 --webui-port 8081
浏览器访问:
http://<server-ip>:8081
无需额外依赖,所有静态资源由 C++ 二进制提供。
六、安全与生产建议
- 认证 :添加
--webui-token=xxx,前端需携带 Token - 只读模式 :可通过
--webui-readonly禁用取消按钮 - 日志脱敏:自动过滤 prompt 中的敏感词(如 API Key)
七、结语:透明即信任
一个优秀的推理系统,不仅要跑得快 ,更要看得清。
通过内建 WebUI 控制台,CANN-LLM 实现了:
- 运维友好:快速定位瓶颈
- 开发友好:直观验证调度策略
- 用户友好:自助管理请求
这标志着国产 AI 软件栈从 "黑盒加速" 走向 "白盒可控" 的重要一步。
🚀 CANN-LLM WebUI 将随 v1.0 正式版一同开源
是否希望下一篇深入 如何用 CANN Profiler 生成火焰图并嵌入 WebUI ,或提供 完整的 CI/CD 流水线配置(GitLab CI + Ascend 云)?欢迎告诉我!