6.2冲刺
昨天的成就: 完成了AI服务需求分析与技术方案设计,实现了 aiService 核心模块的图片识别功能,配置了环境变量,耗时11小时
遇到的困难: 通义千问API响应格式兼容性问题(数组vs字符串),提示词工程优化以提升结构化输出,Base64编码大图片内存占用问题
今天的任务: 开发AI识别控制器,集成进度跟踪模块,编写使用文档
📝 工作代码展示
- AI服务核心模块 (backend/src/services/aiService.js)
javascript
const axios = require('axios');
const fs = require('fs');
class AIService {
constructor() {
this.apiKey = process.env.DASHSCOPE_API_KEY;
this.apiUrl = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation';
}
/**
- 识别施工场景图片
- @param {Buffer} imageBuffer - 图片二进制数据
- @param {string} mimeType - 图片MIME类型
- @param {string} fileName - 文件名
- @returns {Object} 结构化识别结果
*/
async recognizeImage(imageBuffer, mimeType, fileName) {
// 1. 文件大小验证(最大10MB)
if (imageBuffer.length > 10 * 1024 * 1024) {
throw new Error('图片大小超过限制(最大10MB)');
}
// 2. 转换为Base64
const base64Image = imageBuffer.toString('base64');
// 3. 构建请求体
const requestBody = this.buildQwenRequest(base64Image, mimeType, fileName);
try {
// 4. 调用通义千问API
const response = await axios.post(this.apiUrl, requestBody, {
headers: {
'Authorization': Bearer ${this.apiKey},
'Content-Type': 'application/json',
'X-DashScope-SSE': 'disable' // 禁用流式输出
},
timeout: 120000 // 120秒超时
});
// 5. 解析响应
const rawText = this.parseResponse(response.data);
// 6. 提取结构化信息
return this.extractStructuredInfo(rawText);
} catch (error) {
console.error('AI识别失败:', error.message);
if (error.response) {
throw new Error(API调用失败: ${error.response.status} - ${JSON.stringify(error.response.data)});
}
throw new Error(AI服务异常: ${error.message});
}
}
/**
- 构建通义千问多模态请求
*/
buildQwenRequest(base64Image, mimeType, fileName) {
const prompt = `你是一位专业的施工现场安全与质量 inspector。请分析这张施工照片,重点关注以下方面:
【安全风险】
- 是否有人未佩戴安全帽?
- 是否有高空作业无防护?
- 是否有违规操作或危险行为?
【施工质量】
- 材料堆放是否规范?
- 施工工艺是否存在明显缺陷?
- 现场环境是否整洁有序?
【改进建议】
- 针对发现的问题,给出1-2条具体改进建议。
请用简洁的中文回答,分点列出,不要使用Markdown格式。`;
return JSON.stringify({
model: "qwen-vl-max",
input: {
messages: [
{
role: "user",
content: [
{ image: data:${mimeType};base64,${base64Image} },
{ text: prompt }
]
}
]
},
parameters: {
top_p: 0.8,
temperature: 0.5
}
});
}
/**
- 解析API响应
*/
parseResponse(responseData) {
if (!responseData.output?.choices?.[0]?.message?.content) {
throw new Error('无法解析AI响应:返回数据结构异常');
}
const content = responseData.output.choices[0].message.content;
// 兼容数组和字符串两种格式
if (Array.isArray(content)) {
return content
.filter(item => item.text)
.map(item => item.text)
.join('\n');
}
return content;
}
/**
- 提取结构化信息(简化版:按关键词分割)
*/
extractStructuredInfo(rawText) {
const sections = {
safetyRisks: '',
qualityIssues: '',
suggestions: ''
};
// 简单解析逻辑:按章节标题分割
const parts = rawText.split(/【([^】]+)】/);
for (let i = 1; i < parts.length; i += 2) {
const title = parts[i];
const content = parts[i + 1]?.trim() || '';
if (title.includes('安全风险')) {
sections.safetyRisks = content;
} else if (title.includes('施工质量')) {
sections.qualityIssues = content;
} else if (title.includes('改进建议')) {
sections.suggestions = content;
}
}
return {
success: true,
rawText: rawText,
structured: sections,
timestamp: new Date().toISOString()
};
}
}
module.exports = new AIService();
2. 环境变量配置 (.env.example)
properties
阿里云通义千问 API Key
获取方式:https://dashscope.console.aliyun.com/apiKey
DASHSCOPE_API_KEY=sk-your-api-key-here
服务器端口
PORT=3000
数据库连接
DB_HOST=localhost
DB_PORT=5432
DB_NAME=construction_db
DB_USER=admin
DB_PASSWORD=secret
3. 今日计划任务代码框架 (backend/src/controllers/aiController.js)
javascript
const express = require('express');
const multer = require('multer');
const aiService = require('../services/aiService');
const router = express.Router();
// 配置multer文件上传
const upload = multer({
storage: multer.memoryStorage(), // 使用内存存储
limits: {
fileSize: 10 * 1024 * 1024 // 10MB限制
},
fileFilter: (req, file, cb) => {
// 只接受图片
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('只支持图片文件'), false);
}
}
});
/**
-
单张图片识别接口
-
POST /api/progress/recognize
*/
router.post('/recognize', upload.single('image'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({
success: false,
message: '未上传图片文件'
});
}const { buffer, mimetype, originalname } = req.file;
// 调用AI服务
const result = await aiService.recognizeImage(buffer, mimetype, originalname);res.json({
success: true,
data: result
});
} catch (error) {
console.error('识别接口错误:', error);
res.status(500).json({
success: false,
message: error.message || '识别失败'
});
}
});
/**
-
批量图片识别接口
-
POST /api/progress/recognize-multiple
*/
router.post('/recognize-multiple', upload.array('images', 10), async (req, res) => {
try {
if (!req.files || req.files.length === 0) {
return res.status(400).json({
success: false,
message: '未上传图片文件'
});
}const results = [];
// 串行处理(避免并发过多导致API限流)
for (const file of req.files) {
try {
const result = await aiService.recognizeImage(
file.buffer,
file.mimetype,
file.originalname
);
results.push({
fileName: file.originalname,
...result
});
} catch (err) {
results.push({
fileName: file.originalname,
success: false,
error: err.message
});
}
}res.json({
success: true,
count: results.length,
data: results
});
} catch (error) {
console.error('批量识别接口错误:', error);
res.status(500).json({
success: false,
message: '批量识别失败'
});
}
});
module.exports = router;

浙公网安备 33010602011771号