feat(ai): 实现 query_patient_vitals Tool — 首个端到端 Agent Tool

This commit is contained in:
iven
2026-05-18 02:58:38 +08:00
parent 2d62605812
commit f42669f934
2 changed files with 101 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
// Agent Tool 实现 — Phase 0 添加 query_patient_vitals
pub mod query_vitals;
pub use query_vitals::QueryPatientVitalsTool;

View File

@@ -0,0 +1,96 @@
use async_trait::async_trait;
use erp_core::health_provider::TimeRange;
use crate::agent::tool::{AgentTool, DisplayHint, ToolContext, ToolResult};
/// 查询患者最近体征数据(血压/血糖/心率等)
pub struct QueryPatientVitalsTool;
#[async_trait]
impl AgentTool for QueryPatientVitalsTool {
fn name(&self) -> &str {
"query_patient_vitals"
}
fn description(&self) -> &str {
"查询患者最近的体征数据(血压、血糖、心率、体重、血氧等)。需要提供天数范围(默认 7 天)。"
}
fn parameters_schema(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"days": {
"type": "integer",
"description": "查询最近多少天的数据,默认 7 天"
}
}
})
}
async fn execute(&self, ctx: &ToolContext, params: serde_json::Value) -> ToolResult {
let patient_id = match ctx.patient_id {
Some(id) => id,
None => {
return ToolResult {
output: "未关联患者档案,无法查询体征数据".to_string(),
display_hint: None,
};
}
};
let days = params["days"].as_i64().unwrap_or(7);
let now = chrono::Utc::now();
let start = now - chrono::Duration::days(days);
let range = TimeRange { start, end: now };
let metrics = vec![
"systolic_bp_morning".into(),
"diastolic_bp_morning".into(),
"heart_rate".into(),
"blood_sugar".into(),
];
match ctx
.health_provider
.get_vital_signs(ctx.tenant_id, patient_id, &metrics, &range)
.await
{
Ok(vitals) => {
if vitals.is_empty() {
return ToolResult {
output: "该时间段内无体征数据".to_string(),
display_hint: None,
};
}
let mut output = String::from("最近体征数据:\n");
for v in &vitals {
output.push_str(&format!("- {}:", v.metric));
let values_str: Vec<String> = v
.values
.iter()
.take(10)
.map(|(date, val)| format!("{}={}", date, val))
.collect();
output.push_str(&format!(" ({})\n", values_str.join(", ")));
}
let display_hint = vitals.first().map(|v| DisplayHint::VitalCard {
indicator_type: v.metric.clone(),
values: v.values.iter().take(10).cloned().collect(),
unit: v.unit.clone(),
});
ToolResult {
output,
display_hint,
}
}
Err(e) => ToolResult {
output: format!("查询体征数据失败: {}", e),
display_hint: None,
},
}
}
}