1 changed files with 517 additions and 0 deletions
@ -0,0 +1,517 @@ |
|||
# Agent 独立化架构文档 |
|||
|
|||
> 分支:`feat/agent-independence` | Commit:`adeb09a` | 日期:2026-08-09 |
|||
|
|||
## 目录 |
|||
|
|||
- [1. 设计目标](#1-设计目标) |
|||
- [2. 数据库 Schema](#2-数据库-schema) |
|||
- [3. 服务端架构](#3-服务端架构) |
|||
- [4. API 端点](#4-api-端点) |
|||
- [5. 前端架构](#5-前端架构) |
|||
- [6. 多 Session 并行流式输出](#6-多-session-并行流式输出) |
|||
- [7. 标题生成机制](#7-标题生成机制) |
|||
- [8. 模型解析优先级](#8-模型解析优先级) |
|||
- [9. 速率限制](#9-速率限制) |
|||
- [10. 协作接口预留](#10-协作接口预留) |
|||
- [11. Seed 数据](#11-seed-数据) |
|||
- [12. 已知问题与后续工作](#12-已知问题与后续工作) |
|||
|
|||
--- |
|||
|
|||
## 1. 设计目标 |
|||
|
|||
将原本单一的"agent"(全局配置 + 硬编码 system prompt)改造为**独立化、可配置的多 agent 架构**,为多 agent 协作做准备。 |
|||
|
|||
### 核心变更 |
|||
|
|||
| 改造前 | 改造后 | |
|||
|--------|--------| |
|||
| 全局 system prompt(`/api/agent/system-prompt`) | per-agent `systemPrompt` 字段(DB) | |
|||
| 工具集全局共享 | per-agent 工具关联(多对多表) | |
|||
| 模型由用户选择 | 模型优先级:用户 preferred > agent.defaultModelId > 全局 fallback | |
|||
| 路由 `/api/agent/...` | 路由 `/api/agents/:agentSlug/...` | |
|||
| 前端单页 `/` | 前端动态路由 `/chat/:agentSlug` | |
|||
| 单 session 流式 | 多 session 并行流式(instance 隔离) | |
|||
|
|||
### 兼容性 |
|||
|
|||
- **Breaking change**:旧 API 路径 `/api/agent/...` 已删除 |
|||
- DB 迁移策略:清库重建(dev 阶段) |
|||
- 旧迁移文件全部删除,仅保留单个 `0000_natural_ken_ellis.sql` |
|||
|
|||
--- |
|||
|
|||
## 2. 数据库 Schema |
|||
|
|||
文件:`packages/drizzle-pkg/lib/schema/agent.ts` |
|||
|
|||
### 2.1 `agents` 表(新增) |
|||
|
|||
Agent 定义表,每个 agent 是一个独立可配置的 AI 助手。 |
|||
|
|||
| 字段 | 类型 | 默认值 | 说明 | |
|||
|------|------|--------|------| |
|||
| `id` | integer PK | auto | 主键 | |
|||
| `slug` | text(50) unique | - | URL 标识(如 `default`、`coder`) | |
|||
| `name` | text(100) | - | 显示名称 | |
|||
| `description` | text | null | 描述 | |
|||
| `systemPrompt` | text | `""` | 系统提示词 | |
|||
| `defaultModelId` | integer | null | 默认模型 ID | |
|||
| `titleModelId` | integer | null | 标题生成模型 ID(fallback 到 defaultModelId) | |
|||
| `titleStrategy` | text(20) | `"llm"` | 标题策略:`llm` / `first-line` / `none` | |
|||
| `enableThinking` | integer | `0` | 是否启用思考模式 | |
|||
| `enableTools` | integer | `1` | 是否启用工具 | |
|||
| `maxStepCount` | integer | `6` | 最大工具调用步数 | |
|||
| `isDefault` | integer | `0` | 是否默认 agent(全局唯一) | |
|||
| `isCallable` | integer | `0` | 是否可被其他 agent 调用(协作接口) | |
|||
| `sortOrder` | integer | `0` | 排序 | |
|||
| `enabled` | integer | `1` | 是否启用 | |
|||
| `createdAt` / `updatedAt` | timestamp_ms | now | 时间戳 | |
|||
|
|||
索引:`agents_enabled_idx`、`agents_sort_order_idx` |
|||
|
|||
### 2.2 `agent_tool_associations` 表(新增) |
|||
|
|||
Agent 与工具的多对多关联表。 |
|||
|
|||
| 字段 | 类型 | 说明 | |
|||
|------|------|------| |
|||
| `id` | integer PK | 主键 | |
|||
| `agentId` | integer FK → agents.id (cascade) | Agent ID | |
|||
| `toolId` | text FK → agentTools.id (cascade) | 工具 ID | |
|||
| `needsApproval` | integer | 是否需要审批(覆盖工具级配置) | |
|||
| `sortOrder` | integer | 排序 | |
|||
| `createdAt` | timestamp_ms | 创建时间 | |
|||
|
|||
唯一索引:`agent_tool_assoc_uniq(agentId, toolId)` |
|||
|
|||
### 2.3 `agent_sessions` 表(改造) |
|||
|
|||
新增 `agentId` 外键字段。 |
|||
|
|||
| 新增字段 | 类型 | 说明 | |
|||
|----------|------|------| |
|||
| `agentId` | integer FK → agents.id (**set null**) | 所属 agent(agent 删除时 session 保留) | |
|||
|
|||
> `enableThinking` / `enableTools` 改为**可空**(null 表示继承 agent 配置) |
|||
|
|||
新增索引:`idx_agent_sessions_agent(agentId)` |
|||
|
|||
### 2.4 其他表(未改动) |
|||
|
|||
- `agent_messages`:消息表 |
|||
- `agent_message_feedback`:消息反馈表 |
|||
|
|||
--- |
|||
|
|||
## 3. 服务端架构 |
|||
|
|||
### 3.1 目录结构 |
|||
|
|||
``` |
|||
server/service/agent/ |
|||
├── agent.ts # Agent CRUD + 工具关联查询 |
|||
├── chat-engine.ts # 核心聊天引擎(executeChat) |
|||
├── session.ts # Session CRUD + 消息持久化 |
|||
├── title.ts # 标题生成(llm / first-line / none) |
|||
├── rate-limit.ts # 速率限制(per-agentSlug 隔离) |
|||
├── abort-manager.ts # 流式中断管理 |
|||
├── collaboration.ts # 协作接口 stub(预留) |
|||
├── stream-buffer.ts # 流缓冲(SSE 重连支持) |
|||
├── stored-part.ts # 消息 parts 存储类型 |
|||
└── types.ts # 类型定义 |
|||
|
|||
server/service/llm/ |
|||
└── model-resolver.ts # 模型解析(用户/系统/任意) |
|||
``` |
|||
|
|||
### 3.2 Agent Service (`agent.ts`) |
|||
|
|||
核心函数: |
|||
|
|||
| 函数 | 说明 | |
|||
|------|------| |
|||
| `getAgentBySlug(slug)` | 按 slug 查询启用的 agent | |
|||
| `getAgentBySlugAny(slug)` | 按 slug 查询(含禁用) | |
|||
| `getDefaultAgent()` | 获取默认 agent(isDefault=1,fallback 到 sortOrder 最小) | |
|||
| `listAgents(onlyEnabled)` | 列出 agent | |
|||
| `getAgentWithTools(slug)` | Agent + 关联工具 | |
|||
| `getAgentTools(agentId)` | 获取 agent 的工具列表 | |
|||
| `createAgent(input)` | 创建 agent(含工具关联 + isDefault 唯一性维护) | |
|||
| `updateAgent(id, input)` | 更新 agent(含工具关联重建) | |
|||
| `deleteAgent(id)` | 删除 agent(cascade 删关联,session 的 agentId set null) | |
|||
|
|||
### 3.3 Chat Engine (`chat-engine.ts`) |
|||
|
|||
从原 HTTP handler 中抽离的核心逻辑,函数签名: |
|||
|
|||
```typescript |
|||
export interface ChatEngineParams { |
|||
agent: AgentRow; |
|||
session: AgentSessionRow; |
|||
user: { id: number; role: string | null } | null; |
|||
tempToken: string | null; |
|||
body: { content, editMessageId?, regenerate?, ... }; |
|||
ip: string; |
|||
preferredModelId?: number | null; |
|||
requestId?: string; |
|||
registerAbort: (sessionId, controller) => void; |
|||
unregisterAbort: (sessionId) => void; |
|||
setRateLimitHeaders?: (headers) => void; |
|||
onClientClose?: (cb) => void; |
|||
} |
|||
|
|||
export async function executeChat(params: ChatEngineParams): Promise<ChatEngineResult> |
|||
``` |
|||
|
|||
关键流程: |
|||
1. 速率限制检查(未登录用户) |
|||
2. 模型解析(优先级见 [§8](#8-模型解析优先级)) |
|||
3. 构建 ModelMessage(含历史消息 + 工具调用 parts) |
|||
4. `streamText()` 流式生成 |
|||
5. `onFinish` 回调:保存消息 + 触发标题生成(fire-and-forget) |
|||
6. `onAbort` 回调:保存已生成内容 |
|||
|
|||
### 3.4 Model Resolver (`model-resolver.ts`) |
|||
|
|||
| 函数 | 说明 | |
|||
|------|------| |
|||
| `resolveModelForUser(modelId, userId)` | 为用户解析模型(查用户私有 provider) | |
|||
| `resolveModelAny(modelId)` | 任意解析(系统级,用于未登录用户) | |
|||
| `toLanguageModel(resolved)` | 转换为 AI SDK 的 `LanguageModel` | |
|||
| `resolveLanguageModel(provider, modelId)` | 底层:根据 parseMode 创建 OpenAI / OpenAI-Compatible | |
|||
|
|||
### 3.5 Rate Limit (`rate-limit.ts`) |
|||
|
|||
- **Key 隔离**:`${agentSlug}:${sessionId}` / `${agentSlug}:${ip}` |
|||
- **限制**:每 session 20 次、每 IP 每日 50 次 |
|||
- 仅对未登录用户生效 |
|||
|
|||
### 3.6 Abort Manager (`abort-manager.ts`) |
|||
|
|||
全局 `Map<sessionId, AbortController>`,支持按 session 中断流式生成。 |
|||
|
|||
--- |
|||
|
|||
## 4. API 端点 |
|||
|
|||
### 4.1 Agent 管理 |
|||
|
|||
| 方法 | 路径 | 说明 | |
|||
|------|------|------| |
|||
| GET | `/api/agents` | 列出所有 agent | |
|||
| POST | `/api/agents` | 创建 agent | |
|||
| GET | `/api/agents/:agentSlug` | 获取 agent 详情 | |
|||
| PUT | `/api/agents/:agentSlug` | 更新 agent | |
|||
| DELETE | `/api/agents/:agentSlug` | 删除 agent | |
|||
| GET | `/api/agents/:agentSlug/info` | 获取 agent 信息(含工具列表,前端初始化用) | |
|||
|
|||
### 4.2 Session 管理 |
|||
|
|||
| 方法 | 路径 | 说明 | |
|||
|------|------|------| |
|||
| GET | `/api/agents/:agentSlug/sessions` | 列出 session | |
|||
| POST | `/api/agents/:agentSlug/sessions` | 创建 session | |
|||
| GET | `/api/agents/:agentSlug/sessions/:id` | 获取 session 详情 | |
|||
| PUT | `/api/agents/:agentSlug/sessions/:id` | 更新 session(标题等) | |
|||
| DELETE | `/api/agents/:agentSlug/sessions/:id` | 删除 session | |
|||
| PUT | `/api/agents/:agentSlug/sessions/:id/config` | 更新 session 配置(modelId、enableThinking、enableTools) | |
|||
| GET | `/api/agents/:agentSlug/sessions/:id/messages` | 获取 session 消息 | |
|||
|
|||
### 4.3 聊天 |
|||
|
|||
| 方法 | 路径 | 说明 | |
|||
|------|------|------| |
|||
| POST | `/api/agents/:agentSlug/chat` | 发送消息(启动流式) | |
|||
| GET | `/api/agents/:agentSlug/chat/stream` | SSE 流(断线重连) | |
|||
| POST | `/api/agents/:agentSlug/chat/stop` | 停止生成 | |
|||
| POST | `/api/agents/:agentSlug/chat/tool-approve` | 工具审批 | |
|||
| POST | `/api/agents/:agentSlug/chat/cleanup-approvals` | 清理审批 | |
|||
|
|||
### 4.4 其他 |
|||
|
|||
| 方法 | 路径 | 说明 | |
|||
|------|------|------| |
|||
| GET | `/api/agents/:agentSlug/models` | 可用模型列表 | |
|||
| POST | `/api/agents/:agentSlug/feedback` | 消息反馈 | |
|||
| POST | `/api/agents/:agentSlug/migrate` | 旧数据迁移 | |
|||
|
|||
### 4.5 已删除的端点 |
|||
|
|||
- `GET /api/agent/system-prompt` — 已废弃(system prompt 在 agent 配置中) |
|||
- 所有 `/api/agent/...` 路径 |
|||
|
|||
### 4.6 鉴权白名单 |
|||
|
|||
文件:`packages/common/config/index.ts` |
|||
|
|||
`API_ALLOWLIST` 和 `FRONTEND_PAGE_ALLOWLIST` 已更新,支持 `:agentSlug` 参数匹配。 |
|||
|
|||
`FRONTEND_PAGE_ALLOWLIST` 新增 `/chat/:agentSlug`,允许未登录用户访问聊天页面。 |
|||
|
|||
--- |
|||
|
|||
## 5. 前端架构 |
|||
|
|||
### 5.1 路由 |
|||
|
|||
| 路径 | 说明 | |
|||
|------|------| |
|||
| `/` | 重定向到 `/chat/default` | |
|||
| `/chat/:agentSlug` | Agent 聊天页面 | |
|||
|
|||
文件: |
|||
- `app/pages/index.vue` — 重定向页 |
|||
- `app/pages/chat/[agentSlug]/index.vue` — 主聊天页面 |
|||
|
|||
### 5.2 Composables |
|||
|
|||
#### `useAgentSessions(agentSlug: string)` |
|||
|
|||
Session 列表管理,**组件级实例**(非全局)。 |
|||
|
|||
关键函数: |
|||
- `fetchSessions()` — 加载 session 列表 |
|||
- `createSession()` — 创建新 session |
|||
- `newSession()` — 智能创建:若最新 session 为空(`lastActiveAt === createdAt`)则复用,否则创建新的 |
|||
- `selectSession(id)` — 切换 session |
|||
- `deleteSession(id)` — 删除 session |
|||
- `touchSessionOrder(id)` — 将 session 移到列表顶部(更新 `lastActiveAt`) |
|||
- `refreshSessionTitle(id)` — 从服务端同步标题 |
|||
|
|||
#### `useAgentChatStore(options)` |
|||
|
|||
多 session 聊天实例管理,**核心架构**。 |
|||
|
|||
``` |
|||
useAgentChatStore |
|||
├── instances: Map<agentSlug:sid, AgentChatInstance> # 每个 session 独立实例 |
|||
├── currentSessionId: ref<string | null> |
|||
├── currentInstance: computed → 当前 session 的 instance |
|||
└── messages/isLoading/... : computed → 代理到 currentInstance |
|||
``` |
|||
|
|||
关键设计: |
|||
- **每个 session 有独立的 `AgentChatInstance`**(独立的 `messages` ref、`isLoading` ref、`abortController`) |
|||
- 切换 session 只是改 `currentSessionId`,旧 instance 的流**继续在后台运行** |
|||
- `getInstance(sid)` — 获取/创建 instance |
|||
- `removeInstance(sid)` — 停止 + 清除 + 移除 instance |
|||
- `stopAll()` — 停止所有 instance 的流(**新建对话时不调用**) |
|||
- `getLoadingSessionIds()` — 获取所有正在流式输出的 session |
|||
|
|||
#### `useAgentChat(options)` |
|||
|
|||
单 session 聊天逻辑,**闭包隔离**。 |
|||
|
|||
关键函数: |
|||
- `send(content, opts?)` — 发送消息 + 流式处理 |
|||
- `processStream(res, assistantIdx)` — SSE 流解析(操作闭包内的 `messages` ref) |
|||
- `stopGeneration()` — 停止生成(abort) |
|||
- `stopAndSave()` — 停止 + 保存已生成内容 |
|||
- `loadMessages(sid)` — 加载历史消息 |
|||
- `respondToApproval(toolCallId, approved)` — 工具审批响应 |
|||
|
|||
### 5.3 页面组件 (`app/pages/chat/[agentSlug]/index.vue`) |
|||
|
|||
关键逻辑: |
|||
|
|||
```typescript |
|||
// 流完成回调 → 3秒后刷新标题 |
|||
onStreamComplete: (sid) => { |
|||
setTimeout(() => sessions.refreshSessionTitle(sid), 3000); |
|||
} |
|||
|
|||
// 发送消息 |
|||
async function handleSend(content: string) { |
|||
// 1. 确保有 session |
|||
// 2. touchSessionOrder(sid) — 先更新 lastActiveAt(避免 newSession 复用) |
|||
// 3. await chat.send(content) |
|||
// 4. 若是新 session,refreshTitleWithRetry(sid) |
|||
} |
|||
|
|||
// 新建对话 |
|||
async function handleNewSession() { |
|||
// 若当前 session 为空且非 loading → 不创建(复用) |
|||
// 否则 newSession() |
|||
// 不调用 stopAll() — 其他 session 的流继续运行 |
|||
} |
|||
``` |
|||
|
|||
### 5.4 Auth Routes |
|||
|
|||
文件:`app/utils/auth-routes.ts` |
|||
|
|||
`matchesExactOrPrefix` 增强:支持 `:param` 正则匹配(如 `/chat/:agentSlug`)。 |
|||
|
|||
--- |
|||
|
|||
## 6. 多 Session 并行流式输出 |
|||
|
|||
### 设计 |
|||
|
|||
不同 session 可以**同时**流式输出,新建对话**不会**停止其他 session 的流。 |
|||
|
|||
### 实现原理 |
|||
|
|||
1. **Instance 隔离**:每个 session 在 `useAgentChatStore` 中有独立的 `AgentChatInstance`,包含独立的 `messages` ref 和 `abortController` |
|||
2. **闭包捕获**:`processStream` 操作的是 instance 闭包内的 `messages`,不是 store 的 computed |
|||
3. **切换不中断**:`handleNewSession` 不调用 `stopAll()`,旧 session 的 `processStream` promise 继续在后台执行 |
|||
4. **数据不串**:切换 session 后,`currentInstance` 指向新 instance,旧 instance 的流写入自己的 `messages` ref |
|||
|
|||
### 验证场景 |
|||
|
|||
- 流式输出中点"新对话" → 新 session 创建 → 页面切换到空状态 → 旧 session 流继续 |
|||
- 旧 session 流完成后 → `onStreamComplete(sid)` 触发 → `refreshSessionTitle(sid)` 更新左侧标题 |
|||
|
|||
--- |
|||
|
|||
## 7. 标题生成机制 |
|||
|
|||
### 触发链路 |
|||
|
|||
``` |
|||
用户发送首条消息 |
|||
→ chat-engine.ts onFinish 回调 |
|||
→ generateSessionTitle() (fire-and-forget, .catch()) |
|||
→ 根据 agent.titleStrategy: |
|||
- "none": 跳过 |
|||
- "first-line": 截取首行(≤50字) |
|||
- "llm": 调用 titleModelId 生成 |
|||
→ updateSession(sessionId, { title }) |
|||
``` |
|||
|
|||
### 前端同步 |
|||
|
|||
``` |
|||
onStreamComplete(sid) |
|||
→ setTimeout(3s) → refreshSessionTitle(sid) → GET session → 更新 sessions.value 中的 title |
|||
``` |
|||
|
|||
新 session 额外重试(`refreshTitleWithRetry`):3s / 6s / 10s / 15s / 22s 多次刷新。 |
|||
|
|||
### 切换 session 的影响 |
|||
|
|||
- **服务端**:`onFinish` 在服务端触发,与前端无关,标题会正确写入 DB |
|||
- **前端**:`onStreamComplete` 在 instance 闭包中,切走 session 后仍会执行;`sessions` ref 是同一组件实例的 ref,`refreshSessionTitle` 仍能找到并更新 session |
|||
|
|||
**结论:切换 session 不影响标题生成。** |
|||
|
|||
--- |
|||
|
|||
## 8. 模型解析优先级 |
|||
|
|||
``` |
|||
已登录用户: |
|||
session.modelId ?? preferredModelId ?? agent.defaultModelId |
|||
→ resolveModelForUser(modelId, userId) |
|||
|
|||
未登录用户: |
|||
session.modelId ?? agent.defaultModelId |
|||
→ resolveModelAny(modelId) |
|||
``` |
|||
|
|||
- `preferredModelId`:用户在前端选择的模型(请求 body 传入) |
|||
- `session.modelId`:session 级别持久化的模型选择 |
|||
- `agent.defaultModelId`:agent 配置的默认模型 |
|||
|
|||
--- |
|||
|
|||
## 9. 速率限制 |
|||
|
|||
| 维度 | Key 格式 | 限制 | |
|||
|------|----------|------| |
|||
| Session | `${agentSlug}:${sessionId}` | 20 次 | |
|||
| IP | `${agentSlug}:${ip}` | 50 次/日 | |
|||
|
|||
- 仅对**未登录用户**生效 |
|||
- 按 agentSlug 隔离(不同 agent 独立计数) |
|||
|
|||
--- |
|||
|
|||
## 10. 协作接口预留 |
|||
|
|||
文件:`server/service/agent/collaboration.ts` |
|||
|
|||
```typescript |
|||
export interface AgentInvocation { |
|||
agentSlug: string; |
|||
input: string; |
|||
context?: { sessionId?: string; userId?: number | null }; |
|||
} |
|||
|
|||
export interface AgentInvocationResult { |
|||
agentSlug: string; |
|||
output: string; |
|||
ok: boolean; |
|||
error?: string; |
|||
} |
|||
|
|||
export async function invokeAgent(invocation: AgentInvocation): Promise<AgentInvocationResult> |
|||
``` |
|||
|
|||
当前为 **stub**,仅校验 agent 存在 + `isCallable` 字段,返回 "not yet implemented"。 |
|||
|
|||
`agents.isCallable` 字段控制 agent 是否可被其他 agent 调用。 |
|||
|
|||
--- |
|||
|
|||
## 11. Seed 数据 |
|||
|
|||
文件:`packages/drizzle-pkg/seed.ts` |
|||
|
|||
### 管理员账号 |
|||
|
|||
- 用户名:`admin` |
|||
- 密码:`123456qaz` |
|||
|
|||
### 工具(11 个) |
|||
|
|||
| ID | Slug | 类型 | |
|||
|----|------|------| |
|||
| tool_fetch_html | fetch_html | fetch | |
|||
| tool_fetch_md | fetch_md | fetch | |
|||
| tool_fetch_json | fetch_json | fetch | |
|||
| tool_calculator | calculator | calculator | |
|||
| tool_datetime | datetime | datetime | |
|||
| tool_uuid | uuid | uuid | |
|||
| tool_base64 | base64 | base64 | |
|||
| tool_json_formatter | json_formatter | json-formatter | |
|||
| tool_regex_tester | regex_tester | regex-tester | |
|||
| tool_user_info | user_info | user-info | |
|||
| tool_search | search | search | |
|||
|
|||
### Agent(2 个) |
|||
|
|||
| Slug | Name | 工具数 | 特性 | |
|||
|------|------|--------|------| |
|||
| `default` | 默认助手 | 11(全部) | enableThinking=0, maxStepCount=6, isDefault=1, titleStrategy=llm | |
|||
| `coder` | 编程助手 | 3(fetch_md, search, regex_tester) | enableThinking=1, maxStepCount=12, isCallable=1, titleStrategy=first-line | |
|||
|
|||
--- |
|||
|
|||
## 12. 已知问题与后续工作 |
|||
|
|||
### 已完成 |
|||
|
|||
- [x] DB schema 改造 + seed + 清库重建 |
|||
- [x] Service 层全部改造 |
|||
- [x] API 端点创建 + 旧目录删除 |
|||
- [x] 前端路由迁移 + composables 改造 |
|||
- [x] default + coder agent 端到端验证 |
|||
- [x] "输出时新建对话" bug 修复 |
|||
- [x] auth-routes `:param` 匹配修复 |
|||
- [x] fetch 工具 config 解析修复(zod safeParse + fallback) |
|||
|
|||
### 待验证 |
|||
|
|||
- [ ] coder agent 端到端测试(工具调用、思考模式) |
|||
- [ ] 跨 agent session 访问拒绝(已验证基本逻辑) |
|||
- [ ] 多 session 并行流式输出压力测试 |
|||
|
|||
### 后续工作 |
|||
|
|||
- [ ] Agent 管理后台 UI(CRUD 界面) |
|||
- [ ] 协作接口实现(`invokeAgent` 编排逻辑) |
|||
- [ ] Agent 级别模型配置 UI |
|||
- [ ] 更多 agent 类型(如 researcher、translator) |
|||
Loading…
Reference in new issue