Files
map-client-vue/src/server/LLMService.ts
2025-10-14 14:18:20 +08:00

237 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import OpenAI from 'openai';
import type { LLMConfig, Tool } from '../types/index.js';
export class LLMService {
private openai?: OpenAI;
private config?: LLMConfig;
/**
* 配置 LLM 服务
*/
configure(config: LLMConfig): void {
this.config = config;
if (config.provider === 'openai' && config.apiKey) {
this.openai = new OpenAI({
apiKey: config.apiKey,
baseURL: config.baseUrl
});
}
}
/**
* 根据用户输入和工具模式生成参数
*/
async generateParameters(
userInput: string,
tool: Tool
): Promise<Record<string, any>> {
if (!this.openai || !this.config?.enabled) {
throw new Error('LLM 服务未配置或未启用');
}
const systemPrompt = this.buildParameterGenerationPrompt(tool);
try {
const response = await this.openai.chat.completions.create({
model: this.config.model,
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userInput }
],
temperature: this.config.temperature || 0.1,
max_tokens: this.config.maxTokens || 1000,
response_format: { type: 'json_object' }
});
const content = response.choices[0]?.message?.content;
if (!content) {
throw new Error('LLM 未返回有效响应');
}
return JSON.parse(content);
} catch (error) {
if (error instanceof Error) {
throw new Error(`LLM 调用失败: ${error.message}`);
}
throw new Error('LLM 调用失败: 未知错误');
}
}
/**
* 分析用户意图并选择合适的工具
*/
async analyzeIntent(
userInput: string,
availableTools: Tool[]
): Promise<{
selectedTool?: string;
confidence: number;
reasoning: string;
suggestedParameters?: Record<string, any>;
}> {
if (!this.openai || !this.config?.enabled) {
return {
confidence: 0,
reasoning: 'LLM 服务未启用,请手动选择工具'
};
}
const systemPrompt = this.buildIntentAnalysisPrompt(availableTools);
try {
const response = await this.openai.chat.completions.create({
model: this.config.model,
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userInput }
],
temperature: this.config.temperature || 0.1,
max_tokens: this.config.maxTokens || 1000,
response_format: { type: 'json_object' }
});
const content = response.choices[0]?.message?.content;
if (!content) {
return {
confidence: 0,
reasoning: 'LLM 分析失败'
};
}
return JSON.parse(content);
} catch (error) {
console.error('LLM 意图分析失败:', error);
return {
confidence: 0,
reasoning: 'LLM 分析过程中出现错误'
};
}
}
/**
* 生成对话响应
*/
async generateResponse(
userInput: string,
context?: string
): Promise<string> {
if (!this.openai || !this.config?.enabled) {
throw new Error('LLM 服务未配置或未启用');
}
const messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }> = [
{
role: 'system',
content: `你是一个 MCP (Model Context Protocol) 客户端的智能助手。你可以帮助用户:
1. 理解和使用各种 MCP 服务器提供的工具
2. 分析工具执行结果并给出建议
3. 协助配置 MCP 服务器
请用友好、专业的语调回复用户。${context ? `\n\n当前上下文${context}` : ''}`
},
{ role: 'user', content: userInput }
];
try {
const response = await this.openai.chat.completions.create({
model: this.config.model,
messages,
temperature: this.config.temperature || 0.7,
max_tokens: this.config.maxTokens || 2000
});
return response.choices[0]?.message?.content || '抱歉,我无法生成回复。';
} catch (error) {
console.error('生成对话回复失败:', error);
throw new Error('生成回复失败,请稍后重试');
}
}
/**
* 构建参数生成提示
*/
private buildParameterGenerationPrompt(tool: Tool): string {
const properties = tool.inputSchema?.properties || {};
const required = tool.inputSchema?.required || [];
const propertiesDesc = Object.entries(properties)
.map(([key, prop]: [string, any]) => {
const isRequired = required.includes(key) ? ' (必需)' : ' (可选)';
const typeInfo = prop.type ? `类型: ${prop.type}` : '';
const enumInfo = prop.enum ? `可选值: ${prop.enum.join(', ')}` : '';
const desc = prop.description || '无描述';
return `- ${key}${isRequired}: ${desc}${typeInfo ? ` | ${typeInfo}` : ''}${enumInfo ? ` | ${enumInfo}` : ''}`;
})
.join('\n');
return `你是一个参数生成助手。根据用户的输入,为工具 "${tool.name}" 生成合适的参数。
工具描述: ${tool.description || '无描述'}
参数说明:
${propertiesDesc || '此工具无参数'}
要求:
1. 仔细分析用户输入,理解其真实意图
2. 为每个必需参数生成合理的值
3. 为相关的可选参数也生成适当的值
4. 如果无法确定某个参数的值,可以设置为 null 或合理的默认值
5. 返回标准的 JSON 对象格式
6. 确保生成的参数符合工具的要求
示例输出格式:
{
"parameter1": "value1",
"parameter2": "value2",
"parameter3": null
}`;
}
/**
* 构建意图分析提示
*/
private buildIntentAnalysisPrompt(tools: Tool[]): string {
const toolList = tools
.map(tool => `- ${tool.name}: ${tool.description || '无描述'}`)
.join('\n');
return `你是一个意图分析助手。分析用户输入,选择最合适的工具来完成用户的请求。
可用工具:
${toolList || '暂无可用工具'}
分析要求:
1. 仔细理解用户的真实意图和需求
2. 从可用工具中选择最匹配的工具
3. 评估匹配的置信度 (0-100数字越高表示越确定)
4. 提供详细的选择理由
5. 如果适用,预生成一些参数建议
返回 JSON 格式:
{
"selectedTool": "最匹配的工具名称如果没有合适的工具则为null",
"confidence": 85,
"reasoning": "选择这个工具的详细理由,或者为什么没有找到合适工具的原因",
"suggestedParameters": {
"param1": "建议的参数值",
"param2": "另一个参数值"
}
}`;
}
/**
* 检查服务是否可用
*/
isAvailable(): boolean {
return !!(this.openai && this.config?.enabled);
}
/**
* 获取当前配置
*/
getConfig(): LLMConfig | undefined {
return this.config;
}
}