diff --git a/src-tauri/src/commands/ai/tool_registry.rs b/src-tauri/src/commands/ai/tool_registry.rs index 21e182e..c0533ca 100644 --- a/src-tauri/src/commands/ai/tool_registry.rs +++ b/src-tauri/src/commands/ai/tool_registry.rs @@ -140,20 +140,22 @@ pub fn build_ai_tool_registry(db: &Arc) -> AiToolRegistry { // ── 只读 (Low) ── registry.register( - "list_projects", "列出所有项目,返回项目列表(ID、名称、状态、描述)", + "list_projects", "列出所有项目,返回项目列表(ID、名称、状态、描述)。最多返回 50 条", df_ai::ai_tools::object_schema(vec![]), RiskLevel::Low, { let db = db.clone(); Box::new(move |_args: serde_json::Value| { let db = db.clone(); Box::pin(async move { let repo = df_storage::crud::ProjectRepo::new(&db); let mut items = repo.list_active().await?; // list_active 排除回收站(deleted_at),防 LLM 看到已软删项目 + let total = items.len(); + let truncated = total > 50; items.truncate(50); // 防 LLM context 膨胀 - Ok(serde_json::to_value(items)?) + Ok(serde_json::json!({ "items": items, "truncated": truncated })) }) })}, ); registry.register( - "list_tasks", "列出任务,可按 project_id 筛选", + "list_tasks", "列出任务,可按 project_id 筛选。最多返回 50 条", df_ai::ai_tools::object_schema(vec![("project_id", "string", false)]), RiskLevel::Low, { let db = db.clone(); Box::new(move |args: serde_json::Value| { let db = db.clone(); @@ -164,21 +166,25 @@ pub fn build_ai_tool_registry(db: &Arc) -> AiToolRegistry { } else { repo.list_all().await? }; + let total = tasks.len(); + let truncated = total > 50; tasks.truncate(50); // 防 LLM context 膨胀 - Ok(serde_json::to_value(tasks)?) + Ok(serde_json::json!({ "items": tasks, "truncated": truncated })) }) })}, ); registry.register( - "list_ideas", "列出所有灵感", + "list_ideas", "列出所有灵感。最多返回 50 条", df_ai::ai_tools::object_schema(vec![]), RiskLevel::Low, { let db = db.clone(); Box::new(move |_args: serde_json::Value| { let db = db.clone(); Box::pin(async move { let repo = df_storage::crud::IdeaRepo::new(&db); let mut items = repo.list_all().await?; + let total = items.len(); + let truncated = total > 50; items.truncate(50); // 防 LLM context 膨胀 - Ok(serde_json::to_value(items)?) + Ok(serde_json::json!({ "items": items, "truncated": truncated })) }) })}, ); diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue index db58c58..618932c 100644 --- a/src/components/AiChat.vue +++ b/src/components/AiChat.vue @@ -709,9 +709,12 @@ function onMessagesScroll() { refreshBackToBottom() } -function scrollToBottom() { +function scrollToBottom(smooth = false) { if (messagesContainer.value) { - messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight + messagesContainer.value.scrollTo({ + top: messagesContainer.value.scrollHeight, + behavior: smooth ? 'smooth' : 'instant', + }) showBackToBottom.value = false } }