feat(ai): 实现 query_patient_vitals Tool — 首个端到端 Agent Tool
This commit is contained in:
5
crates/erp-ai/src/agent/tools/mod.rs
Normal file
5
crates/erp-ai/src/agent/tools/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
// Agent Tool 实现 — Phase 0 添加 query_patient_vitals
|
||||
|
||||
pub mod query_vitals;
|
||||
|
||||
pub use query_vitals::QueryPatientVitalsTool;
|
||||
96
crates/erp-ai/src/agent/tools/query_vitals.rs
Normal file
96
crates/erp-ai/src/agent/tools/query_vitals.rs
Normal 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user