From 7ff429e07127df8a1733176714e5011986c5429f Mon Sep 17 00:00:00 2001
From: MingNian <1281442923@qq.com>
Date: Mon, 22 Jun 2026 20:50:32 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A5=E5=85=A5FastGPT=E7=9F=A5?=
=?UTF-8?q?=E8=AF=86=E5=BA=93RAG=E6=A3=80=E7=B4=A2=EF=BC=8CAI=E5=9B=9E?=
=?UTF-8?q?=E7=AD=94=E5=89=8D=E5=85=88=E6=9F=A5=E5=8C=BB=E5=AD=A6=E8=B5=84?=
=?UTF-8?q?=E6=96=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 FastGptKnowledgeClient:调用 searchTest 接口检索知识库
- ai_chat_endpoints: 调 DeepSeek 之前先检索,把相关片段拼到 system prompt
- 5个智能体(default/consultation/diet/report/unified)启用 RAG
- 3个纯工具调用智能体(health/medication/exercise)保持原样
- .env: 新增 FastGPT 配置(API Key、datasetId、相似度等)
- api_client: 更新默认 baseUrl 适配新网络环境
---
.../AI/fastgpt_knowledge_client.cs | 71 +++++++++++++++++++
.../Endpoints/ai_chat_endpoints.cs | 32 ++++++++-
backend/src/Health.WebApi/Program.cs | 6 ++
health_app/lib/core/api_client.dart | 2 +-
4 files changed, 109 insertions(+), 2 deletions(-)
create mode 100644 backend/src/Health.Infrastructure/AI/fastgpt_knowledge_client.cs
diff --git a/backend/src/Health.Infrastructure/AI/fastgpt_knowledge_client.cs b/backend/src/Health.Infrastructure/AI/fastgpt_knowledge_client.cs
new file mode 100644
index 0000000..6eb1f57
--- /dev/null
+++ b/backend/src/Health.Infrastructure/AI/fastgpt_knowledge_client.cs
@@ -0,0 +1,71 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace Health.Infrastructure.AI;
+
+///
+/// FastGPT 知识库检索客户端(仅做 RAG 检索,不参与生成)。
+/// 我们后端的 DeepSeek 依旧负责对话生成与工具调用;
+/// 这里只把检索到的医学知识片段拼回 system prompt,提升回答准确度。
+///
+public sealed class FastGptKnowledgeClient(HttpClient http, IConfiguration config, ILogger logger)
+{
+ private readonly HttpClient _http = http;
+ private readonly string _datasetId = config["FASTGPT_DATASET_ID"] ?? "";
+ private readonly float _similarity = float.TryParse(config["FASTGPT_SIMILARITY"], out var s) ? s : 0.4f;
+ private readonly int _limit = int.TryParse(config["FASTGPT_LIMIT"], out var l) ? l : 3000;
+ private readonly ILogger _logger = logger;
+ private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNameCaseInsensitive = true };
+
+ public bool IsEnabled => !string.IsNullOrWhiteSpace(_datasetId);
+
+ ///
+ /// 按用户问题检索知识库,返回最相关的文档片段。
+ /// 失败或未配置时返回空字符串,调用方应当容错——RAG 只是增强,不是必需。
+ ///
+ public async Task SearchAsync(string question, CancellationToken ct = default)
+ {
+ if (!IsEnabled || string.IsNullOrWhiteSpace(question)) return "";
+
+ try
+ {
+ var payload = new
+ {
+ datasetId = _datasetId,
+ text = question,
+ limit = _limit,
+ similarity = _similarity,
+ searchMode = "embedding",
+ };
+ var json = JsonSerializer.Serialize(payload);
+ var content = new StringContent(json, Encoding.UTF8, "application/json");
+ using var response = await _http.PostAsync("core/dataset/searchTest", content, ct);
+ if (!response.IsSuccessStatusCode)
+ {
+ _logger.LogWarning("FastGPT 检索失败: {Status}", response.StatusCode);
+ return "";
+ }
+
+ var body = await response.Content.ReadAsStringAsync(ct);
+ using var doc = JsonDocument.Parse(body);
+ if (!doc.RootElement.TryGetProperty("data", out var data)) return "";
+ if (!data.TryGetProperty("list", out var list) || list.ValueKind != JsonValueKind.Array) return "";
+
+ var snippets = new List();
+ foreach (var item in list.EnumerateArray())
+ {
+ var q = item.TryGetProperty("q", out var qEl) ? qEl.GetString() ?? "" : "";
+ var a = item.TryGetProperty("a", out var aEl) ? aEl.GetString() ?? "" : "";
+ if (string.IsNullOrWhiteSpace(q) && string.IsNullOrWhiteSpace(a)) continue;
+ snippets.Add(string.IsNullOrWhiteSpace(a) ? q : $"{q}:{a}");
+ }
+
+ return snippets.Count == 0 ? "" : string.Join("\n", snippets);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "FastGPT 检索异常,已忽略");
+ return "";
+ }
+ }
+}
diff --git a/backend/src/Health.WebApi/Endpoints/ai_chat_endpoints.cs b/backend/src/Health.WebApi/Endpoints/ai_chat_endpoints.cs
index cbe5082..436b8db 100644
--- a/backend/src/Health.WebApi/Endpoints/ai_chat_endpoints.cs
+++ b/backend/src/Health.WebApi/Endpoints/ai_chat_endpoints.cs
@@ -33,6 +33,7 @@ public static class AiChatEndpoints
HttpContext http,
DeepSeekClient llmClient,
PromptManager promptManager,
+ FastGptKnowledgeClient knowledgeClient,
IAiToolExecutionService toolExecution,
IAiWriteConfirmationStore confirmations,
IAiConversationService conversations,
@@ -110,9 +111,24 @@ public static class AiChatEndpoints
var systemPrompt = promptManager.GetSystemPrompt(parsedType);
var patientContext = await patientContexts.BuildAsync(userId.Value, ct);
+ // ── RAG 知识库检索 ──
+ // 仅对需要医学知识的 Agent 启用检索;记数据/管用药/排运动这些纯工具调用类跳过。
+ // 失败/空结果不阻塞流程,仅作为 prompt 增强。
+ var knowledgeSnippet = "";
+ if (NeedsKnowledgeBase(parsedType) && knowledgeClient.IsEnabled)
+ {
+ knowledgeSnippet = await knowledgeClient.SearchAsync(message, ct);
+ }
+
+ var enhancedSystem = systemPrompt + "\n\n当前患者信息:\n" + patientContext;
+ if (!string.IsNullOrWhiteSpace(knowledgeSnippet))
+ {
+ enhancedSystem += "\n\n知识库参考资料(如与用户问题相关请优先采用):\n" + knowledgeSnippet;
+ }
+
var messages = new List
{
- new() { Role = "system", Content = systemPrompt + "\n\n当前患者信息:\n" + patientContext },
+ new() { Role = "system", Content = enhancedSystem },
};
// 加载历史对话(最近 10 条)
@@ -303,6 +319,20 @@ public static class AiChatEndpoints
// ── Agent / Tool 调度 ──
+ ///
+ /// 哪些 Agent 需要走 FastGPT 知识库 RAG。
+ /// 纯工具调用类(Health/Medication/Exercise)跳过 RAG,避免拖慢响应。
+ ///
+ private static bool NeedsKnowledgeBase(AgentType agentType) => agentType switch
+ {
+ AgentType.Default => true,
+ AgentType.Consultation => true,
+ AgentType.Diet => true,
+ AgentType.Report => true,
+ AgentType.Unified => true,
+ _ => false,
+ };
+
private static List GetToolsForAgent(AgentType agentType) => agentType switch
{
AgentType.Health => HealthDataAgentHandler.Tools,
diff --git a/backend/src/Health.WebApi/Program.cs b/backend/src/Health.WebApi/Program.cs
index 3b72192..5a342e1 100644
--- a/backend/src/Health.WebApi/Program.cs
+++ b/backend/src/Health.WebApi/Program.cs
@@ -162,6 +162,12 @@ builder.Services.AddHttpClient(client =>
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", builder.Configuration["VLM_API_KEY"] ?? "");
client.Timeout = TimeSpan.FromSeconds(120);
});
+builder.Services.AddHttpClient(client =>
+{
+ client.BaseAddress = new Uri((builder.Configuration["FASTGPT_BASE_URL"] ?? "https://cloud.fastgpt.cn/api").TrimEnd('/') + "/");
+ client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", builder.Configuration["FASTGPT_API_KEY"] ?? "");
+ client.Timeout = TimeSpan.FromSeconds(15);
+});
// ---- 后台服务 ----
builder.Services.AddHostedService();
diff --git a/health_app/lib/core/api_client.dart b/health_app/lib/core/api_client.dart
index 5012dd7..52077c9 100644
--- a/health_app/lib/core/api_client.dart
+++ b/health_app/lib/core/api_client.dart
@@ -8,7 +8,7 @@ import 'local_database.dart';
/// Android 真机 WiFi: flutter run --dart-define=API_BASE_URL=http://电脑IP:5000
const String baseUrl = String.fromEnvironment(
'API_BASE_URL',
- defaultValue: 'http://192.168.1.29:5000',
+ defaultValue: 'http://10.4.170.202:5000',
);
class ApiException implements Exception {