feat: AI Agent 处理器扩展 + 服药打卡确认链路 + AI 气泡配色微调 + 数据刷新 providers 抽离 + 多端页面细节调整
This commit is contained in:
@@ -37,6 +37,7 @@ public static class AiChatEndpoints
|
||||
FastGptKnowledgeClient knowledgeClient,
|
||||
IAiToolExecutionService toolExecution,
|
||||
IAiWriteConfirmationStore confirmations,
|
||||
IMedicationService medications,
|
||||
IAiConversationService conversations,
|
||||
IAttachmentContextBuilder attachments,
|
||||
IPatientContextService patientContexts,
|
||||
@@ -139,7 +140,11 @@ public static class AiChatEndpoints
|
||||
knowledgeSnippet = await knowledgeClient.SearchAsync(message, ct);
|
||||
}
|
||||
|
||||
var enhancedSystem = systemPrompt + "\n\n当前患者信息:\n" + patientContext;
|
||||
var beijingNow = DateTime.UtcNow.AddHours(8);
|
||||
var enhancedSystem = systemPrompt
|
||||
+ $"\n\n系统当前北京时间:{beijingNow:yyyy-MM-dd HH:mm:ss}(UTC+8)。所有‘今天/昨天/当前/下一次’均以此时间为准。"
|
||||
+ "\n\n当前患者背景信息(仅作医学背景;个人计划和完成状态必须调用对应工具获取最新数据):\n"
|
||||
+ patientContext;
|
||||
if (!string.IsNullOrWhiteSpace(knowledgeSnippet))
|
||||
{
|
||||
enhancedSystem += "\n\n知识库参考资料(如与用户问题相关请优先采用):\n" + knowledgeSnippet;
|
||||
@@ -243,7 +248,7 @@ public static class AiChatEndpoints
|
||||
try
|
||||
{
|
||||
toolResult = IsWriteToolCall(tc.Function.Name, tc.Function.Arguments)
|
||||
? await PreparePendingWriteAsync(confirmations, userId.Value, tc.Function.Name, tc.Function.Arguments, ct)
|
||||
? await PreparePendingWriteAsync(confirmations, medications, userId.Value, tc.Function.Name, tc.Function.Arguments, ct)
|
||||
: await toolExecution.ExecuteAsync(tc.Function.Name, tc.Function.Arguments, userId.Value, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -559,6 +564,10 @@ public static class AiChatEndpoints
|
||||
MedicationAgentHandler.ManageMedicationTool,
|
||||
ExerciseAgentHandler.ManageExerciseTool,
|
||||
CommonAgentHandler.CheckArchiveTool,
|
||||
PatientReadAgentHandler.QueryDietRecordsTool,
|
||||
PatientReadAgentHandler.QueryFollowUpsTool,
|
||||
PatientReadAgentHandler.QueryReportsTool,
|
||||
PatientReadAgentHandler.QueryNotificationsTool,
|
||||
],
|
||||
_ => CommonAgentHandler.Tools,
|
||||
};
|
||||
@@ -566,7 +575,6 @@ public static class AiChatEndpoints
|
||||
private static bool IsWriteToolCall(string toolName, string arguments)
|
||||
{
|
||||
if (toolName == "record_health_data") return true;
|
||||
if (toolName == "manage_archive") return GetToolAction(arguments) != "query";
|
||||
if (toolName == "manage_medication") return GetToolAction(arguments) is "create" or "confirm";
|
||||
if (toolName == "manage_exercise") return GetToolAction(arguments) is "create" or "checkin";
|
||||
return false;
|
||||
@@ -589,14 +597,39 @@ public static class AiChatEndpoints
|
||||
|
||||
private static async Task<object> PreparePendingWriteAsync(
|
||||
IAiWriteConfirmationStore confirmations,
|
||||
IMedicationService medications,
|
||||
Guid userId,
|
||||
string toolName,
|
||||
string arguments,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var command = await confirmations.CreateAsync(userId, toolName, arguments, TimeSpan.FromMinutes(10), ct);
|
||||
using var json = JsonDocument.Parse(arguments);
|
||||
var args = json.RootElement;
|
||||
AiMedicationPlanDto? medicationPlan = null;
|
||||
if (toolName == "manage_medication")
|
||||
{
|
||||
var validationError = MedicationAgentHandler.ValidateWriteArguments(args);
|
||||
if (validationError != null)
|
||||
return new { success = false, message = validationError };
|
||||
|
||||
if (string.Equals(GetString(args, "action"), "confirm", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var medicationId = args.GetProperty("medication_id").GetGuid();
|
||||
var scheduledTime = GetString(args, "scheduled_time")!;
|
||||
var overview = await medications.GetAiOverviewAsync(
|
||||
userId,
|
||||
DateOnly.FromDateTime(DateTime.UtcNow.AddHours(8)),
|
||||
ct);
|
||||
medicationPlan = overview.FirstOrDefault(plan =>
|
||||
plan.Id == medicationId &&
|
||||
plan.ScheduledOnDate &&
|
||||
plan.Doses.Any(dose => dose.ScheduledTime == scheduledTime && dose.Status is "scheduled" or "overdue"));
|
||||
if (medicationPlan == null)
|
||||
return new { success = false, message = "没有找到这顿尚未确认的今日服药安排,请重新查询后再确认" };
|
||||
}
|
||||
}
|
||||
|
||||
var command = await confirmations.CreateAsync(userId, toolName, arguments, TimeSpan.FromMinutes(10), ct);
|
||||
|
||||
var preview = new Dictionary<string, object?>
|
||||
{
|
||||
@@ -613,12 +646,15 @@ public static class AiChatEndpoints
|
||||
break;
|
||||
case "manage_medication":
|
||||
preview["type"] = "medication";
|
||||
preview["name"] = GetString(args, "name") ?? "服药确认";
|
||||
preview["dosage"] = GetString(args, "dosage") ?? "";
|
||||
preview["frequency"] = GetString(args, "frequency") ?? "Daily";
|
||||
preview["time"] = GetStringArray(args, "time_of_day");
|
||||
var medicationAction = GetString(args, "action");
|
||||
preview["name"] = medicationPlan?.Name ?? GetString(args, "name") ?? "用药计划";
|
||||
preview["operation"] = medicationAction ?? "create";
|
||||
preview["dosage"] = medicationPlan?.Dosage ?? GetString(args, "dosage") ?? "";
|
||||
preview["frequency"] = medicationPlan?.Frequency ?? GetString(args, "frequency") ?? "";
|
||||
preview["time"] = GetString(args, "scheduled_time") ?? GetStringArray(args, "time_of_day");
|
||||
preview["duration_days"] = GetInt(args, "duration_days") ?? 0;
|
||||
preview["start_date"] = GetString(args, "start_date") ?? DateOnly.FromDateTime(DateTime.UtcNow.AddHours(8)).ToString("yyyy-MM-dd");
|
||||
preview["long_term"] = args.TryGetProperty("long_term", out var longTerm) && longTerm.ValueKind == JsonValueKind.True;
|
||||
preview["start_date"] = medicationPlan?.StartDate?.ToString("yyyy-MM-dd") ?? GetString(args, "start_date") ?? "";
|
||||
break;
|
||||
case "manage_exercise":
|
||||
preview["type"] = "exercise";
|
||||
@@ -628,11 +664,6 @@ public static class AiChatEndpoints
|
||||
preview["start_date"] = GetString(args, "start_date") ?? DateOnly.FromDateTime(DateTime.UtcNow.AddHours(8)).ToString("yyyy-MM-dd");
|
||||
preview["reminder_time"] = GetString(args, "reminder_time") ?? "19:00";
|
||||
break;
|
||||
case "manage_archive":
|
||||
preview["type"] = "archive";
|
||||
preview["value"] = "健康档案更新";
|
||||
preview["unit"] = "";
|
||||
break;
|
||||
}
|
||||
|
||||
return preview;
|
||||
@@ -805,6 +836,8 @@ public static class AiChatEndpoints
|
||||
{
|
||||
if (resultDict.TryGetValue("name", out var name))
|
||||
metadata["name"] = name?.ToString() ?? "";
|
||||
if (resultDict.TryGetValue("operation", out var operation))
|
||||
metadata["operation"] = operation?.ToString() ?? "create";
|
||||
if (resultDict.TryGetValue("dosage", out var dosage))
|
||||
metadata["dosage"] = dosage?.ToString() ?? "";
|
||||
if (resultDict.TryGetValue("time", out var time))
|
||||
@@ -834,15 +867,6 @@ public static class AiChatEndpoints
|
||||
metadata["recordTime"] = DateTime.UtcNow.AddHours(8).ToString("MM月dd日 HH:mm");
|
||||
}
|
||||
break;
|
||||
case "manage_archive":
|
||||
if (!isPendingConfirmation) break;
|
||||
messageType = "data_confirm";
|
||||
metadata["type"] = "archive";
|
||||
metadata["value"] = "健康档案更新";
|
||||
metadata["unit"] = "";
|
||||
metadata["success"] = true;
|
||||
metadata["recordTime"] = DateTime.UtcNow.AddHours(8).ToString("MM月dd日 HH:mm");
|
||||
break;
|
||||
case "analyze_report":
|
||||
messageType = "report_analysis";
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user