feat: 重构AI推荐功能并优化爬虫基础URL

重构前端AI推荐组件,移除本地过滤逻辑,改为从后端获取日期范围内的数据
新增AI服务模块,包含Prompt和推荐逻辑
为投标服务添加按日期范围查询接口
统一各爬虫服务的baseURL格式
This commit is contained in:
dmy
2026-01-12 18:59:17 +08:00
parent 61520e9ebf
commit 3d269ce9d1
16 changed files with 131 additions and 51 deletions

1
src/ai/Prompt.ts Normal file
View File

@@ -0,0 +1 @@
export const PromptString: string = `先给我说你统计了多少个项目。我只对辽宁、山东、江苏、浙江、福建、广东、广西、河北这些地方的海上风电、海上光伏、漂浮式光伏、滩涂光伏、滩涂风电、渔光互补项目感兴趣。从我提供的这些工程里面找到我感兴趣的工程。如果没有推荐的,也要给出思考过程。`;

View File

@@ -3,9 +3,6 @@ import { AiService } from './ai.service';
export class BidDataDto {
title: string;
url: string;
source: string;
publishDate: string;
}
export class BidsRequestDto {

83
src/ai/ai.service.ts Normal file
View File

@@ -0,0 +1,83 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import OpenAI from 'openai';
import { PromptString } from './Prompt';
export interface BidDataDto {
title: string;
}
export interface AIRecommendation {
title: string;
url: string;
source: string;
confidence: number;
}
@Injectable()
export class AiService {
private readonly logger = new Logger(AiService.name);
private openai: OpenAI;
constructor(private readonly configService: ConfigService) {
const apiKey = this.configService.get<string>('ARK_API_KEY');
this.openai = new OpenAI({
apiKey: apiKey || '',
baseURL: 'https://ark.cn-beijing.volces.com/api/v3',
timeout: 120000, // 120秒超时
});
}
async getRecommendations(bids: BidDataDto[]): Promise<AIRecommendation[]> {
this.logger.log('开始获取 AI 推荐');
this.logger.log(`发送给 AI 的数据数量: ${bids.length}`);
try {
const prompt =PromptString+ `请根据以下投标项目标题列表,推荐最值得关注的 5-10 个项目。请以 JSON 格式返回,格式如下:
[
{
"title": "项目标题",
"confidence": 推荐度(0-100的数字)
}
]
投标项目标题列表:
${JSON.stringify(bids.map(b => b.title), null, 2)}`;
this.logger.log('发给AI的内容',prompt);
const completion = await this.openai.chat.completions.create({
model: 'doubao-seed-1-6-lite-251015',
max_tokens: 32768,
reasoning_effort: 'medium',
messages: [
{
role: 'user',
content: prompt,
},
],
});
this.logger.log('AI API 响应成功');
const aiContent = completion.choices[0].message.content;
this.logger.log('AI 返回的内容:', aiContent);
if (!aiContent) {
throw new Error('AI 返回内容为空');
}
const jsonMatch = aiContent.match(/\[[\s\S]*\]/);
if (!jsonMatch) {
throw new Error('AI 返回格式不正确');
}
const recommendations = JSON.parse(jsonMatch[0]) as AIRecommendation[];
this.logger.log(`解析后的推荐结果: ${recommendations.length}`);
return recommendations;
} catch (error) {
this.logger.error('获取 AI 推荐失败:', error);
throw error;
}
}
}