feat(agent): add 4 real-time tools for enhanced agent capabilities
Add the following real-time tools to ImmigrationToolsService: - get_current_datetime: Get current date/time with timezone support - web_search: Search internet for latest immigration news/policies (Google CSE) - get_exchange_rate: Query real-time currency exchange rates (for investment immigration) - fetch_immigration_news: Fetch latest immigration announcements All tools include graceful degradation with fallback responses when external APIs are unavailable. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
022b1eb608
commit
4c125f3276
|
|
@ -78,6 +78,8 @@ ${config.conversationStyle || '专业但不生硬,用简洁明了的语言解
|
|||
|
||||
## 工具使用说明
|
||||
你有以下工具可以使用:
|
||||
|
||||
### 知识与记忆工具
|
||||
1. **search_knowledge**: 搜索知识库获取最新的移民政策信息
|
||||
2. **check_off_topic**: 检查用户问题是否与移民相关
|
||||
3. **collect_assessment_info**: 收集用户信息用于评估
|
||||
|
|
@ -85,6 +87,12 @@ ${config.conversationStyle || '专业但不生硬,用简洁明了的语言解
|
|||
5. **save_user_memory**: 保存用户的重要信息以便后续对话记忆
|
||||
6. **get_user_context**: 获取用户的历史背景信息和之前的对话记忆
|
||||
|
||||
### 实时查询工具
|
||||
7. **get_current_datetime**: 获取当前日期和时间(支持时区设置)
|
||||
8. **web_search**: 搜索互联网获取最新移民政策、新闻动态等实时信息
|
||||
9. **get_exchange_rate**: 查询实时货币汇率(用于投资移民金额换算)
|
||||
10. **fetch_immigration_news**: 获取香港移民相关的最新新闻和政策公告
|
||||
|
||||
## 已积累的经验
|
||||
${config.accumulatedExperience || '暂无'}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ConversationContext } from '../claude-agent.service';
|
||||
import { KnowledgeClientService } from '../../knowledge/knowledge-client.service';
|
||||
|
||||
|
|
@ -14,7 +15,10 @@ export interface Tool {
|
|||
|
||||
@Injectable()
|
||||
export class ImmigrationToolsService {
|
||||
constructor(private knowledgeClient: KnowledgeClientService) {}
|
||||
constructor(
|
||||
private knowledgeClient: KnowledgeClientService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get all available tools for the agent
|
||||
|
|
@ -164,6 +168,88 @@ export class ImmigrationToolsService {
|
|||
required: ['query'],
|
||||
},
|
||||
},
|
||||
// ========== 实时工具 ==========
|
||||
{
|
||||
name: 'get_current_datetime',
|
||||
description: '获取当前的日期和时间,用于回答与时间相关的问题',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
timezone: {
|
||||
type: 'string',
|
||||
description: '时区,默认为 Asia/Hong_Kong',
|
||||
},
|
||||
format: {
|
||||
type: 'string',
|
||||
enum: ['full', 'date', 'time'],
|
||||
description: '返回格式:full-完整日期时间,date-仅日期,time-仅时间',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'web_search',
|
||||
description: '搜索互联网获取最新信息,用于查询移民政策变更、新闻动态等实时内容',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: {
|
||||
type: 'string',
|
||||
description: '搜索关键词',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
description: '限定搜索网站,如 immd.gov.hk(香港入境处)',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
enum: ['zh-CN', 'zh-TW', 'en'],
|
||||
description: '搜索语言,默认 zh-CN',
|
||||
},
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_exchange_rate',
|
||||
description: '查询实时货币汇率,用于投资移民金额换算等场景',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
from: {
|
||||
type: 'string',
|
||||
description: '源货币代码,如 CNY(人民币)、USD(美元)',
|
||||
},
|
||||
to: {
|
||||
type: 'string',
|
||||
description: '目标货币代码,如 HKD(港币)',
|
||||
},
|
||||
amount: {
|
||||
type: 'number',
|
||||
description: '要转换的金额(可选)',
|
||||
},
|
||||
},
|
||||
required: ['from', 'to'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'fetch_immigration_news',
|
||||
description: '获取香港移民相关的最新新闻和政策公告',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
category: {
|
||||
type: 'string',
|
||||
enum: ['QMAS', 'GEP', 'IANG', 'TTPS', 'CIES', 'TECHTAS', 'ALL'],
|
||||
description: '移民类别,ALL表示所有类别',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: '返回条数,默认5条',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -196,6 +282,18 @@ export class ImmigrationToolsService {
|
|||
case 'get_user_context':
|
||||
return this.getUserContext(input, context);
|
||||
|
||||
case 'get_current_datetime':
|
||||
return this.getCurrentDatetime(input);
|
||||
|
||||
case 'web_search':
|
||||
return this.webSearch(input);
|
||||
|
||||
case 'get_exchange_rate':
|
||||
return this.getExchangeRate(input);
|
||||
|
||||
case 'fetch_immigration_news':
|
||||
return this.fetchImmigrationNews(input);
|
||||
|
||||
default:
|
||||
return { error: `Unknown tool: ${toolName}` };
|
||||
}
|
||||
|
|
@ -452,4 +550,295 @@ export class ImmigrationToolsService {
|
|||
message: '这是新用户,暂无历史信息',
|
||||
};
|
||||
}
|
||||
|
||||
// ========== 实时工具实现 ==========
|
||||
|
||||
/**
|
||||
* Get current datetime - 获取当前日期时间
|
||||
*/
|
||||
private getCurrentDatetime(input: Record<string, unknown>): unknown {
|
||||
const { timezone = 'Asia/Hong_Kong', format = 'full' } = input as {
|
||||
timezone?: string;
|
||||
format?: 'full' | 'date' | 'time';
|
||||
};
|
||||
|
||||
console.log(`[Tool:get_current_datetime] Timezone: ${timezone}, Format: ${format}`);
|
||||
|
||||
const now = new Date();
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
timeZone: timezone,
|
||||
};
|
||||
|
||||
let result: string;
|
||||
|
||||
switch (format) {
|
||||
case 'date':
|
||||
options.year = 'numeric';
|
||||
options.month = 'long';
|
||||
options.day = 'numeric';
|
||||
options.weekday = 'long';
|
||||
result = now.toLocaleDateString('zh-CN', options);
|
||||
break;
|
||||
case 'time':
|
||||
options.hour = '2-digit';
|
||||
options.minute = '2-digit';
|
||||
options.second = '2-digit';
|
||||
options.hour12 = false;
|
||||
result = now.toLocaleTimeString('zh-CN', options);
|
||||
break;
|
||||
case 'full':
|
||||
default:
|
||||
options.year = 'numeric';
|
||||
options.month = 'long';
|
||||
options.day = 'numeric';
|
||||
options.weekday = 'long';
|
||||
options.hour = '2-digit';
|
||||
options.minute = '2-digit';
|
||||
options.second = '2-digit';
|
||||
options.hour12 = false;
|
||||
result = now.toLocaleString('zh-CN', options);
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
datetime: result,
|
||||
timezone,
|
||||
timestamp: now.toISOString(),
|
||||
message: `当前${timezone}时间: ${result}`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Web search - 搜索互联网获取最新信息
|
||||
* 使用 Google Custom Search API 或其他搜索服务
|
||||
*/
|
||||
private async webSearch(input: Record<string, unknown>): Promise<unknown> {
|
||||
const { query, site, language = 'zh-CN' } = input as {
|
||||
query: string;
|
||||
site?: string;
|
||||
language?: string;
|
||||
};
|
||||
|
||||
console.log(`[Tool:web_search] Query: "${query}", Site: ${site || 'any'}, Language: ${language}`);
|
||||
|
||||
// 构建搜索查询
|
||||
let searchQuery = query;
|
||||
if (site) {
|
||||
searchQuery = `site:${site} ${query}`;
|
||||
}
|
||||
|
||||
// 尝试使用 Google Custom Search API
|
||||
const googleApiKey = this.configService.get<string>('GOOGLE_SEARCH_API_KEY');
|
||||
const googleCseId = this.configService.get<string>('GOOGLE_CSE_ID');
|
||||
|
||||
if (googleApiKey && googleCseId) {
|
||||
try {
|
||||
const url = new URL('https://www.googleapis.com/customsearch/v1');
|
||||
url.searchParams.set('key', googleApiKey);
|
||||
url.searchParams.set('cx', googleCseId);
|
||||
url.searchParams.set('q', searchQuery);
|
||||
url.searchParams.set('lr', `lang_${language.split('-')[0]}`);
|
||||
url.searchParams.set('num', '5');
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
if (response.ok) {
|
||||
const data = (await response.json()) as { items?: Array<{ title: string; link: string; snippet: string }> };
|
||||
const results = (data.items || []).map((item: { title: string; link: string; snippet: string }) => ({
|
||||
title: item.title,
|
||||
url: item.link,
|
||||
snippet: item.snippet,
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
query: searchQuery,
|
||||
results,
|
||||
resultCount: results.length,
|
||||
message: `找到 ${results.length} 条相关结果`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Tool:web_search] Google API error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 降级:返回建议用户自行搜索的信息
|
||||
return {
|
||||
success: false,
|
||||
query: searchQuery,
|
||||
results: [],
|
||||
message: '搜索服务暂时不可用。建议您访问以下官方网站获取最新信息:',
|
||||
suggestedSites: [
|
||||
{ name: '香港入境事务处', url: 'https://www.immd.gov.hk' },
|
||||
{ name: '香港人才服务窗口', url: 'https://www.talentservicewindow.gov.hk' },
|
||||
{ name: '投资推广署', url: 'https://www.investhk.gov.hk' },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get exchange rate - 获取实时汇率
|
||||
* 使用免费的汇率 API
|
||||
*/
|
||||
private async getExchangeRate(input: Record<string, unknown>): Promise<unknown> {
|
||||
const { from, to, amount } = input as {
|
||||
from: string;
|
||||
to: string;
|
||||
amount?: number;
|
||||
};
|
||||
|
||||
console.log(`[Tool:get_exchange_rate] From: ${from}, To: ${to}, Amount: ${amount || 'N/A'}`);
|
||||
|
||||
// 使用 Exchange Rate API (免费层)
|
||||
const apiKey = this.configService.get<string>('EXCHANGE_RATE_API_KEY');
|
||||
const baseUrl = apiKey
|
||||
? `https://v6.exchangerate-api.com/v6/${apiKey}/pair/${from}/${to}`
|
||||
: `https://api.exchangerate-api.com/v4/latest/${from}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(baseUrl);
|
||||
if (response.ok) {
|
||||
const data = (await response.json()) as {
|
||||
conversion_rate?: number;
|
||||
rates?: Record<string, number>;
|
||||
time_last_update_utc?: string;
|
||||
};
|
||||
|
||||
let rate: number | undefined;
|
||||
if (apiKey) {
|
||||
// v6 API response
|
||||
rate = data.conversion_rate;
|
||||
} else {
|
||||
// v4 API response (free, no key needed)
|
||||
rate = data.rates?.[to];
|
||||
}
|
||||
|
||||
if (rate) {
|
||||
const convertedAmount = amount ? amount * rate : null;
|
||||
return {
|
||||
success: true,
|
||||
from,
|
||||
to,
|
||||
rate,
|
||||
amount: amount || null,
|
||||
convertedAmount,
|
||||
lastUpdate: data.time_last_update_utc || new Date().toISOString(),
|
||||
message: amount
|
||||
? `${amount} ${from} = ${convertedAmount?.toFixed(2)} ${to} (汇率: ${rate.toFixed(4)})`
|
||||
: `1 ${from} = ${rate.toFixed(4)} ${to}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Tool:get_exchange_rate] API error:', error);
|
||||
}
|
||||
|
||||
// 降级:使用固定参考汇率
|
||||
const fallbackRates: Record<string, Record<string, number>> = {
|
||||
CNY: { HKD: 1.08, USD: 0.14 },
|
||||
USD: { HKD: 7.78, CNY: 7.24 },
|
||||
HKD: { CNY: 0.93, USD: 0.13 },
|
||||
};
|
||||
|
||||
const fallbackRate = fallbackRates[from]?.[to];
|
||||
if (fallbackRate) {
|
||||
const convertedAmount = amount ? amount * fallbackRate : null;
|
||||
return {
|
||||
success: true,
|
||||
from,
|
||||
to,
|
||||
rate: fallbackRate,
|
||||
amount: amount || null,
|
||||
convertedAmount,
|
||||
isEstimate: true,
|
||||
message: `参考汇率 (可能有偏差): 1 ${from} ≈ ${fallbackRate} ${to}${amount ? `,${amount} ${from} ≈ ${convertedAmount?.toFixed(2)} ${to}` : ''}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: `暂不支持 ${from} 到 ${to} 的汇率查询`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch immigration news - 获取移民相关新闻
|
||||
* 从官方渠道或新闻源获取
|
||||
*/
|
||||
private async fetchImmigrationNews(input: Record<string, unknown>): Promise<unknown> {
|
||||
const { category = 'ALL', limit = 5 } = input as {
|
||||
category?: string;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
console.log(`[Tool:fetch_immigration_news] Category: ${category}, Limit: ${limit}`);
|
||||
|
||||
// 尝试从 knowledge-service 获取最新新闻(如果有此功能)
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${this.configService.get<string>('KNOWLEDGE_SERVICE_URL') || 'http://knowledge-service:3003'}/api/v1/news/immigration?category=${category}&limit=${limit}`,
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const data = (await response.json()) as { success?: boolean; data?: Array<unknown> };
|
||||
if (data.success && data.data) {
|
||||
return {
|
||||
success: true,
|
||||
category,
|
||||
news: data.data,
|
||||
message: `获取到 ${data.data.length} 条${category === 'ALL' ? '' : category + '相关'}移民新闻`,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.log('[Tool:fetch_immigration_news] Knowledge service news not available');
|
||||
}
|
||||
|
||||
// 降级:返回静态参考信息和官方渠道
|
||||
const categoryNews: Record<string, Array<{ title: string; summary: string; date: string; source: string }>> = {
|
||||
QMAS: [
|
||||
{
|
||||
title: '优才计划持续接受申请',
|
||||
summary: '香港优才计划无配额限制,全年接受申请。申请人需符合基本资格要求,并通过计分制评核。',
|
||||
date: '2024-01',
|
||||
source: '香港入境事务处',
|
||||
},
|
||||
],
|
||||
TTPS: [
|
||||
{
|
||||
title: '高才通计划最新动态',
|
||||
summary: '高端人才通行证计划持续运作,A/B/C三类申请通道开放。',
|
||||
date: '2024-01',
|
||||
source: '香港入境事务处',
|
||||
},
|
||||
],
|
||||
CIES: [
|
||||
{
|
||||
title: '新资本投资者入境计划',
|
||||
summary: '投资门槛为3000万港币,需投资于许可资产类别。',
|
||||
date: '2024-03',
|
||||
source: '香港入境事务处',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const newsItems = category === 'ALL'
|
||||
? Object.values(categoryNews).flat()
|
||||
: categoryNews[category] || [];
|
||||
|
||||
return {
|
||||
success: true,
|
||||
category,
|
||||
news: newsItems.slice(0, limit),
|
||||
isStatic: true,
|
||||
message: newsItems.length > 0
|
||||
? `以下是${category === 'ALL' ? '' : category + '类别'}的参考信息。如需最新资讯,请访问香港入境事务处官网。`
|
||||
: '暂无相关新闻,建议访问香港入境事务处官网获取最新信息。',
|
||||
officialSources: [
|
||||
{ name: '香港入境事务处', url: 'https://www.immd.gov.hk/hks/services/visas/admission_talents.html' },
|
||||
{ name: '香港人才服务窗口', url: 'https://www.talentservicewindow.gov.hk' },
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue