dvc890 commited on
Commit
df4c6dd
·
verified ·
1 Parent(s): ca68589

Upload 64 files

Browse files
Files changed (1) hide show
  1. ai-tools.js +55 -16
ai-tools.js CHANGED
@@ -9,22 +9,22 @@ const mongoTools = [
9
  functionDeclarations: [
10
  {
11
  name: "query_database",
12
- description: "查询学校数据库中的信息户询问具体的学生成绩、考勤或班级数据时,必须使用此工具。支持的集合(collections): 'Student'(学生), 'Score'(成绩), 'Attendance'(考勤), 'Class'(班级)。",
13
  parameters: {
14
  type: "OBJECT",
15
  properties: {
16
  collection: {
17
  type: "STRING",
18
- description: "要查询的集合名称,例如 'Student', 'Score', 'Attendance'。",
19
  enum: ["Student", "Score", "Attendance", "Class"]
20
  },
21
  filter: {
22
  type: "OBJECT",
23
- description: "Mongoose/MongoDB 查询过滤条件JSON 对象。例: {name: '张三'} {score: {$lt: 60}}不要包含 schoolId,系统会自动注入。",
24
  },
25
  limit: {
26
  type: "NUMBER",
27
- description: "限制返回条数,默认 5,最大 20。"
28
  }
29
  },
30
  required: ["collection", "filter"]
@@ -58,9 +58,9 @@ function injectSecurityFilter(filter, user, role, schoolId) {
58
  return safeFilter;
59
  }
60
 
 
61
  if (role === 'TEACHER') {
62
- // 简单权限控:老师只能查自己相关或全校公开数据
63
- // 实际逻辑可根据需求扩展
64
  }
65
 
66
  if (role === 'STUDENT') {
@@ -72,12 +72,41 @@ function injectSecurityFilter(filter, user, role, schoolId) {
72
  return safeFilter;
73
  }
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  /**
76
  * 3. 工具执行器 (Executor)
77
  */
78
  async function executeMongoTool(functionCall, user, role, schoolId) {
79
- // 兼容 OpenAI 格式 (arguments 是字符串) 和 Gemini 格式 (args 是对象)
80
  let args = functionCall.args;
 
81
  if (typeof functionCall.arguments === 'string') {
82
  try {
83
  args = JSON.parse(functionCall.arguments);
@@ -89,17 +118,20 @@ async function executeMongoTool(functionCall, user, role, schoolId) {
89
 
90
  const { collection, filter = {}, limit = 5 } = args || {};
91
 
 
 
 
92
  // 🛡️ 安全注入
93
- const safeFilter = injectSecurityFilter(filter, user, role, schoolId);
94
  const safeLimit = Math.min(Math.max(limit, 1), 20);
95
 
96
- // --- 🔍 MCP LOGGING START ---
97
  console.log(`\n================= [MCP TOOL CALL] =================`);
98
  console.log(`🛠️ Tool: query_database`);
99
  console.log(`📂 Collection: ${collection}`);
100
- console.log(`📥 AI Params: ${JSON.stringify(filter)}`);
101
- console.log(`🔒 Safe Query: ${JSON.stringify(safeFilter)}`);
102
- console.log(`👤 User Role: ${role} (${user.username})`);
103
  console.log(`---------------------------------------------------`);
104
 
105
  try {
@@ -109,10 +141,17 @@ async function executeMongoTool(functionCall, user, role, schoolId) {
109
  switch (collection) {
110
  case "Student":
111
  fields = "name studentNo className gender flowerBalance seatNo -_id";
 
 
 
 
112
  result = await Student.find(safeFilter).select(fields).limit(safeLimit).lean();
113
  break;
114
  case "Score":
115
  fields = "studentName courseName score type examName -_id";
 
 
 
116
  result = await Score.find(safeFilter).select(fields).sort({ _id: -1 }).limit(safeLimit).lean();
117
  break;
118
  case "Attendance":
@@ -125,24 +164,24 @@ async function executeMongoTool(functionCall, user, role, schoolId) {
125
  break;
126
  default:
127
  console.log(`❌ [MCP ERROR] Unknown collection: ${collection}`);
128
- console.log(`===================================================\n`);
129
  return { error: "Unknown collection" };
130
  }
131
 
132
  console.log(`✅ [MCP SUCCESS] Found ${result.length} records.`);
133
  if (result.length > 0) {
134
- console.log(`📄 Sample Data: ${JSON.stringify(result[0])}`);
 
 
135
  }
136
  console.log(`===================================================\n`);
137
 
138
  if (result.length === 0) {
139
- return { info: "未找到符合条件的数据。" };
140
  }
141
  return result;
142
 
143
  } catch (error) {
144
  console.error("❌ [MCP EXCEPTION]", error.message);
145
- console.log(`===================================================\n`);
146
  return { error: "Database query failed", details: error.message };
147
  }
148
  }
 
9
  functionDeclarations: [
10
  {
11
  name: "query_database",
12
+ description: "查询学校数据库。⚠️字段名必须准确:班级字段名是 'className' (禁止 'class'),学生姓名在Student表是 'name',在成绩表是 'studentName'。支持的集合: 'Student', 'Score', 'Attendance', 'Class'。",
13
  parameters: {
14
  type: "OBJECT",
15
  properties: {
16
  collection: {
17
  type: "STRING",
18
+ description: "集合名称",
19
  enum: ["Student", "Score", "Attendance", "Class"]
20
  },
21
  filter: {
22
  type: "OBJECT",
23
+ description: "Mongoose查询条件JSON。例: {className:'一年级(1)班', name:'张三'}。注意:查询班级时必须使用 'className' 字段。",
24
  },
25
  limit: {
26
  type: "NUMBER",
27
+ description: "默认 5"
28
  }
29
  },
30
  required: ["collection", "filter"]
 
58
  return safeFilter;
59
  }
60
 
61
+ // 老师只能看自己相关的班级逻辑可以在这里加强
62
  if (role === 'TEACHER') {
63
+ // 暂时不做强过滤依赖业务层逻辑
 
64
  }
65
 
66
  if (role === 'STUDENT') {
 
72
  return safeFilter;
73
  }
74
 
75
+ /**
76
+ * 辅助:递归修正查询字段名 (Deep Normalization)
77
+ * AI 经常把 className 写成 class,把 studentName 写成 name,这里做统一修正
78
+ */
79
+ function normalizeQueryFields(query, collection) {
80
+ if (!query || typeof query !== 'object') return query;
81
+
82
+ if (Array.isArray(query)) {
83
+ return query.map(item => normalizeQueryFields(item, collection));
84
+ }
85
+
86
+ const newQuery = {};
87
+ for (const key in query) {
88
+ let newKey = key;
89
+
90
+ // 1. 修正班级字段: class -> className
91
+ if (key === 'class') newKey = 'className';
92
+
93
+ // 2. 修正名字字段: Score表里叫 studentName, Student表里叫 name
94
+ if (collection === 'Score' || collection === 'Attendance') {
95
+ if (key === 'name') newKey = 'studentName';
96
+ }
97
+
98
+ // 递归处理值 (例如 $or 数组内部的对象)
99
+ newQuery[newKey] = normalizeQueryFields(query[key], collection);
100
+ }
101
+ return newQuery;
102
+ }
103
+
104
  /**
105
  * 3. 工具执行器 (Executor)
106
  */
107
  async function executeMongoTool(functionCall, user, role, schoolId) {
 
108
  let args = functionCall.args;
109
+ // 兼容 OpenAI 格式
110
  if (typeof functionCall.arguments === 'string') {
111
  try {
112
  args = JSON.parse(functionCall.arguments);
 
118
 
119
  const { collection, filter = {}, limit = 5 } = args || {};
120
 
121
+ // 🛠️ 关键修复:在注入安全字段前,先修正 AI 的字段命名错误
122
+ const normalizedFilter = normalizeQueryFields(filter, collection);
123
+
124
  // 🛡️ 安全注入
125
+ const safeFilter = injectSecurityFilter(normalizedFilter, user, role, schoolId);
126
  const safeLimit = Math.min(Math.max(limit, 1), 20);
127
 
128
+ // --- 🔍 MCP LOGGING ---
129
  console.log(`\n================= [MCP TOOL CALL] =================`);
130
  console.log(`🛠️ Tool: query_database`);
131
  console.log(`📂 Collection: ${collection}`);
132
+ console.log(`📥 AI Params: ${JSON.stringify(filter)}`); // 原始
133
+ console.log(`🔧 Normalized: ${JSON.stringify(normalizedFilter)}`); // 修正后
134
+ console.log(`🔒 Safe Query: ${JSON.stringify(safeFilter)}`); // 最终
135
  console.log(`---------------------------------------------------`);
136
 
137
  try {
 
141
  switch (collection) {
142
  case "Student":
143
  fields = "name studentNo className gender flowerBalance seatNo -_id";
144
+ // 模糊搜索支持
145
+ if (safeFilter.name && !safeFilter.name.$regex) {
146
+ safeFilter.name = { $regex: safeFilter.name, $options: 'i' };
147
+ }
148
  result = await Student.find(safeFilter).select(fields).limit(safeLimit).lean();
149
  break;
150
  case "Score":
151
  fields = "studentName courseName score type examName -_id";
152
+ if (safeFilter.studentName && !safeFilter.studentName.$regex) {
153
+ safeFilter.studentName = { $regex: safeFilter.studentName, $options: 'i' };
154
+ }
155
  result = await Score.find(safeFilter).select(fields).sort({ _id: -1 }).limit(safeLimit).lean();
156
  break;
157
  case "Attendance":
 
164
  break;
165
  default:
166
  console.log(`❌ [MCP ERROR] Unknown collection: ${collection}`);
 
167
  return { error: "Unknown collection" };
168
  }
169
 
170
  console.log(`✅ [MCP SUCCESS] Found ${result.length} records.`);
171
  if (result.length > 0) {
172
+ console.log(`📄 Sample: ${JSON.stringify(result[0])}`);
173
+ } else {
174
+ console.log(`⚠️ No records found. Hint: Check if schoolId matches.`);
175
  }
176
  console.log(`===================================================\n`);
177
 
178
  if (result.length === 0) {
179
+ return { info: "未找到符合条件的数据。请确认查询条件是否准确(如姓名是否正确)。" };
180
  }
181
  return result;
182
 
183
  } catch (error) {
184
  console.error("❌ [MCP EXCEPTION]", error.message);
 
185
  return { error: "Database query failed", details: error.message };
186
  }
187
  }