本项目使用 Node.js 和 TypeScript 实现一个高性能的图像描述微服务,展示 JavaScript 在多模态 AI 应用中的强大能力。
# 创建项目目录
mkdir image-description-service
cd image-description-service
# 初始化项目
npm init -y
npm install typescript ts-node @types/node
npm install express dotenv openai
npm install -D @types/express
# 初始化 TypeScript 配置
npx tsc --init
# SiliconFlow API 配置
SILICONFLOW_API_KEY=sk-*******************************************
SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1
types.ts
)export interface ImageDescriptionRequest {
imageUrl: string;
language?: string;
model?: string;
}
export interface ImageDescriptionResponse {
description: string;
chunks: string[];
metadata: {
processingTime: number;
model: string;
};
}
imageDescriptionService.ts
)import OpenAI from 'openai';
import dotenv from 'dotenv';
import {
ImageDescriptionRequest,
ImageDescriptionResponse
} from './types';
dotenv.config();
export class ImageDescriptionService {
private openai: OpenAI;
constructor() {
this.openai = new OpenAI({
apiKey: process.env.SILICONFLOW_API_KEY,
baseURL: process.env.SILICONFLOW_BASE_URL
});
}
async describeImage(
request: ImageDescriptionRequest
): Promise<ImageDescriptionResponse> {
const startTime = Date.now();
try {
const response = await this.openai.chat.completions.create({
model: request.model || "Qwen/Qwen2-VL-72B-Instruct",
messages: [
{
role: "user",
content: [
{
type: "image_url",
image_url: { url: request.imageUrl }
},
{
type: "text",
text: `用${request.language || '中文'}描述这张图片`
}
]
}
],
stream: true
});
const chunks: string[] = [];
let fullDescription = '';
for await (const chunk of response) {
const content = chunk.choices[0]?.delta?.content || '';
if (content) {
chunks.push(content);
fullDescription += content;
}
}
return {
description: fullDescription,
chunks,
metadata: {
processingTime: Date.now() - startTime,
model: request.model || "Qwen/Qwen2-VL-72B-Instruct"
}
};
} catch (error) {
console.error('Image description error:', error);
throw new Error('Failed to describe image');
}
}
}
app.ts
)import express from 'express';
import { ImageDescriptionService } from './imageDescriptionService';
const app = express();
const port = process.env.PORT || 3000;
const descriptionService = new ImageDescriptionService();
app.use(express.json());
app.post('/describe-image', async (req, res) => {
try {
const { imageUrl, language, model } = req.body;
const result = await descriptionService.describeImage({
imageUrl,
language,
model
});
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(port, () => {
console.log(`服务运行在 http://localhost:${port}`);
});
client.ts
)import axios from 'axios';
async function describeImage() {
try {
const response = await axios.post('http://localhost:3000/describe-image', {
imageUrl: 'https://sf-maas-uat-prod.oss-cn-shanghai.aliyuncs.com/dog.png',
language: '中文'
});
console.log('图像描述:', response.data.description);
console.log('处理时间:', response.data.metadata.processingTime, 'ms');
} catch (error) {
console.error('调用失败:', error);
}
}
describeImage();
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
快来体验 siliconflow 图像描述服务:https://cloud.siliconflow.cn/i/vnCCfVaQ
快来体验 siliconflow 图像描述服务:https://cloud.siliconflow.cn/i/vnCCfVaQ
快来体验 siliconflow 图像描述服务:https://cloud.siliconflow.cn/i/vnCCfVaQ