feat: DeepSeek v4 模型对齐、作业助手脚本与 Agent 对比测试
- 前端 WorkflowEditor/ModelConfigs/NodeTemplates:deepseek-v4-flash、v4-pro,弃用提示 - llm_service 默认 deepseek-v4-flash;workflow_engine 等与模型配置注入 - 作业管理脚本支持 AGENT_NAME 与 v4-pro;新增 compare_homework_agents 脚本 - 文档重命名为 (红头)项目核心文档汇总.md 并更新 DeepSeek 说明 Made-with: Cursor
This commit is contained in:
@@ -121,7 +121,7 @@
|
||||
- **工具节点**: 定时任务、Agent节点
|
||||
|
||||
### 5. 内置工具调用
|
||||
平台提供8个内置工具,可在LLM节点中启用:
|
||||
平台提供10个内置工具,可在LLM节点中启用:
|
||||
1. 🌐 **http_request** - HTTP请求工具
|
||||
2. 📖 **file_read** - 文件读取工具
|
||||
3. ✍️ **file_write** - 文件写入工具
|
||||
@@ -130,6 +130,8 @@
|
||||
6. 🔢 **math_calculate** - 数学计算工具
|
||||
7. 💻 **system_info** - 系统信息工具
|
||||
8. 📦 **json_process** - JSON处理工具
|
||||
9. 🗄️ **database_query** - 数据库查询工具(只读查询场景)
|
||||
10. 📱 **adb_log** - Android/设备日志工具
|
||||
|
||||
### 6. 执行管理
|
||||
- 创建执行任务
|
||||
@@ -145,14 +147,26 @@
|
||||
- 数据查询功能
|
||||
|
||||
### 8. 模型配置管理
|
||||
- 模型配置CRUD API
|
||||
- 多模型支持(OpenAI、DeepSeek等)
|
||||
- 模型配置CRUD API(API Key 等在服务端加密存储;工作流 LLM 节点可绑定 `model_config_id` 由可信所有者运行时解密注入)
|
||||
- 多模型支持(OpenAI、Anthropic 兼容路径、DeepSeek 等)
|
||||
- 模型切换和配置
|
||||
|
||||
**DeepSeek API(OpenAI 兼容格式)**
|
||||
与 [DeepSeek API 文档](https://api-docs.deepseek.com/) 对齐的常用参数如下(密钥需在控制台申请,勿写入文档或仓库):
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| **base_url** | `https://api.deepseek.com`(OpenAI 兼容;Anthropic 兼容格式为 `https://api.deepseek.com/anthropic`) |
|
||||
| **主推模型** | `deepseek-v4-flash`、`deepseek-v4-pro` |
|
||||
| **兼容旧名(计划弃用)** | `deepseek-chat`、`deepseek-reasoner` 计划于 **2026/07/24** 弃用;二者分别对应 `deepseek-v4-flash` 的非思考与思考模式 |
|
||||
|
||||
本平台侧:**新建 LLM 节点、节点模板及后端未显式指定模型时的 DeepSeek 默认值**为 **`deepseek-v4-flash`**;工作流编辑器与「模型配置」页下拉仍可选择兼容旧模型名并标注弃用时间。
|
||||
|
||||
### 9. Agent管理
|
||||
- Agent CRUD API
|
||||
- Agent管理页面
|
||||
- Agent协作功能
|
||||
- 批量场景Agent脚本(教育/企业/政务/媒体)
|
||||
|
||||
## 项目结构
|
||||
|
||||
@@ -255,7 +269,8 @@ pnpm dev
|
||||
```
|
||||
|
||||
### 服务访问地址
|
||||
- **前端**: http://localhost:8038
|
||||
- **前端(本地开发推荐)**: http://localhost:3001
|
||||
- **前端(Docker方案)**: http://localhost:8038
|
||||
- **后端API**: http://localhost:8037
|
||||
- **API文档**: http://localhost:8037/docs
|
||||
- **Redis**: localhost:6379
|
||||
@@ -305,7 +320,7 @@ pnpm dev
|
||||
### 节点类型说明
|
||||
- **开始节点**:工作流起始节点
|
||||
- **输入节点**:数据输入节点,可配置输入参数
|
||||
- **LLM节点**:AI模型处理节点,支持OpenAI、DeepSeek等模型
|
||||
- **LLM节点**:AI 模型处理节点,支持 OpenAI、DeepSeek 等;DeepSeek 推荐选用 **`deepseek-v4-flash`** 或 **`deepseek-v4-pro`**(旧名 `deepseek-chat` / `deepseek-reasoner` 仍可兼容使用,计划 **2026/07/24** 弃用)
|
||||
- **条件节点**:条件判断节点,支持表达式配置
|
||||
- **转换节点**:数据转换节点,支持JSONPath、模板等
|
||||
- **输出节点**:数据输出节点,可配置输出格式
|
||||
@@ -352,10 +367,9 @@ alembic downgrade -1
|
||||
```
|
||||
|
||||
### 添加新节点类型
|
||||
1. 后端:在 `backend/app/core/nodes/` 中添加节点执行器
|
||||
2. 后端:在 `backend/app/core/workflow_engine.py` 中注册节点类型
|
||||
3. 前端:在 `frontend/src/components/nodes/` 中添加节点组件
|
||||
4. 前端:在 `frontend/src/stores/workflowStore.ts` 中注册节点类型
|
||||
1. 后端:在 `backend/app/services/workflow_engine.py` 的 `execute_node` 中增加节点类型分支;如需校验,可扩展 `workflow_validator.py`
|
||||
2. 前端:在 `frontend/src/components/nodes/`(或工作流编辑器关联组件)中增加节点展示与配置
|
||||
3. 前端:在 `frontend/src/stores/workflowStore.ts` 等处注册节点类型与默认数据
|
||||
|
||||
## 测试指南
|
||||
|
||||
@@ -376,7 +390,7 @@ alembic downgrade -1
|
||||
|
||||
## 内置工具列表
|
||||
|
||||
平台提供8个内置工具,详细功能如下:
|
||||
平台提供10个内置工具,详细功能如下:
|
||||
|
||||
| 工具名称 | 功能描述 | 主要参数 |
|
||||
|---------|---------|---------|
|
||||
@@ -388,6 +402,10 @@ alembic downgrade -1
|
||||
| math_calculate | 执行数学计算 | expression |
|
||||
| system_info | 获取系统信息 | 无 |
|
||||
| json_process | 处理JSON数据 | json_string, operation |
|
||||
| database_query | 只读数据库查询 | datasource/query(按工具schema) |
|
||||
| adb_log | 设备日志读取 | command/serial/lines(按工具schema) |
|
||||
|
||||
> 说明:`http_request` 已加入响应体截断与头部精简机制(支持 `max_body_chars`),用于避免大页面导致 LLM 上下文超限。
|
||||
|
||||
详细使用示例请参考:[内置工具列表.md](./内置工具列表.md)
|
||||
|
||||
@@ -406,9 +424,11 @@ alembic downgrade -1
|
||||
3. **工作流执行引擎** - DAG构建、拓扑排序、节点执行
|
||||
4. **可视化编辑器** - 拖拽节点、连线、配置面板
|
||||
5. **异步任务处理** - Celery集成,支持长时间运行的任务
|
||||
6. **多模型支持** - OpenAI、DeepSeek集成
|
||||
7. **内置工具调用** - 8个内置工具支持
|
||||
6. **多模型支持** - OpenAI、DeepSeek 等(DeepSeek 主推 `deepseek-v4-flash` / `deepseek-v4-pro`)
|
||||
7. **内置工具调用** - 10 个内置工具支持
|
||||
8. **实时状态推送** - WebSocket实时推送执行状态
|
||||
9. **批量Agent场景生成** - 教育与政务/媒体场景批量创建脚本
|
||||
10. **Windows运维文档统一** - 启停/重启流程合并为单一权威文档
|
||||
|
||||
### 近期开发重点(高优先级)
|
||||
1. **监控和告警前端界面** - 系统监控面板、告警规则管理
|
||||
@@ -451,12 +471,21 @@ alembic downgrade -1
|
||||
## 联系和支持
|
||||
|
||||
- **API文档**:http://localhost:8037/docs
|
||||
- **前端服务**:http://localhost:8038
|
||||
- **前端服务(本地开发)**:http://localhost:3001
|
||||
- **前端服务(Docker)**:http://localhost:8038
|
||||
- **问题反馈**:查看项目文档或联系开发团队
|
||||
|
||||
## 关键文档索引(建议)
|
||||
|
||||
- Windows 启停唯一文档:[`(红头)Windows服务器启动与重启唯一指南.md`](./(红头)Windows服务器启动与重启唯一指南.md)
|
||||
- 上传图片与OCR实现:[`(红头)上传图片和识别的实现文档.md`](./(红头)上传图片和识别的实现文档.md)
|
||||
- 教育行业批量Agent脚本:`backend/scripts/create_education_agents_batch.py`
|
||||
- 政务/媒体批量Agent脚本:`backend/scripts/create_gov_media_agents_batch.py`
|
||||
- 企业场景批量Agent脚本:`backend/scripts/create_enterprise_scenario_agents.py`
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2026-04-06
|
||||
**文档版本**: 1.0
|
||||
**最后更新**: 2026-04-30
|
||||
**文档版本**: 1.2
|
||||
|
||||
*本文档基于项目现有文档整理生成,涵盖项目核心信息。详细技术方案请参考[方案-优化版.md](./方案-优化版.md)。*
|
||||
*本文档基于项目现有文档整理生成,涵盖项目核心信息。详细技术方案请参考[方案-优化版.md](./方案-优化版.md)。DeepSeek 模型名与 Base URL 以官方文档为准,变更时请同步修订本节。*
|
||||
@@ -27,7 +27,7 @@ class NodeTemplateCreate(BaseModel):
|
||||
prompt: str = Field(..., min_length=1, description="提示词模板")
|
||||
variables: Optional[List[Dict[str, Any]]] = Field(None, description="变量定义列表")
|
||||
provider: Optional[str] = Field("deepseek", description="默认LLM提供商")
|
||||
model: Optional[str] = Field("deepseek-chat", description="默认模型")
|
||||
model: Optional[str] = Field("deepseek-v4-flash", description="默认模型")
|
||||
temperature: Optional[str] = Field("0.7", description="默认温度参数")
|
||||
max_tokens: Optional[int] = Field(1500, description="默认最大token数")
|
||||
is_public: Optional[bool] = Field(False, description="是否公开")
|
||||
@@ -176,7 +176,7 @@ async def create_node_template(
|
||||
prompt=template_data.prompt,
|
||||
variables=template_data.variables or [],
|
||||
provider=template_data.provider or "deepseek",
|
||||
model=template_data.model or "deepseek-chat",
|
||||
model=template_data.model or "deepseek-v4-flash",
|
||||
temperature=template_data.temperature or "0.7",
|
||||
max_tokens=template_data.max_tokens or 1500,
|
||||
is_public=template_data.is_public or False,
|
||||
|
||||
@@ -60,7 +60,12 @@ async def test_node(
|
||||
"edges": []
|
||||
}
|
||||
|
||||
engine = WorkflowEngine("test-node", workflow_data, db=db)
|
||||
engine = WorkflowEngine(
|
||||
"test-node",
|
||||
workflow_data,
|
||||
db=db,
|
||||
trusted_model_config_user_id=str(current_user.id),
|
||||
)
|
||||
|
||||
# 执行节点
|
||||
result = await engine.execute_node(node, input_data)
|
||||
|
||||
@@ -25,7 +25,7 @@ class NodeTemplate(Base):
|
||||
|
||||
# 默认配置
|
||||
provider = Column(String(50), default="deepseek", comment="默认LLM提供商")
|
||||
model = Column(String(100), default="deepseek-chat", comment="默认模型")
|
||||
model = Column(String(100), default="deepseek-v4-flash", comment="默认模型")
|
||||
temperature = Column(String(10), default="0.7", comment="默认温度参数")
|
||||
max_tokens = Column(Integer, default=1500, comment="默认最大token数")
|
||||
|
||||
|
||||
@@ -480,7 +480,7 @@ class LLMService:
|
||||
async def call_deepseek(
|
||||
self,
|
||||
prompt: str,
|
||||
model: str = "deepseek-chat",
|
||||
model: str = "deepseek-v4-flash",
|
||||
temperature: float = 0.7,
|
||||
max_tokens: Optional[int] = None,
|
||||
api_key: Optional[str] = None,
|
||||
@@ -492,7 +492,7 @@ class LLMService:
|
||||
|
||||
Args:
|
||||
prompt: 提示词
|
||||
model: 模型名称,默认deepseek-chat
|
||||
model: 模型名称,默认 deepseek-v4-flash(deepseek-chat / deepseek-reasoner 将于 2026/07/24 弃用)
|
||||
temperature: 温度参数,默认0.7
|
||||
max_tokens: 最大token数
|
||||
api_key: API密钥(可选,如果不提供则使用默认配置)
|
||||
@@ -610,7 +610,7 @@ class LLMService:
|
||||
elif provider == "deepseek":
|
||||
# 默认模型
|
||||
if not model:
|
||||
model = "deepseek-chat"
|
||||
model = "deepseek-v4-flash"
|
||||
return await self.call_deepseek(
|
||||
prompt=prompt,
|
||||
model=model,
|
||||
@@ -852,7 +852,7 @@ class LLMService:
|
||||
self,
|
||||
prompt: str,
|
||||
tools: List[Dict[str, Any]],
|
||||
model: str = "deepseek-chat",
|
||||
model: str = "deepseek-v4-flash",
|
||||
temperature: float = 0.7,
|
||||
max_tokens: Optional[int] = None,
|
||||
api_key: Optional[str] = None,
|
||||
@@ -928,7 +928,7 @@ class LLMService:
|
||||
)
|
||||
elif provider == "deepseek":
|
||||
if not model:
|
||||
model = "deepseek-chat"
|
||||
model = "deepseek-v4-flash"
|
||||
return await self.call_deepseek_with_tools(
|
||||
prompt=prompt,
|
||||
tools=tools,
|
||||
|
||||
@@ -92,6 +92,7 @@ class WorkflowEngine:
|
||||
logger=None,
|
||||
db=None,
|
||||
budget_limits: Optional[Dict[str, Any]] = None,
|
||||
trusted_model_config_user_id: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
初始化工作流引擎
|
||||
@@ -101,6 +102,7 @@ class WorkflowEngine:
|
||||
workflow_data: 工作流数据(包含nodes和edges)
|
||||
logger: 执行日志记录器(可选)
|
||||
db: 数据库会话(可选,用于Agent节点加载Agent配置)
|
||||
trusted_model_config_user_id: 允许加载「模型配置」解密密钥的用户 ID(通常为当前执行所属 Workflow/Agent 的 owner)
|
||||
"""
|
||||
self.workflow_id = workflow_id
|
||||
self.nodes = {node['id']: node for node in workflow_data.get('nodes', [])}
|
||||
@@ -115,6 +117,7 @@ class WorkflowEngine:
|
||||
self._llm_invocations: int = 0
|
||||
self._tool_calls_used: int = 0
|
||||
self.budget_limits: Dict[str, Any] = dict(budget_limits or {})
|
||||
self.trusted_model_config_user_id: Optional[str] = trusted_model_config_user_id
|
||||
self._cap_steps: int = max(
|
||||
1, int(getattr(settings, "WORKFLOW_MAX_STEPS_PER_RUN", 2000) or 2000)
|
||||
)
|
||||
@@ -178,6 +181,71 @@ class WorkflowEngine:
|
||||
detail=f"已超过工具调用预算({self._cap_tool} 次)",
|
||||
)
|
||||
|
||||
def _resolve_llm_credentials_from_model_config(
|
||||
self, node_data: Dict[str, Any]
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
若节点 data 含 model_config_id,则从数据库加载加密密钥并校验归属;
|
||||
返回 {"api_key","base_url","provider","model"},否则返回 None。
|
||||
"""
|
||||
raw = node_data.get("model_config_id") or node_data.get("modelConfigId")
|
||||
if raw is None or raw == "":
|
||||
return None
|
||||
cfg_id = str(raw).strip()
|
||||
if not cfg_id:
|
||||
return None
|
||||
|
||||
if not self.trusted_model_config_user_id:
|
||||
logger.warning(
|
||||
"LLM 节点配置了 model_config_id=%s,但未绑定 trusted_model_config_user_id,"
|
||||
"将跳过模型配置密钥注入(仍使用节点与环境变量)。",
|
||||
cfg_id,
|
||||
)
|
||||
return None
|
||||
|
||||
from app.models.model_config import ModelConfig
|
||||
from app.services.encryption_service import EncryptionService
|
||||
|
||||
db = self.db or SessionLocal()
|
||||
own_db = self.db is None
|
||||
try:
|
||||
cfg = db.query(ModelConfig).filter(ModelConfig.id == cfg_id).first()
|
||||
if not cfg:
|
||||
raise ValueError(f"模型配置不存在: {cfg_id}")
|
||||
if cfg.user_id != self.trusted_model_config_user_id:
|
||||
raise ValueError("无权使用该模型配置(仅创建者可调用)")
|
||||
|
||||
api_key_plain = EncryptionService.decrypt(cfg.api_key)
|
||||
if not (api_key_plain or "").strip():
|
||||
raise ValueError("模型配置中的 API Key 无效")
|
||||
|
||||
base_url = (cfg.base_url or "").strip() or None
|
||||
raw_prov = (cfg.provider or "").strip().lower()
|
||||
if raw_prov == "local":
|
||||
llm_prov = "openai"
|
||||
elif raw_prov in ("openai", "deepseek"):
|
||||
llm_prov = raw_prov
|
||||
elif raw_prov == "anthropic":
|
||||
raise ValueError(
|
||||
"当前 LLM 节点暂不支持从模型配置加载 Anthropic;请改用 OpenAI 或 DeepSeek 兼容配置"
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"不支持的模型配置提供商: {cfg.provider}")
|
||||
|
||||
model_name = (cfg.model_name or "").strip()
|
||||
if not model_name:
|
||||
raise ValueError("模型配置中模型名称为空")
|
||||
|
||||
return {
|
||||
"api_key": api_key_plain.strip(),
|
||||
"base_url": base_url,
|
||||
"provider": llm_prov,
|
||||
"model": model_name,
|
||||
}
|
||||
finally:
|
||||
if own_db:
|
||||
db.close()
|
||||
|
||||
def _get_persist_scope(self) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""(scope_kind, scope_id) 或 (None, None),用于持久化 user_memory_*。"""
|
||||
if self._persist_scope_cache is None:
|
||||
@@ -1625,9 +1693,33 @@ class WorkflowEngine:
|
||||
max_tokens = int(max_tokens_raw) if max_tokens_raw is not None else None
|
||||
else:
|
||||
max_tokens = None
|
||||
# 不传递 api_key 和 base_url,让 LLM 服务使用系统默认配置(与节点测试保持一致)
|
||||
api_key = None
|
||||
base_url = None
|
||||
# 默认使用环境变量中的 Key;若节点绑定 model_config_id 且执行上下文可信,则注入用户保存的密钥与 endpoint
|
||||
api_key: Optional[str] = None
|
||||
base_url: Optional[str] = None
|
||||
mc_cred: Optional[Dict[str, Any]] = None
|
||||
try:
|
||||
mc_cred = self._resolve_llm_credentials_from_model_config(node_data)
|
||||
if mc_cred:
|
||||
api_key = mc_cred.get("api_key")
|
||||
base_url = mc_cred.get("base_url")
|
||||
provider = mc_cred.get("provider", provider)
|
||||
model = mc_cred.get("model", model)
|
||||
except Exception as mc_err:
|
||||
if self.logger:
|
||||
duration = int((time.time() - start_time) * 1000)
|
||||
self.logger.log_node_error(node_id, node_type, mc_err, duration)
|
||||
logger.error(f"[rjb] LLM 模型配置解析失败: {mc_err}", exc_info=True)
|
||||
return {
|
||||
'output': None,
|
||||
'status': 'failed',
|
||||
'error': str(mc_err),
|
||||
}
|
||||
|
||||
llm_extra_kw: Dict[str, Any] = {}
|
||||
if api_key is not None:
|
||||
llm_extra_kw["api_key"] = api_key
|
||||
if base_url is not None:
|
||||
llm_extra_kw["base_url"] = base_url
|
||||
|
||||
# 记录实际发送给LLM的prompt
|
||||
logger.info(f"[rjb] 准备调用LLM: node_id={node_id}, provider={provider}, model={model}, prompt前200字符='{prompt[:200] if len(prompt) > 200 else prompt}'")
|
||||
@@ -1660,7 +1752,10 @@ class WorkflowEngine:
|
||||
# 调用LLM服务
|
||||
try:
|
||||
if self.logger:
|
||||
logger.debug(f"[rjb] LLM节点配置: provider={provider}, model={model}, 使用系统默认API Key配置, 工具调用: {'启用' if tools else '禁用'}")
|
||||
key_src = "模型配置(model_config_id)" if mc_cred else "环境变量默认"
|
||||
logger.debug(
|
||||
f"[rjb] LLM节点配置: provider={provider}, model={model}, API密钥来源={key_src}, 工具调用: {'启用' if tools else '禁用'}"
|
||||
)
|
||||
self.logger.info(f"调用LLM服务: {provider}/{model}", node_id=node_id, node_type=node_type)
|
||||
|
||||
# 根据是否启用工具选择不同的调用方式
|
||||
@@ -1684,6 +1779,8 @@ class WorkflowEngine:
|
||||
_tool_extra["request_timeout"] = max(10.0, float(_rt))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
_merged_tool_kw = dict(llm_extra_kw)
|
||||
_merged_tool_kw.update(_tool_extra)
|
||||
result = await llm_service.call_llm_with_tools(
|
||||
prompt=prompt,
|
||||
tools=tools,
|
||||
@@ -1694,7 +1791,7 @@ class WorkflowEngine:
|
||||
execution_logger=self.logger,
|
||||
tool_choice=_tool_choice,
|
||||
on_tool_executed=self._on_tool_executed_budget,
|
||||
**_tool_extra,
|
||||
**_merged_tool_kw,
|
||||
)
|
||||
result = self._enrich_llm_json_user_profile(result, input_data)
|
||||
else:
|
||||
@@ -1703,8 +1800,8 @@ class WorkflowEngine:
|
||||
provider=provider,
|
||||
model=model,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens
|
||||
# 不传递 api_key 和 base_url,使用系统默认配置
|
||||
max_tokens=max_tokens,
|
||||
**llm_extra_kw,
|
||||
)
|
||||
result = self._enrich_llm_json_user_profile(result, input_data)
|
||||
|
||||
@@ -4786,6 +4883,7 @@ class WorkflowEngine:
|
||||
logger=child_logger,
|
||||
db=self.db,
|
||||
budget_limits=child_budget,
|
||||
trusted_model_config_user_id=self.trusted_model_config_user_id,
|
||||
)
|
||||
try:
|
||||
child_result = await child_engine.execute(sub_input)
|
||||
|
||||
@@ -41,6 +41,19 @@ def _snapshot_to_jsonable(snapshot: dict) -> dict:
|
||||
return _json.loads(_json.dumps(snapshot, default=str))
|
||||
|
||||
|
||||
def _trusted_user_for_execution(db, execution: Optional[Execution]) -> Optional[str]:
|
||||
"""用于校验 LLM 节点引用的 model_configs 归属(与 Workflow / Agent 所有者一致)。"""
|
||||
if not execution:
|
||||
return None
|
||||
if execution.agent_id:
|
||||
ag = db.query(Agent).filter(Agent.id == execution.agent_id).first()
|
||||
return ag.user_id if ag else None
|
||||
if execution.workflow_id:
|
||||
wf = db.query(Workflow).filter(Workflow.id == execution.workflow_id).first()
|
||||
return wf.user_id if wf else None
|
||||
return None
|
||||
|
||||
|
||||
@celery_app.task(bind=True)
|
||||
def execute_workflow_task(
|
||||
self,
|
||||
@@ -80,12 +93,14 @@ def execute_workflow_task(
|
||||
|
||||
# 创建工作流引擎(传入logger、db、合并后的执行预算)
|
||||
budget = merge_budget_for_execution(db, execution) if execution else {}
|
||||
trusted_uid = _trusted_user_for_execution(db, execution)
|
||||
engine = WorkflowEngine(
|
||||
workflow_id,
|
||||
workflow_data,
|
||||
logger=execution_logger,
|
||||
db=db,
|
||||
budget_limits=budget,
|
||||
trusted_model_config_user_id=trusted_uid,
|
||||
)
|
||||
|
||||
max_retries = max(0, int(getattr(settings, "WORKFLOW_TASK_MAX_RETRIES", 0) or 0))
|
||||
@@ -269,12 +284,14 @@ def resume_workflow_task(
|
||||
self.update_state(state="PROGRESS", meta={"progress": 0, "status": "running"})
|
||||
|
||||
budget = merge_budget_for_execution(db, execution) if execution else {}
|
||||
trusted_uid = _trusted_user_for_execution(db, execution)
|
||||
engine = WorkflowEngine(
|
||||
wf_key,
|
||||
workflow_data,
|
||||
logger=execution_logger,
|
||||
db=db,
|
||||
budget_limits=budget,
|
||||
trusted_model_config_user_id=trusted_uid,
|
||||
)
|
||||
max_retries = max(0, int(getattr(settings, "WORKFLOW_TASK_MAX_RETRIES", 0) or 0))
|
||||
result: Optional[dict] = None
|
||||
|
||||
156
backend/scripts/compare_homework_agents.py
Normal file
156
backend/scripts/compare_homework_agents.py
Normal file
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
对比「学生作业管理助手」与「学生作业管理助手2号」在相同输入下的节点测试结果。
|
||||
调用 POST /api/v1/nodes/test(同步),需后端运行且 DEEPSEEK_API_KEY 有效。
|
||||
|
||||
用法: cd backend && .\\venv\\Scripts\\python.exe scripts/compare_homework_agents.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import requests
|
||||
|
||||
BACKEND = os.getenv("PLATFORM_BASE_URL", "http://127.0.0.1:8037").rstrip("/")
|
||||
USER = os.getenv("PLATFORM_USERNAME", "admin")
|
||||
PWD = os.getenv("PLATFORM_PASSWORD", "123456")
|
||||
|
||||
NAMES = ["学生作业管理助手", "学生作业管理助手2号"]
|
||||
|
||||
TEST_QUERY = (
|
||||
"帮我记一项作业:语文摘抄名著段落3处并批注,截止周五下午5点前。"
|
||||
"请只回复一条简短清单(科目、要点、截止时间),不要超过120字。"
|
||||
)
|
||||
|
||||
|
||||
def _login() -> str:
|
||||
r = requests.post(
|
||||
f"{BACKEND}/api/v1/auth/login",
|
||||
data={"username": USER, "password": PWD},
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
timeout=15,
|
||||
)
|
||||
r.raise_for_status()
|
||||
t = r.json().get("access_token")
|
||||
if not t:
|
||||
raise RuntimeError("无 access_token")
|
||||
return t
|
||||
|
||||
|
||||
def _find_llm_node(wf: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
for n in wf.get("nodes") or []:
|
||||
if n.get("type") == "llm":
|
||||
return n
|
||||
return None
|
||||
|
||||
|
||||
def _get_agents(h: Dict[str, str]) -> List[Dict[str, Any]]:
|
||||
r = requests.get(
|
||||
f"{BACKEND}/api/v1/agents",
|
||||
params={"search": "学生作业管理", "limit": 50},
|
||||
headers=h,
|
||||
timeout=30,
|
||||
)
|
||||
r.raise_for_status()
|
||||
return list(r.json() or [])
|
||||
|
||||
|
||||
def _fetch_agent_detail(h: Dict[str, str], aid: str) -> Dict[str, Any]:
|
||||
r = requests.get(f"{BACKEND}/api/v1/agents/{aid}", headers=h, timeout=30)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
def _test_node(h: Dict[str, str], node: Dict[str, Any]):
|
||||
body = {"node": node, "input_data": {"query": TEST_QUERY}}
|
||||
t0 = time.perf_counter()
|
||||
r = requests.post(
|
||||
f"{BACKEND}/api/v1/nodes/test",
|
||||
headers=h,
|
||||
json=body,
|
||||
timeout=240,
|
||||
)
|
||||
elapsed_ms = int((time.perf_counter() - t0) * 1000)
|
||||
if r.status_code != 200:
|
||||
return elapsed_ms, "", f"HTTP {r.status_code}: {r.text[:800]}", None
|
||||
data = r.json()
|
||||
out = data.get("output")
|
||||
if isinstance(out, dict):
|
||||
text = out.get("output") or out.get("text") or json.dumps(out, ensure_ascii=False)
|
||||
else:
|
||||
text = str(out)
|
||||
err = data.get("error_message")
|
||||
status = data.get("status")
|
||||
return elapsed_ms, text[:4000], err, status
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
token = _login()
|
||||
except Exception as e:
|
||||
print("登录失败:", e, file=sys.stderr)
|
||||
return 1
|
||||
h = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
||||
|
||||
agents = _get_agents(h)
|
||||
by_name = {a.get("name"): a for a in agents}
|
||||
|
||||
results = []
|
||||
for name in NAMES:
|
||||
a = by_name.get(name)
|
||||
if not a:
|
||||
results.append(
|
||||
{"name": name, "error": f"未找到名为「{name}」的 Agent"}
|
||||
)
|
||||
continue
|
||||
detail = _fetch_agent_detail(h, a["id"])
|
||||
wf = detail.get("workflow_config") or {}
|
||||
node = _find_llm_node(wf)
|
||||
if not node:
|
||||
results.append({"name": name, "id": a["id"], "error": "工作流中无 LLM 节点"})
|
||||
continue
|
||||
data = node.get("data") or {}
|
||||
elapsed_ms, text, err, st = _test_node(h, node)
|
||||
results.append(
|
||||
{
|
||||
"name": name,
|
||||
"id": a["id"],
|
||||
"provider": data.get("provider"),
|
||||
"model": data.get("model"),
|
||||
"elapsed_ms": elapsed_ms,
|
||||
"status": st,
|
||||
"api_error": err,
|
||||
"output_excerpt": text[:2000],
|
||||
}
|
||||
)
|
||||
|
||||
print("=== 对比测试(同步节点测试 API)===")
|
||||
print("输入:", TEST_QUERY)
|
||||
print()
|
||||
for r in results:
|
||||
print(f"【{r['name']}】")
|
||||
if r.get("error"):
|
||||
print(" ", r["error"])
|
||||
print()
|
||||
continue
|
||||
print(f" id: {r['id']}")
|
||||
print(f" provider/model: {r.get('provider')} / {r.get('model')}")
|
||||
print(f" 耗时: {r['elapsed_ms']} ms status: {r.get('status')}")
|
||||
if r.get("api_error"):
|
||||
print(f" error_message: {r['api_error']}")
|
||||
print(f" 输出节选:\n{r.get('output_excerpt', '')}\n")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
环境变量:
|
||||
PLATFORM_BASE_URL, PLATFORM_USERNAME, PLATFORM_PASSWORD
|
||||
AGENT_NAME(默认 学生作业管理助手)
|
||||
AGENT_NAME(默认 学生作业管理助手);示例:`AGENT_NAME=学生作业管理助手2号 HOMEWORK_LLM_MODEL=deepseek-v4-pro`
|
||||
HOMEWORK_LLM_PROVIDER / HOMEWORK_LLM_MODEL / HOMEWORK_LLM_TIMEOUT(可选)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -33,7 +33,7 @@ PROVIDER = os.getenv(
|
||||
"HOMEWORK_LLM_PROVIDER", os.getenv("ENTERPRISE_LLM_PROVIDER", "deepseek")
|
||||
)
|
||||
MODEL = os.getenv(
|
||||
"HOMEWORK_LLM_MODEL", os.getenv("ENTERPRISE_LLM_MODEL", "deepseek-chat")
|
||||
"HOMEWORK_LLM_MODEL", os.getenv("ENTERPRISE_LLM_MODEL", "deepseek-v4-flash")
|
||||
)
|
||||
REQ_TIMEOUT = max(
|
||||
30,
|
||||
@@ -52,7 +52,9 @@ BUDGET_CONFIG = {
|
||||
|
||||
HOMEWORK_TOOLS = ["file_read", "text_analyze", "datetime", "json_process"]
|
||||
|
||||
HOMEWORK_PROMPT = """你是「学生作业管理助手」,帮助学生**记作业**与**监督完成**,语气友好、具体、可执行。
|
||||
|
||||
def _homework_prompt(agent_display_name: str) -> str:
|
||||
return f"""你是「{agent_display_name}」,帮助学生**记作业**与**监督完成**,语气友好、具体、可执行。
|
||||
|
||||
【核心能力】
|
||||
1. **记作业**:从用户自然语言中提取「科目 / 作业内容 / 截止日期与时间 / 老师要求要点 / 预估耗时」,整理成清单。
|
||||
@@ -104,7 +106,7 @@ def build_workflow() -> Dict[str, Any]:
|
||||
"position": {"x": llm_pos[0], "y": llm_pos[1]},
|
||||
"data": {
|
||||
"label": "作业管理",
|
||||
"prompt": HOMEWORK_PROMPT,
|
||||
"prompt": _homework_prompt(AGENT_NAME),
|
||||
"provider": PROVIDER,
|
||||
"model": MODEL,
|
||||
"temperature": 0.3,
|
||||
@@ -169,7 +171,7 @@ def main() -> int:
|
||||
h = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
||||
|
||||
desc = (
|
||||
"学生作业管理助手:记作业(科目、内容、截止日)、跟进度、温和督促与周回顾;"
|
||||
f"{AGENT_NAME}:记作业(科目、内容、截止日)、跟进度、温和督促与周回顾;"
|
||||
"支持上传文件/照片后用 file_read 提取正文(文本、PDF、docx、xlsx、图片 OCR)与 json_process 整理;"
|
||||
f"默认模型 {PROVIDER}/{MODEL},单次执行内工具迭代上限 10。"
|
||||
)
|
||||
|
||||
@@ -645,6 +645,25 @@
|
||||
<el-option label="DeepSeek" value="deepseek" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="已配置模型">
|
||||
<el-select
|
||||
v-model="selectedNode.data.model_config_id"
|
||||
placeholder="从模型配置中选择(可选)"
|
||||
clearable
|
||||
filterable
|
||||
@change="handleConfiguredModelChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="cfg in availableConfiguredModels"
|
||||
:key="cfg.id"
|
||||
:label="`${cfg.name} (${cfg.provider}/${cfg.model_name})`"
|
||||
:value="cfg.id"
|
||||
/>
|
||||
</el-select>
|
||||
<div style="margin-top: 5px; color: #909399; font-size: 12px;">
|
||||
选择后会回填提供商与模型;保存工作流后,运行时将通过 model_config_id 使用你在「模型配置」中保存的密钥与 API 地址(需与当前 Agent/工作流属主一致)。
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="提示词">
|
||||
<div class="prompt-input-wrapper" style="position: relative;">
|
||||
<el-input
|
||||
@@ -764,8 +783,11 @@
|
||||
<el-option label="GPT-4 Turbo" value="gpt-4-turbo-preview" />
|
||||
</template>
|
||||
<template v-else-if="selectedNode.data.provider === 'deepseek'">
|
||||
<el-option label="DeepSeek Chat" value="deepseek-chat" />
|
||||
<el-option label="DeepSeek V4 Flash(推荐)" value="deepseek-v4-flash" />
|
||||
<el-option label="DeepSeek V4 Pro(推荐)" value="deepseek-v4-pro" />
|
||||
<el-option label="DeepSeek Coder" value="deepseek-coder" />
|
||||
<el-option label="DeepSeek Chat(兼容,计划弃用 2026/07/24)" value="deepseek-chat" />
|
||||
<el-option label="DeepSeek Reasoner(兼容,计划弃用 2026/07/24)" value="deepseek-reasoner" />
|
||||
</template>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -827,6 +849,25 @@
|
||||
<el-option label="DeepSeek" value="deepseek" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="已配置模型">
|
||||
<el-select
|
||||
v-model="selectedNode.data.model_config_id"
|
||||
placeholder="从模型配置中选择(可选)"
|
||||
clearable
|
||||
filterable
|
||||
@change="handleConfiguredModelChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="cfg in availableConfiguredModels"
|
||||
:key="cfg.id"
|
||||
:label="`${cfg.name} (${cfg.provider}/${cfg.model_name})`"
|
||||
:value="cfg.id"
|
||||
/>
|
||||
</el-select>
|
||||
<div style="margin-top: 5px; color: #909399; font-size: 12px;">
|
||||
选择后会回填提供商与模型;保存工作流后,运行时将通过 model_config_id 使用你在「模型配置」中保存的密钥与 API 地址(需与当前 Agent/工作流属主一致)。
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="提示词">
|
||||
<el-input
|
||||
v-model="selectedNode.data.prompt"
|
||||
@@ -846,8 +887,11 @@
|
||||
<el-option label="GPT-4 Turbo" value="gpt-4-turbo-preview" />
|
||||
</template>
|
||||
<template v-else-if="selectedNode.data.provider === 'deepseek'">
|
||||
<el-option label="DeepSeek Chat" value="deepseek-chat" />
|
||||
<el-option label="DeepSeek V4 Flash(推荐)" value="deepseek-v4-flash" />
|
||||
<el-option label="DeepSeek V4 Pro(推荐)" value="deepseek-v4-pro" />
|
||||
<el-option label="DeepSeek Coder" value="deepseek-coder" />
|
||||
<el-option label="DeepSeek Chat(兼容,计划弃用 2026/07/24)" value="deepseek-chat" />
|
||||
<el-option label="DeepSeek Reasoner(兼容,计划弃用 2026/07/24)" value="deepseek-reasoner" />
|
||||
</template>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -2910,6 +2954,14 @@ import { StartNode, LLMNode, ConditionNode, EndNode, DefaultNode } from './NodeT
|
||||
import { useCollaboration } from '@/composables/useCollaboration'
|
||||
import NodeExecutionDetail from './NodeExecutionDetail.vue'
|
||||
|
||||
interface ConfiguredModelItem {
|
||||
id: string
|
||||
name: string
|
||||
provider: string
|
||||
model_name: string
|
||||
base_url?: string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
workflowId?: string
|
||||
agentId?: string
|
||||
@@ -2986,6 +3038,50 @@ const hasUpstreamNodes = computed(() => {
|
||||
// 节点模板相关
|
||||
const nodeTemplates = ref<any[]>([])
|
||||
const loadingTemplates = ref(false)
|
||||
const configuredModels = ref<ConfiguredModelItem[]>([])
|
||||
|
||||
const availableConfiguredModels = computed(() => {
|
||||
const p = selectedNode.value?.data?.provider
|
||||
if (!p) return configuredModels.value
|
||||
return configuredModels.value.filter(cfg => cfg.provider === p)
|
||||
})
|
||||
|
||||
const loadConfiguredModels = async () => {
|
||||
try {
|
||||
const response = await api.get('/api/v1/model-configs', {
|
||||
params: {
|
||||
limit: 100
|
||||
}
|
||||
})
|
||||
configuredModels.value = Array.isArray(response.data) ? response.data : []
|
||||
} catch (error) {
|
||||
console.warn('[WorkflowEditor] 加载模型配置失败:', error)
|
||||
configuredModels.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfiguredModelChange = (configId: string) => {
|
||||
if (!selectedNode.value) return
|
||||
if (!configId) return
|
||||
const cfg = configuredModels.value.find(item => item.id === configId)
|
||||
if (!cfg) return
|
||||
selectedNode.value.data.provider = cfg.provider
|
||||
selectedNode.value.data.model = cfg.model_name
|
||||
}
|
||||
|
||||
watch(
|
||||
() => selectedNode.value?.data?.provider,
|
||||
(newProv) => {
|
||||
const sn = selectedNode.value
|
||||
if (!sn || (sn.type !== 'llm' && sn.type !== 'template')) return
|
||||
const mid = sn.data?.model_config_id as string | undefined
|
||||
if (!mid || !newProv) return
|
||||
const cfg = configuredModels.value.find((c) => c.id === mid)
|
||||
if (cfg && cfg.provider !== newProv) {
|
||||
sn.data.model_config_id = ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 加载节点模板列表
|
||||
const loadNodeTemplates = async () => {
|
||||
@@ -4538,7 +4634,7 @@ const getScenarios = (nodeType: string) => {
|
||||
],
|
||||
config: {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '请总结以下内容,100字以内:{{input}}',
|
||||
temperature: 0.5,
|
||||
max_tokens: 500
|
||||
@@ -4558,7 +4654,7 @@ const getScenarios = (nodeType: string) => {
|
||||
],
|
||||
config: {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '请把下列内容翻译成{{target_lang}}:{{input}}',
|
||||
temperature: 0.3,
|
||||
max_tokens: 1000
|
||||
@@ -4571,7 +4667,7 @@ const getScenarios = (nodeType: string) => {
|
||||
icon: 'DataAnalysis',
|
||||
config: {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '请从以下文本中提取关键信息,以JSON格式返回:{{input}}',
|
||||
temperature: 0.2,
|
||||
max_tokens: 1000
|
||||
@@ -4584,7 +4680,7 @@ const getScenarios = (nodeType: string) => {
|
||||
icon: 'Sort',
|
||||
config: {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '请对以下文本进行分类:{{input}}',
|
||||
temperature: 0.1,
|
||||
max_tokens: 200
|
||||
@@ -4701,7 +4797,7 @@ const configTemplates = ref<Array<{
|
||||
nodeType: 'llm',
|
||||
config: {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '请总结以下内容,100字以内:{{input}}',
|
||||
temperature: 0.5,
|
||||
max_tokens: 500
|
||||
@@ -4715,7 +4811,7 @@ const configTemplates = ref<Array<{
|
||||
nodeType: 'llm',
|
||||
config: {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '请把下列内容翻译成英文:{{input}}',
|
||||
temperature: 0.3,
|
||||
max_tokens: 1000
|
||||
@@ -5138,7 +5234,7 @@ const applyTemplate = () => {
|
||||
if (t === 'llm') {
|
||||
if (templateSelection.value === 'llm_summary') {
|
||||
selectedNode.value.data.provider = selectedNode.value.data.provider || 'deepseek'
|
||||
selectedNode.value.data.model = selectedNode.value.data.model || 'deepseek-chat'
|
||||
selectedNode.value.data.model = selectedNode.value.data.model || 'deepseek-v4-flash'
|
||||
selectedNode.value.data.prompt = '请总结以下内容,100字以内:{text}'
|
||||
selectedNode.value.data.temperature = 0.5
|
||||
} else if (templateSelection.value === 'llm_translate') {
|
||||
@@ -5218,12 +5314,12 @@ const applyTemplate = () => {
|
||||
if (t === 'llm') {
|
||||
if (templateSelection.value === 'llm_extract') {
|
||||
selectedNode.value.data.provider = selectedNode.value.data.provider || 'deepseek'
|
||||
selectedNode.value.data.model = selectedNode.value.data.model || 'deepseek-chat'
|
||||
selectedNode.value.data.model = selectedNode.value.data.model || 'deepseek-v4-flash'
|
||||
selectedNode.value.data.prompt = '请从以下文本中提取关键信息(JSON格式):{text}'
|
||||
selectedNode.value.data.temperature = 0.3
|
||||
} else if (templateSelection.value === 'llm_classify') {
|
||||
selectedNode.value.data.provider = selectedNode.value.data.provider || 'deepseek'
|
||||
selectedNode.value.data.model = selectedNode.value.data.model || 'deepseek-chat'
|
||||
selectedNode.value.data.model = selectedNode.value.data.model || 'deepseek-v4-flash'
|
||||
selectedNode.value.data.prompt = '请将以下内容分类为:正面/中性/负面。内容:{text}'
|
||||
selectedNode.value.data.temperature = 0.2
|
||||
}
|
||||
@@ -5431,7 +5527,7 @@ const handleDrop = (event: DragEvent) => {
|
||||
// LLM节点默认配置
|
||||
...(isLLMNode && !isTemplateNode ? {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '请处理用户请求。',
|
||||
temperature: 0.5,
|
||||
max_tokens: 1500,
|
||||
@@ -5441,7 +5537,7 @@ const handleDrop = (event: DragEvent) => {
|
||||
// 模板节点默认配置
|
||||
...(isTemplateNode ? {
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
prompt: '',
|
||||
temperature: 0.7,
|
||||
max_tokens: 1500,
|
||||
@@ -7757,6 +7853,8 @@ onMounted(async () => {
|
||||
|
||||
// 加载节点模板列表
|
||||
loadNodeTemplates()
|
||||
// 加载模型配置列表(用于LLM节点快速选择已配置模型)
|
||||
loadConfiguredModels()
|
||||
// 加载测试用例
|
||||
loadTestCases()
|
||||
// 加载配置模板
|
||||
|
||||
@@ -108,7 +108,21 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型名称" prop="model_name">
|
||||
<el-input v-model="form.model_name" placeholder="例如: gpt-3.5-turbo, deepseek-chat" />
|
||||
<el-select
|
||||
v-model="form.model_name"
|
||||
placeholder="选择或输入模型名称"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="name in providerModelOptions"
|
||||
:key="name"
|
||||
:label="name"
|
||||
:value="name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="API密钥" prop="api_key">
|
||||
<el-input
|
||||
@@ -164,7 +178,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import MainLayout from '@/components/MainLayout.vue'
|
||||
import {
|
||||
@@ -207,6 +221,20 @@ const testing = ref(false)
|
||||
const testResult = ref<any>(null)
|
||||
const currentTestConfig = ref<ModelConfig | null>(null)
|
||||
|
||||
const providerModelOptions = computed(() => {
|
||||
const provider = form.value.provider
|
||||
if (provider === 'openai') {
|
||||
return ['gpt-4o', 'gpt-4-turbo-preview', 'gpt-4', 'gpt-3.5-turbo']
|
||||
}
|
||||
if (provider === 'deepseek') {
|
||||
return ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner', 'deepseek-coder']
|
||||
}
|
||||
if (provider === 'anthropic') {
|
||||
return ['claude-3-5-sonnet-latest', 'claude-3-opus-20240229']
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
name: [
|
||||
|
||||
@@ -337,7 +337,7 @@ const formData = ref({
|
||||
description: string
|
||||
}>,
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
temperature: 0.7,
|
||||
max_tokens: 1500,
|
||||
is_public: false
|
||||
@@ -414,7 +414,7 @@ const handleCreate = () => {
|
||||
prompt: '',
|
||||
variables: [],
|
||||
provider: 'deepseek',
|
||||
model: 'deepseek-chat',
|
||||
model: 'deepseek-v4-flash',
|
||||
temperature: 0.7,
|
||||
max_tokens: 1500,
|
||||
is_public: false
|
||||
@@ -434,7 +434,7 @@ const handleEdit = (row: any) => {
|
||||
prompt: row.prompt,
|
||||
variables: row.variables ? JSON.parse(JSON.stringify(row.variables)) : [],
|
||||
provider: row.provider || 'deepseek',
|
||||
model: row.model || 'deepseek-chat',
|
||||
model: row.model || 'deepseek-v4-flash',
|
||||
temperature: parseFloat(row.temperature) || 0.7,
|
||||
max_tokens: row.max_tokens || 1500,
|
||||
is_public: row.is_public || false
|
||||
|
||||
Reference in New Issue
Block a user