文档: 架构方案文档(意图识别论证+多主题愿景/论证+文档物理分类+边界清晰化)

squash合并:
- 意图识别层论证(8维度+10业界佐证)
- 多主题上下文管理愿景+并存论证+补充论证(多轮agentic)
- 架构设计文档物理分类(四子目录+INDEX+命名规范+引用同步+边界清晰化)
- 前端架构技术债清单归档
This commit is contained in:
2026-06-19 15:04:04 +08:00
parent f1a06732fd
commit 998a2f243d
73 changed files with 1083 additions and 80 deletions

View File

@@ -0,0 +1,638 @@
# F-01 模型能力系统 Phase 1 — 模型配置与智能路由设计
> 状态:📐 设计定稿2026-06-16含多角度佐证 + 厂商适配 + 用户流程)
> 类型:架构设计文档(不碰任何 code
> 关联F-260614-05 多模态 Phase 2 / F-260614-04 多 Provider 负载均衡池
> 前置F-07 df-ai-core trait 下沉 ✅ 已完成batch61·2069f79
> 决策记录:见 [功能决策记录-2026-06-14.md](../滚动规范/功能决策记录-2026-06-14.md) §「模型能力系统」行
---
> ## ⚠️ 实施状态2026-06-18 核对:已落地,路由部分改方向)
>
> **阶段 1-6 全部落地**(数据模型 / 探测器 / 厂商拉取 / 路由器 / 7+ 调用点接入 / 前端):
> - 阶段 1 数据模型:`crates/df-ai-core/src/model.rs:106` `ModelConfig`、`:20/35/55/71` 四维度枚举Modality/Capability/CostTier/IntelligenceTier、`:211` `deserialize_model_configs` 向后兼容;`crates/df-storage/src/models.rs:159-160` `AiProviderRecord.model_configs` 字段(**注意:实际落地字段名 `model_configs` 而非本文档 §1.1/§2.3 描述的老 `models` JSON 字段扩展**,新字段经 V18 迁移幂等补列 `crates/df-storage/src/migrations.rs:317-325`)。
> - 阶段 2 探测器:`crates/df-ai/src/model_probe.rs`(已建)+ `crates/df-ai/presets/models.json`(已建)。注意预设表/PatternRule 未拆独立 `preset_table.rs`,合并在 model_probe 内。
> - 阶段 3 厂商拉取:`crates/df-ai/src/model_fetch.rs`已建fetch_models 分派)。
> - 阶段 4 路由器:`crates/df-ai/src/router.rs:40/56/76` `ModelRouter::select` / `select_model_id`。
> - 阶段 5 调用点:主对话 `src-tauri/src/commands/ai/agentic.rs:418/423`、标题 `title.rs:86/91`、知识提炼/嵌入 `knowledge_inject.rs:58-63/359-364`、压缩 `compress.rs:59-64`、项目扫描 `project.rs:533-538/627-632`、灵感评估 `crates/df-ideas/src/adversarial.rs:158-163`、AiNode `crates/df-nodes/src/ai_node.rs:221-226`。
>
> **§6.1 路由逻辑已改方向2026-06-18 决策 B-260618-03**:本文档 §6.1 描述的 `TaskRequirements` 含 `min_intelligence`/`max_cost` 两字段、`select` 含「智力达标」「成本可控」两过滤步、`max_by_key((weight, Reverse(cost_tier)))` 同权重选便宜——**均已删除**。
> - 实际形态:`crates/df-ai/src/router.rs:27-35` `TaskRequirements` 仅 3 字段(`modalities`/`needs_tool_use`/`estimated_context``min_intelligence`/`max_cost` 已删;`:56-63` `select` 过滤链仅 4 步enabled / 模态 / 能力 / 窗口),`:62` 排序纯 `max_by_key(m.weight)`,无 cost tie-break。
> - 根因provider `/v1/models` API 不返回 cost_tier/intelligence两维度 100% 靠预设表写死 + 模型名启发式猜数据无客观依据不可信不参与硬路由。枚举CostTier/IntelligenceTier保留在 `model.rs` 供未来出现真实判别源再接回。
>
> **§6.2 场景路由表 / §6.3 调用点表**:表中行号(如 `agentic.rs:50`/`title.rs:63`/`project.rs:378`)已漂移,实际调用点见上方阶段 5 行号清单。
---
## 0. 摘要
将当前「一个 Provider 一个 default_model 跑全场」升级为「多模型池 + 能力感知 + 智能路由」。
用户只需填 Provider 基本信息 → 自动拉取模型列表 → 自动探测每个模型的模态/能力/价格/智力 → 路由器按场景自动选最优模型。
**核心价值**:含图消息自动选 Vision 模型、标题生成自动选便宜模型、复杂推理自动选强模型、限流自动 fallback。
---
## 1. 现状盘点
### 1.1 数据模型AiProviderRecord
`crates/df-storage/src/models.rs:127-140`
```rust
pub struct AiProviderRecord {
pub provider_type: String, // "openai_compat" | "anthropic_compat"
pub base_url: String,
pub default_model: String, // 唯一实际使用的模型名
pub models: Option<String>, // JSON array of model names展示用未消费
pub config: Option<String>, // JSON extra config几乎没用
pub is_default: bool,
...
}
```
**问题**
1. `models` 存了模型列表但**没有能力标记**,不知道哪个支持 vision / tool use
2. **没有启用/禁用开关**,配了的模型无法控制哪些参与路由
3. **没有权重/优先级**,无法表达"优先用 AA 不可用时 fallback 到 B"
4. `default_model` 是唯一实际使用的模型名,`models` 列表形同虚设
### 1.2 模型选择机制(当前)
```
用户在 Settings 配置 Provider含 base_url + default_model
AiSession.active_provider_id ← 全局唯一活跃 Provider
get_active_provider() → 取 Provider 配置
CompletionRequest { model: provider.default_model } ← 直接用固定 model
```
- 一个时刻只有一个活跃 Provider
- 所有场景(对话/标题/知识提炼/项目扫描)共用同一个 model
- 无 ModelCapability / ModelRouter / 按场景路由机制
---
## 2. 模型配置数据模型
### 2.1 ModelConfig每个模型的完整描述
```rust
/// 单个模型的完整配置4 维度 + 路由控制)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelConfig {
// ── 基础 ──
/// 模型名(如 "glm-4v-flash"
pub model_id: String,
/// 启用/禁用false = 配了但不参与路由,相当于"备档"
#[serde(default = "default_true")]
pub enabled: bool,
/// 用户自定义别名(可选)
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
// ── 维度 1模态能接收什么输入──
pub modalities: Vec<Modality>,
// ── 维度 2能力能做什么──
pub capabilities: Vec<Capability>,
// ── 维度 3价格成本分级──
pub cost_tier: CostTier,
// ── 维度 4聪明程度智力分级──
pub intelligence: IntelligenceTier,
// ── 路由控制 ──
/// 权重0-100同能力候选中优先选权重高的
#[serde(default = "default_weight")]
pub weight: u32,
/// 上下文窗口大小tokens
#[serde(default = "default_context_window")]
pub context_window: usize,
// ── 探测元数据(只读,由 ModelProbe 填充)──
#[serde(skip_serializing_if = "Option::is_none")]
pub probe_source: Option<ProbeSource>,
}
fn default_true() -> bool { true }
fn default_weight() -> u32 { 50 }
fn default_context_window() -> usize { 8192 }
```
### 2.2 枚举定义
```rust
/// 模态(输入类型)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum Modality {
Text,
Vision,
// 未来扩展Audio, Video
}
/// 能力
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum Capability {
ToolUse, // function calling / tool use
Embedding, // 向量嵌入
CodeGen, // 代码生成强项
}
/// 价格分级
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum CostTier {
Free, // 免费
Low, // flash / mini 系列
Medium, // 标准定价
High, // 旗舰定价
}
/// 聪明程度(智力分级)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum IntelligenceTier {
Lite, // flash/mini/lite/nano — 快但简单任务
Standard, // air/standard — 日常够用
Plus, // plus/pro/max — 复杂推理
Ultra, // 最强模型 — 兜底用
}
```
### 2.3 存储方式:扩展 models JSON 字段(零迁移)
`AiProviderRecord.models``["glm-4-flash", "glm-4v"]` 升级为:
```json
[
{
"model_id": "glm-4-flash",
"enabled": true,
"modalities": ["text"],
"capabilities": ["tool_use"],
"cost_tier": "low",
"intelligence": "lite",
"weight": 90,
"context_window": 128000
},
{
"model_id": "glm-4v",
"enabled": true,
"modalities": ["text", "vision"],
"capabilities": ["tool_use"],
"cost_tier": "medium",
"intelligence": "plus",
"weight": 70,
"context_window": 128000
}
]
```
**向后兼容**:自定义反序列化器,老格式 `["model-a"]` → 自动转成默认配置列表。
```rust
fn deserialize_models<'de, D>(d: D) -> Result<Vec<ModelConfig>, D::Error>
where D: serde::Deserializer<'de> {
let v = serde_json::Value::deserialize(d)?;
match v {
// 老格式:字符串数组 → 每个转默认 ModelConfig
serde_json::Value::Array(arr) if arr.iter().all(|v| v.is_string()) => {
arr.into_iter()
.filter_map(|v| v.as_str().map(|s| ModelConfig::with_defaults(s)))
.collect::<Vec<_>>()
.pipe(Ok)
}
// 新格式ModelConfig 数组
serde_json::Value::Array(_) => {
serde_json::from_value::<Vec<ModelConfig>>(v).map_err(D::Error::custom)
}
_ => Ok(vec![]),
}
}
```
**否决方案**:新建 `ai_models` 独立表 — 增量复杂度高(迁移 + 新 Repo`models` JSON 字段已够用。
---
## 3. 多角度佐证
### 3.1 行业对标
| 产品 | 模型配置方式 | 对照 |
|------|------------|------|
| OpenRouter | 每个模型标 modality/pricing/context_length | 4 维度完全覆盖,且加了 intelligence 分级 |
| Cursor | 内置模型能力感知(自动判断图片/工具) | 预设表 + 启发式探测本质相同 |
| LangChain ModelRouter | 按 max_tokens/supports_tool_use 路由 | capabilities + modalities 路由一致 |
| One-API / New-API | 纯渠道管理,无能力感知 | 能力标记是显著增量 |
**结论**4 维度配置是行业标配的超集,非过度设计。
### 3.2 路由器实际需求倒推
| 路由决策 | 必须知道 | 对应维度 | 缺了会怎样 |
|---------|---------|---------|-----------|
| 含图消息选哪个 | 是否支持 Vision | 模态 | 发给纯文本模型 → 400 |
| Agentic loop 选哪个 | 是否支持 tool_use | 能力 | 发 tool 定义给不支持的 → 忽略/报错 |
| 标题生成选哪个 | 够便宜够快 | 价格+智力 | 用旗舰模型 → 烧钱 |
| 复杂推理选哪个 | 够聪明 | 智力 | 用 flash → 质量差 |
| 上下文超长选哪个 | 窗口够大 | context_window | 超窗口 → 截断 |
**结论**:每个维度直接对应路由器必须做的判断,缺一不可。
### 3.3 用户认知负担
| 信息 | 用户负担 | 说明 |
|------|---------|------|
| 模型名 | 零 | 从文档复制 |
| 模态 | 低 | 文档一眼可见,或名字带 v |
| 能力 | 低 | 文档明确标注 |
| 价格 | 中 | 查文档,但分级别直觉可判 |
| 智力 | 低 | flash < air < plus 是行业共识 |
**结论**4 个维度都是用户本来就知道的,加上自动填充,门槛极低。
### 3.4 可扩展性
| 未来需求 | 支持 | 扩展方式 |
|---------|------|---------|
| 音频输入 | ✅ | Modality 加 Audio |
| 视频输入 | ✅ | Modality 加 Video |
| 精确价格控制 | ✅ | cost_tier 升级为精确数值 |
| 流式支持差异 | ✅ | Capability 加 Streaming |
| 自定义能力标签 | ✅ | Capability 加 Custom(String) |
**结论**enum + Vec 开放式,未来加维度只需加变体。
### 3.5 数据兼容性
`models: ["model-a"]` → 自定义反序列化自动补默认值。`default_model` 字段保留向后兼容。零迁移脚本。
### 3.6 竞品缺陷反证
| 问题 | 竞品现状 | 我们的方案 |
|------|---------|-----------|
| 含图发给纯文本模型 | One-API 不感知能力 | 路由器自动选 Vision |
| 标题生成用旗舰模型 | 固定模型手动切 | 按 intelligence+cost 自动选 Lite |
| 新模型不知道能不能用 | 用户自己试 | 预设表 + 启发式自动推断 |
| 限流后无 fallback | 手动切换 | 按 weight 自动 fallback |
### 3.7 实施风险
| 风险 | 评估 | 缓解 |
|------|------|------|
| 预设表维护 | 低(主流 20-30 个,半年更新) | JSON 配置文件可热更新 |
| 启发式误判 | 低(命名是行业惯例) | 标注 Medium 可信度,用户可修正 |
| 路由器选错 | 低 | weight fallback + 降级兜底 |
**7 个角度一致指向:方案可行且合理。**
---
## 4. 模型探测器ModelProbe
### 4.1 多源探测
每个模型的信息从多个来源获取,交叉验证:
```rust
pub struct ModelProbe {
preset_table: ModelPresetTable,
}
impl ModelProbe {
pub async fn probe(&self, provider: &AiProviderRecord, model_id: &str) -> ModelProfile {
// 1. 启发式:从模型名推断
let heuristic = self.heuristic_infer(model_id);
// 2. 查预设表(精确匹配 > 模糊匹配)
let preset = self.preset_table.lookup(model_id);
// 3. 多源合并(预设表 > 启发式)
let merged = self.merge_sources(heuristic, preset);
// 4. 最终结果 + 标注每个维度的置信度
self.finalize(merged)
}
}
```
### 4.2 探测优先级链
```
用户手动设定 (UserSet) ← 最高优先,永不被覆盖
内置预设表精确匹配 (PresetTable) ← 高可信
模型名启发式 (Heuristic) ← 中可信,标注"建议确认"
默认值 (Default) ← 低可信
```
### 4.3 探测结果带置信度
```rust
pub struct ModelProfile {
pub model_id: String,
pub modalities: SourcedField<Vec<Modality>>,
pub capabilities: SourcedField<Vec<Capability>>,
pub cost_tier: SourcedField<CostTier>,
pub intelligence: SourcedField<IntelligenceTier>,
pub context_window: SourcedField<usize>,
}
pub struct SourcedField<T> {
pub value: T,
pub source: Source,
pub confidence: Confidence,
}
pub enum Source { PresetTable, Heuristic, UserSet }
pub enum Confidence { High, Medium, Low }
```
### 4.4 内置预设表
```rust
pub struct ModelPresetTable {
exact: HashMap<String, ModelPreset>, // 精确匹配
patterns: Vec<PatternRule>, // 模糊匹配
}
// 精确匹配示例
"glm-4-flash" => { modalities:[Text], capabilities:[ToolUse], cost:Low, intelligence:Lite, context:128K }
"glm-4-air" => { modalities:[Text], capabilities:[ToolUse], cost:Low, intelligence:Standard }
"glm-4-plus" => { modalities:[Text], capabilities:[ToolUse], cost:Medium, intelligence:Plus }
"glm-4v" => { modalities:[Text,Vision], capabilities:[ToolUse], cost:Medium, intelligence:Plus }
"glm-4v-flash" => { modalities:[Text,Vision], capabilities:[ToolUse], cost:Low, intelligence:Lite }
"deepseek-chat" => { modalities:[Text], capabilities:[ToolUse], cost:Low, intelligence:Standard }
"deepseek-coder"=> { modalities:[Text], capabilities:[ToolUse,CodeGen], cost:Low, intelligence:Plus }
"claude-3-5-sonnet-20241022" => { modalities:[Text,Vision], capabilities:[ToolUse], cost:High, intelligence:Ultra }
"claude-3-haiku-20240307" => { modalities:[Text,Vision], capabilities:[ToolUse], cost:Low, intelligence:Standard }
"gpt-4o" => { modalities:[Text,Vision], capabilities:[ToolUse], cost:High, intelligence:Ultra }
"gpt-4o-mini" => { modalities:[Text,Vision], capabilities:[ToolUse], cost:Low, intelligence:Standard }
"embedding-3" => { modalities:[], capabilities:[Embedding], cost:Low, intelligence:Lite }
// 模糊匹配规则
PatternRule { keyword: "flash|mini|lite|nano", Intelligence(Lite) }
PatternRule { keyword: "plus|pro|max|ultra", Intelligence(Plus) }
PatternRule { keyword: "v$|vl|vision|-v\\d", Modality(Vision) }
PatternRule { keyword: "embed", Capability(Embedding) }
PatternRule { keyword: "code|coder", Capability(CodeGen) }
```
预设表以 JSON 配置文件形式存储(`crates/df-ai/presets/models.json`),可热更新不随版本绑。
---
## 5. 厂商模型列表适配
### 5.1 差异盘点
| 厂商/协议 | 端点 | 鉴权 | 返回格式 |
|-----------|------|------|---------|
| OpenAI 兼容GLM/DeepSeek/Kimi/通义) | `GET /v1/models` | Bearer | `{data:[{id, owned_by}]}` |
| Anthropic | `GET /v1/models` | x-api-key + version | `{data:[{id, display_name}]}` |
| 中转站 | `GET /v1/models` | Bearer | 同 OpenAI但可能混入噪音或返回 404 |
| Ollama | `GET /api/tags` | 无 | `{models:[{name, size}]}` — 完全不同 |
### 5.2 统一拉取接口
```rust
/// 模型拉取结果(统一格式)
pub struct FetchedModel {
pub model_id: String,
pub display_name: Option<String>,
}
/// 按 provider_type 分派
pub async fn fetch_models(
provider_type: &str,
base_url: &str,
api_key: &str,
) -> Result<Vec<FetchedModel>, FetchError> {
match provider_type {
"openai_compat" => fetch_openai_compat(base_url, api_key).await,
"anthropic_compat" => fetch_anthropic_compat(base_url, api_key).await,
_ => Err(FetchError::Unsupported),
}
}
```
### 5.3 URL 智能拼接
```rust
fn build_models_url(base_url: &str) -> String {
let url = base_url.trim_end_matches('/');
if url.ends_with("/chat/completions") {
url.replace("/chat/completions", "/models")
} else if url.ends_with("/v1") || url.ends_with("/v4") {
format!("{}/models", url)
} else if url.ends_with("/models") {
url.to_string()
} else {
format!("{}/v1/models", url)
}
}
```
### 5.4 噪音过滤
```rust
fn is_non_chat_model(id: &str) -> bool {
let id = id.to_lowercase();
id.contains("dall-e") || id.contains("midjourney") // 图片生成
|| id.contains("tts") || id.contains("whisper") // 语音
|| id.contains("moderation") // 审查
// embedding 保留(知识库需要)
}
```
### 5.5 失败降级
| 错误 | 处理 |
|------|------|
| 404 / 不支持 | 静默降级到手动输入(批量文本) |
| 401 / 403 | 提示密钥问题 |
| 网络/超时 | 提示检查连接 |
| 解析失败 | 提示格式不兼容,降级手动 |
**核心原则:自动拉取是锦上添花,手动输入永远保底。**
---
## 6. 模型路由器ModelRouter
### 6.1 路由逻辑
```rust
pub struct TaskRequirements {
pub modalities: Vec<Modality>, // 任务需要什么模态
pub needs_tool_use: bool, // 是否需要工具调用
pub min_intelligence: IntelligenceTier, // 最低智力要求
pub max_cost: Option<CostTier>, // 成本上限(可选)
pub estimated_context: usize, // 预估上下文大小
}
impl ModelRouter {
pub fn select(&self, req: &TaskRequirements, pool: &[ModelConfig]) -> Option<&ModelConfig> {
pool.iter()
.filter(|m| m.enabled) // 1. 只选启用的
.filter(|m| req.modalities.iter().all(|r| m.modalities.contains(r))) // 2. 模态匹配
.filter(|m| !req.needs_tool_use || m.capabilities.contains(&Capability::ToolUse)) // 3. 能力匹配
.filter(|m| m.intelligence >= req.min_intelligence) // 4. 智力达标
.filter(|m| req.max_cost.map_or(true, |max| m.cost_tier <= max)) // 5. 成本可控
.filter(|m| m.context_window >= req.estimated_context) // 6. 窗口够大
.max_by_key(|m| (m.weight, -(m.cost_tier as i32))) // 7. 权重优先,同权重选便宜的
}
}
```
### 6.2 场景路由表
| 场景 | 路由偏好 | 典型选择 |
|------|---------|---------|
| 标题生成 | Lite + Low cost | glm-4-flash |
| 日常对话 | Standard | glm-4-air |
| 写代码 | Standard/Plus + CodeGen | deepseek-coder |
| 含图片 | Vision + Standard | glm-4v |
| 复杂推理 | Plus/Ultra | glm-4-plus |
| 知识嵌入 | Embedding | embedding-3 |
### 6.3 7 个调用点接入
| 调用点 | 当前代码位置 | TaskRequirements |
|--------|------------|-----------------|
| 主对话 stream_llm | agentic.rs:50 | 动态按消息内容含图→Vision |
| 标题生成 | title.rs:63 | Lite + Low省钱 |
| 知识提炼 | knowledge_inject.rs:33,323 | Standard |
| 知识嵌入 | knowledge_inject.rs embedding | Embedding |
| 项目扫描描述 | project.rs:378 | Standard含图→Vision |
| 灵感评估 | df-ideas adversarial.rs | Standard |
| AiNode 工作流 | df-nodes ai_node.rs:118 | 按节点配置 |
---
## 7. 用户配置与使用流程
### 7.1 配置流程(做一次)
```
Step 1: 填 Provider 基本信息(名称/类型/地址/密钥)
Step 2: 点「测试连接并拉取模型」
↓ 自动调 /v1/models按协议分派
↓ 自动过滤非 chat 模型
↓ 自动探测每个模型特征(预设表 + 启发式)
Step 3: 显示模型列表(已标注特征 + 可信度)
↓ 用户确认或微调(大多数不需要进这步)
Step 4: 完成
```
**拉取失败时降级**:手动输入模型名(批量文本,每行一个)→ 同样走探测。
### 7.2 日常使用(零配置)
```
用户发消息 → 路由器自动选模型 → 用户无感 → 出结果
```
**手动覆盖**AiChat 顶部下拉选「自动(推荐)」或指定具体模型。
**自动 fallback**:模型限流/失败 → 按 weight 选次高 → 自动重试。
### 7.3 简洁性指标
| 传统做法 | 我们的做法 | 省了什么 |
|---------|-----------|---------|
| 手动填每个模型能力 | 预设表自动填充 | 省 90% 配置 |
| 自己记哪个支持图片 | 路由器自动选 | 省记忆 |
| 每次对话手动切模型 | 自动路由 | 省操作 |
| 限流后手动换 | 自动 fallback | 省故障处理 |
**用户最小操作路径**:填 3 个字段 → 点 1 个按钮 → 完成。
---
## 8. 实施分阶段
### 阶段 1数据模型df-ai-core + df-storage
| 文件 | 改动 |
|------|------|
| `crates/df-ai-core/src/provider.rs` | 新增 ModelConfig / Modality / Capability / CostTier / IntelligenceTier |
| `crates/df-storage/src/models.rs` | AiProviderRecord.models 反序列化升级(兼容老格式) |
| `crates/df-ai/src/model_config.rs`(新建) | deserialize_models 兼容函数 + ModelConfig::with_defaults |
### 阶段 2预设表 + 探测器df-ai
| 文件 | 改动 |
|------|------|
| `crates/df-ai/presets/models.json`(新建) | 主流模型预设数据 |
| `crates/df-ai/src/model_probe.rs`(新建) | ModelProbe + 启发式 + 多源合并 |
| `crates/df-ai/src/preset_table.rs`(新建) | 预设表加载 + 精确/模糊匹配 |
### 阶段 3厂商模型列表拉取df-ai
| 文件 | 改动 |
|------|------|
| `crates/df-ai/src/model_fetch.rs`(新建) | fetch_models + openai_compat/anthropic_compat 分派 + URL 拼接 + 噪音过滤 |
### 阶段 4路由器df-ai
| 文件 | 改动 |
|------|------|
| `crates/df-ai/src/router.rs`(新建) | ModelRouter + TaskRequirements + select 逻辑 |
### 阶段 57 调用点接入src-tauri
| 文件 | 改动 |
|------|------|
| `src-tauri/src/commands/ai/agentic.rs` | 主对话路由(按 has_image 动态选) |
| `src-tauri/src/commands/ai/title.rs` | 标题生成路由Lite + Low |
| `src-tauri/src/commands/ai/knowledge_inject.rs` | 知识提炼/嵌入路由 |
| `src-tauri/src/commands/project.rs` | 项目扫描路由 |
| `src-tauri/src/commands/ai/commands.rs` | 新增 IPCai_fetch_models / ai_probe_model |
### 阶段 6前端src
| 文件 | 改动 |
|------|------|
| `src/api/types.ts` | ModelConfig 类型对齐 |
| `src/components/settings/` | Provider 配置页加模型列表 + 探测结果展示 + 启用/禁用/权重 |
| `src/components/AiChat.vue` | 顶部模型下拉(自动/指定) |
---
## 9. 与 F-05多模态的关系
| 子能力 | 依赖 F-01 | 说明 |
|--------|------------|------|
| ContentPart 数据模型 | ❌ | 纯类型升级,与 ModelConfig 解耦 |
| provider content 数组化 | ❌ | 协议层改造,与路由器无关 |
| 前端粘贴/拖拽/渲染 | ❌ | 纯前端 |
| **vision 模型自动路由** | ✅ | ModelRouter 按 modalities 含 Vision 筛选 |
F-05 可独立先做 80%(数据模型 + provider + 前端vision 路由留到 F-01 落地后接入。
过渡期靠 ModelConfig.modalities 手动标注 + 路由器筛选。
---
## 10. 不做的事(显式排除)
- 不做模型自动下载/安装(仅 API 模型,不含本地模型管理)
- 不做模型 benchmark 自动跑分(智力分级靠预设表 + 用户确认)
- 不做多 Provider 负载均衡(属 F-04本任务只做单 Provider 内多模型路由)
- 不做模型用量统计/成本告警(属运维增强,后续独立做)
- 不新建 ai_models 独立表(扩展 models JSON 字段够用)
- 本文档不实施任何 code纯设计