fix(tools): 修复 coordinator-tools 与 immigration-tools 之间的 input_schema 不一致

## 问题

全链路检查发现 coordinator-tools.ts(Claude 实际使用的工具定义)与
immigration-tools.service.ts(实际执行器)之间有 4 处 input_schema 不一致,
会导致 Claude 发送的参数无法被正确解析。

## 修复

### check_off_topic
- coordinator 发 `query`,handler 读 `question`
- Fix: handler 同时支持 `query` 和 `question` 两个字段名

### collect_assessment_info
- coordinator 发 `{ userId, field, value }`(单字段模式)
- handler 读 `{ category, age, education, ... }`(批量模式)
- Fix: handler 同时支持两种输入格式

### generate_payment
- coordinator 旧 schema: `{ userId, serviceType, amount, description }`
- handler 需要: `{ serviceType, category, paymentMethod }`
- Fix: 更新 coordinator schema 为 `{ serviceType, category, paymentMethod }`
  - serviceType enum 改为 ASSESSMENT/CONSULTATION/DOCUMENT_REVIEW(匹配 payment-service)
  - 添加 category enum 和 paymentMethod enum
  - 移除 userId(从 context 获取)和 amount(由 payment-service 定价)

### save_user_memory
- coordinator 旧 schema 多余 `userId`(handler 用 context.userId)
- coordinator 发 `importance` 但 handler 不读
- handler 支持 `category` 但 coordinator 未定义
- Fix: coordinator schema 移除 userId,移除 importance,添加 category

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-07 01:31:38 -08:00
parent a3f2be078b
commit 9e9865acb0
2 changed files with 41 additions and 28 deletions

View File

@ -298,23 +298,24 @@ export const DIRECT_TOOLS: ToolDefinition[] = [
name: 'save_user_memory',
description:
'直接保存用户信息到长期记忆。' +
'用于保存关键信息,无需启动完整的记忆管理 Agent。',
'用于保存关键信息,无需启动完整的记忆管理 Agent。' +
'userId 不需要传,系统自动获取。',
input_schema: {
type: 'object',
properties: {
userId: { type: 'string', description: '用户 ID' },
memoryType: {
type: 'string',
enum: ['FACT', 'PREFERENCE', 'INTENT'],
description: '记忆类型',
description: '记忆类型FACT-用户事实PREFERENCE-偏好INTENT-意图',
},
content: { type: 'string', description: '记忆内容' },
importance: {
type: 'number',
description: '重要程度 1-10',
content: { type: 'string', description: '要记住的内容' },
category: {
type: 'string',
enum: ['QMAS', 'GEP', 'IANG', 'TTPS', 'CIES', 'TECHTAS'],
description: '相关移民类别(可选)',
},
},
required: ['userId', 'memoryType', 'content'],
required: ['memoryType', 'content'],
},
isConcurrencySafe: false,
},
@ -346,20 +347,28 @@ export const DIRECT_TOOLS: ToolDefinition[] = [
{
name: 'generate_payment',
description:
'生成支付链接。仅在用户明确表示要付费或预约服务时使用。',
'生成支付二维码。仅在用户明确表示要付费或预约服务时使用。' +
'需要确认用户选择的服务类别和支付方式后调用。',
input_schema: {
type: 'object',
properties: {
userId: { type: 'string', description: '用户 ID' },
serviceType: {
type: 'string',
enum: ['assessment', 'consultation', 'full_service'],
enum: ['ASSESSMENT', 'CONSULTATION', 'DOCUMENT_REVIEW'],
description: '服务类型',
},
amount: { type: 'number', description: '金额HKD' },
description: { type: 'string', description: '服务描述' },
category: {
type: 'string',
enum: ['QMAS', 'GEP', 'IANG', 'TTPS', 'CIES', 'TECHTAS'],
description: '移民类别',
},
paymentMethod: {
type: 'string',
enum: ['ALIPAY', 'WECHAT', 'CREDIT_CARD'],
description: '支付方式(支付宝、微信、信用卡)',
},
},
required: ['userId', 'serviceType'],
required: ['serviceType', 'category', 'paymentMethod'],
},
isConcurrencySafe: false,
},

View File

@ -427,7 +427,8 @@ export class ImmigrationToolsService {
* Check if question is off-topic - knowledge-service API
*/
private async checkOffTopic(input: Record<string, unknown>): Promise<unknown> {
const { question } = input as { question: string };
// coordinator-tools.ts sends `query`, legacy getTools() sent `question`
const question = (input.query as string) || (input.question as string) || '';
console.log(`[Tool:check_off_topic] Checking: "${question}"`);
@ -456,19 +457,22 @@ export class ImmigrationToolsService {
input: Record<string, unknown>,
context: ConversationContext,
): Promise<unknown> {
const info = input as {
category: string;
age?: number;
education?: string;
university?: string;
yearsOfExperience?: number;
currentJobTitle?: string;
industry?: string;
annualIncome?: number;
hasHKEmployer?: boolean;
};
// coordinator-tools.ts sends { userId, field, value } (single-field mode)
// Legacy getTools() sent { category, age, education, ... } (batch mode)
// Support both formats
let info: Record<string, unknown>;
console.log(`[Tool:collect_assessment_info] User ${context.userId} - Category: ${info.category}`);
if (input.field && input.value) {
// Single-field mode from coordinator-tools.ts
info = { [input.field as string]: input.value };
} else {
// Batch mode from legacy getTools()
const { userId: _userId, ...rest } = input;
info = rest;
}
const category = (info.category as string) || '';
console.log(`[Tool:collect_assessment_info] User ${context.userId} - Fields: ${Object.keys(info).join(', ')}`);
// 保存收集到的信息到用户记忆
const memoryContent = Object.entries(info)
@ -482,7 +486,7 @@ export class ImmigrationToolsService {
memoryType: 'FACT',
content: `用户评估信息 - ${memoryContent}`,
sourceConversationId: context.conversationId,
relatedCategory: info.category,
relatedCategory: category,
importance: 80,
});
}