feat(ai): Day 5 — ChatResponse display_hints + Web RichMessage 渲染
- ChatResponse 增加 display_hints 字段,Orchestrator 收集 Tool 产生的 DisplayHint - DisplayHint 实现 utoipa::ToSchema - Web ChatResponse 类型同步,DisplayHint 8 种联合类型 - RichMessage 组件:InsightCard/RiskAlert/LabReportCard/TrendChart/PatientProfile/VitalCard - AiSidebar 消息中渲染 display_hints 富消息 - 小程序 AiChatResponse 类型同步
This commit is contained in:
@@ -49,6 +49,7 @@ pub struct AgentRunResult {
|
||||
pub total_output_tokens: u32,
|
||||
pub iterations: usize,
|
||||
pub tool_calls: Vec<ToolCallLog>,
|
||||
pub display_hints: Vec<super::tool::DisplayHint>,
|
||||
}
|
||||
|
||||
impl AgentOrchestrator {
|
||||
@@ -76,6 +77,7 @@ impl AgentOrchestrator {
|
||||
let mut total_input_tokens = 0u32;
|
||||
let mut total_output_tokens = 0u32;
|
||||
let mut tool_call_logs: Vec<ToolCallLog> = Vec::new();
|
||||
let mut display_hints: Vec<super::tool::DisplayHint> = Vec::new();
|
||||
|
||||
loop {
|
||||
iterations += 1;
|
||||
@@ -107,6 +109,7 @@ impl AgentOrchestrator {
|
||||
total_output_tokens,
|
||||
iterations,
|
||||
tool_calls: tool_call_logs,
|
||||
display_hints,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -155,21 +158,25 @@ impl AgentOrchestrator {
|
||||
// 执行每个 Tool Call(受沙箱 allowed_tools 约束)
|
||||
for tc in &tool_calls {
|
||||
let start = std::time::Instant::now();
|
||||
let (tool_result, success) = match self.tool_registry.get(&tc.name) {
|
||||
let (tool_result, success, hint) = match self.tool_registry.get(&tc.name) {
|
||||
Some(tool) => {
|
||||
if let Some(allowed) = allowed_tools {
|
||||
if !allowed.contains(tc.name.as_str()) {
|
||||
(format!("Tool '{}' 在当前角色下不可用", tc.name), false)
|
||||
(
|
||||
format!("Tool '{}' 在当前角色下不可用", tc.name),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
let result = tool.execute(ctx, tc.arguments.clone()).await;
|
||||
(result.output, true)
|
||||
(result.output, true, result.display_hint)
|
||||
}
|
||||
} else {
|
||||
let result = tool.execute(ctx, tc.arguments.clone()).await;
|
||||
(result.output, true)
|
||||
(result.output, true, result.display_hint)
|
||||
}
|
||||
}
|
||||
None => (format!("未知 Tool: {}", tc.name), false),
|
||||
None => (format!("未知 Tool: {}", tc.name), false, None),
|
||||
};
|
||||
let duration = start.elapsed();
|
||||
|
||||
@@ -179,6 +186,10 @@ impl AgentOrchestrator {
|
||||
success,
|
||||
});
|
||||
|
||||
if let Some(h) = hint {
|
||||
display_hints.push(h);
|
||||
}
|
||||
|
||||
messages.push(ChatMessage {
|
||||
role: ChatMessageRole::Tool,
|
||||
content: tool_result,
|
||||
|
||||
@@ -30,7 +30,7 @@ pub struct ToolResult {
|
||||
}
|
||||
|
||||
/// 前端渲染提示 — 告诉前端如何富化展示 Tool 返回的数据
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum DisplayHint {
|
||||
VitalCard {
|
||||
|
||||
@@ -40,6 +40,8 @@ pub struct ChatResponse {
|
||||
pub reply: String,
|
||||
pub message_id: String,
|
||||
pub iterations: usize,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub display_hints: Option<Vec<crate::agent::tool::DisplayHint>>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -252,5 +254,10 @@ where
|
||||
reply: result.reply,
|
||||
message_id,
|
||||
iterations: result.iterations,
|
||||
display_hints: if result.display_hints.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(result.display_hints)
|
||||
},
|
||||
})))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user