feat: 更新AI教练服务,支持处理多个图片URL,优化饮食分析逻辑。调整相关DTO以支持数组格式的图片URL,并修改默认视觉模型为'qwen-vl-max'。

This commit is contained in:
richarjiang
2025-08-18 15:07:47 +08:00
parent eb71f845e5
commit 3d36ee90f0
3 changed files with 12 additions and 11 deletions

View File

@@ -42,7 +42,7 @@ export class AiCoachController {
userId, userId,
conversationId, conversationId,
userContent, userContent,
imageUrl: body.imageUrl, imageUrls: body.imageUrls,
}); });
let text = ''; let text = '';
for await (const chunk of readable) { for await (const chunk of readable) {
@@ -62,7 +62,7 @@ export class AiCoachController {
userId, userId,
conversationId, conversationId,
userContent, userContent,
imageUrl: body.imageUrl, imageUrls: body.imageUrls,
}); });
readable.on('data', (chunk) => { readable.on('data', (chunk) => {

View File

@@ -103,7 +103,7 @@ export class AiCoachService {
}); });
// 默认选择通义千问对话模型OpenAI兼容名可通过环境覆盖 // 默认选择通义千问对话模型OpenAI兼容名可通过环境覆盖
this.model = this.configService.get<string>('DASHSCOPE_MODEL') || 'qwen-flash'; this.model = this.configService.get<string>('DASHSCOPE_MODEL') || 'qwen-flash';
this.visionModel = this.configService.get<string>('DASHSCOPE_VISION_MODEL') || 'qwen-vl-plus'; this.visionModel = this.configService.get<string>('DASHSCOPE_VISION_MODEL') || 'qwen-vl-max';
} }
async createOrAppendMessages(params: { async createOrAppendMessages(params: {
@@ -200,7 +200,7 @@ export class AiCoachService {
conversationId: string; conversationId: string;
userContent: string; userContent: string;
systemNotice?: string; systemNotice?: string;
imageUrl?: string; imageUrls?: string[];
}): Promise<Readable> { }): Promise<Readable> {
// 解析指令(如果以 # 开头) // 解析指令(如果以 # 开头)
const commandResult = this.parseCommand(params.userContent); const commandResult = this.parseCommand(params.userContent);
@@ -227,8 +227,8 @@ export class AiCoachService {
} }
} else if (commandResult.command === 'diet') { } else if (commandResult.command === 'diet') {
// 使用视觉模型分析饮食图片 // 使用视觉模型分析饮食图片
if (params.imageUrl) { if (params.imageUrls) {
const dietAnalysis = await this.analyzeDietImage(params.imageUrl); const dietAnalysis = await this.analyzeDietImage(params.imageUrls);
messages.push({ messages.push({
role: 'user', role: 'user',
content: `用户通过拍照记录饮食,图片分析结果如下:\n${dietAnalysis}` content: `用户通过拍照记录饮食,图片分析结果如下:\n${dietAnalysis}`
@@ -418,7 +418,7 @@ export class AiCoachService {
* @param imageUrl 图片URL * @param imageUrl 图片URL
* @returns 饮食分析结果 * @returns 饮食分析结果
*/ */
private async analyzeDietImage(imageUrl: string): Promise<string> { private async analyzeDietImage(imageUrls: string[]): Promise<string> {
try { try {
const prompt = `请分析这张食物图片,识别其中的食物种类、分量,并提供以下信息: const prompt = `请分析这张食物图片,识别其中的食物种类、分量,并提供以下信息:
@@ -445,11 +445,11 @@ export class AiCoachService {
role: 'user', role: 'user',
content: [ content: [
{ type: 'text', text: prompt }, { type: 'text', text: prompt },
{ type: 'image_url', image_url: { url: imageUrl } as any }, ...imageUrls.map((imageUrl) => ({ type: 'image_url', image_url: { url: imageUrl } as any })),
] as any, ] as any,
}, },
], ],
temperature: 0, temperature: 1,
}); });
this.logger.log(`diet image analysis result: ${completion.choices?.[0]?.message?.content}`); this.logger.log(`diet image analysis result: ${completion.choices?.[0]?.message?.content}`);

View File

@@ -25,8 +25,9 @@ export class AiChatRequestDto {
@ApiProperty({ required: false, description: '当用户要记体重时的图片URL电子秤等' }) @ApiProperty({ required: false, description: '当用户要记体重时的图片URL电子秤等' })
@IsOptional() @IsOptional()
@IsString() @IsArray()
imageUrl?: string; @IsString({ each: true })
imageUrls?: string[];
@ApiProperty({ required: false, description: '是否启用流式输出', default: true }) @ApiProperty({ required: false, description: '是否启用流式输出', default: true })
@IsOptional() @IsOptional()