6.2冲刺

昨天的成就: 完成了AI服务需求分析与技术方案设计,实现了 aiService 核心模块的图片识别功能,配置了环境变量,耗时11小时
遇到的困难: 通义千问API响应格式兼容性问题(数组vs字符串),提示词工程优化以提升结构化输出,Base64编码大图片内存占用问题
今天的任务: 开发AI识别控制器,集成进度跟踪模块,编写使用文档
📝 工作代码展示

  1. 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;

posted @ 2026-06-18 21:27  sas1996  阅读(10)  评论(0)    收藏  举报