feat: ai agent部分功能
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { Button, Checkbox, Input, Spin, Tabs, Tag, message } from 'ant-design-vue';
|
||||
import { Button, Checkbox, Input, InputNumber, Select, Spin, Switch, Tabs, Tag, message } from 'ant-design-vue';
|
||||
import { CheckCircleFilled, KeyOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
import {
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
getAiPlatformConfigOptions,
|
||||
saveAiFeatureCopy,
|
||||
} from '#/views/system/ai/platform/api';
|
||||
import { saveSystemConfig } from '#/views/system/system-config/api';
|
||||
import { getSystemConfigList, saveSystemConfig } from '#/views/system/system-config/api';
|
||||
|
||||
/** 二级 Tab 持久化 */
|
||||
const SUB_TAB_STORAGE_KEY = 'system_config_ai_sub_tab';
|
||||
@@ -70,6 +70,36 @@ const mrFieldOptions = ref<{ label: string; value: string }[]>([]);
|
||||
const mrSelectedFields = ref<string[]>([]);
|
||||
const mrSaving = ref(false);
|
||||
|
||||
// ===== Agent 高级配置(走 getSystemConfigList,写入 xk_system_config) =====
|
||||
// 这组配置与上方"模型配置"独立:
|
||||
// - 上方"模型配置"决定用什么 provider/key/model(写入 ai_active_provider/_api_key_id/_model)
|
||||
// - 这里只决定"是否走 Go Agent 中转",用独立开关 ai_agent_via_agent 控制
|
||||
// 这样切换 agent 模式时不会污染模型组合(模型永远在「模型配置 Tab」选)
|
||||
// 分组 0:Agent 接入(独立开关 + 服务地址 + 鉴权密钥)
|
||||
// aiActiveProviderAdv 不再使用——只保留模型 Tab 里的 activeProvider 作为真实 provider
|
||||
const aiAgentViaAgent = ref(false);
|
||||
const aiAgentBaseUrl = ref('http://127.0.0.1:18123');
|
||||
const aiAgentSecret = ref('');
|
||||
const aiAgentTesting = ref(false);
|
||||
// 分组 1:ReAct 多轮循环
|
||||
const aiReactEnabled = ref(false);
|
||||
const aiReactMaxIterations = ref(3);
|
||||
const aiReactPlanningEnabled = ref(false);
|
||||
const aiReactReflectionEnabled = ref(false);
|
||||
const aiReactReflectionTemperature = ref(0.2);
|
||||
const aiReactJsonRepairEnabled = ref(true);
|
||||
// 分组 2:Token 预算
|
||||
const aiTokenBudgetEnabled = ref(true);
|
||||
const aiTokenBudgetPerRequest = ref(8000);
|
||||
const aiTokenMaxPerCall = ref(2048);
|
||||
// 分组 3:流量实验
|
||||
const aiShadowTrafficEnabled = ref(false);
|
||||
const aiShadowTrafficRatio = ref(0);
|
||||
const aiAbTestEnabled = ref(false);
|
||||
const aiAbTestExperimentRatio = ref(10);
|
||||
// 高级配置保存状态
|
||||
const advSaving = ref(false);
|
||||
|
||||
const currentPlatform = computed(() =>
|
||||
platforms.value.find((p) => p.code === activeProvider.value),
|
||||
);
|
||||
@@ -171,6 +201,176 @@ function clearMrFields() {
|
||||
mrSelectedFields.value = [];
|
||||
}
|
||||
|
||||
// ===== Agent 高级配置的辅助函数 =====
|
||||
// 与 PHP SystemConfigService::castValue bool 分支对齐:1/true/yes 视为真
|
||||
function parseBoolAdv(v: unknown): boolean {
|
||||
return v === '1' || v === 1 || v === 'true' || v === true;
|
||||
}
|
||||
function numStr(v: number): string {
|
||||
return String(v);
|
||||
}
|
||||
function boolStr(v: boolean): string {
|
||||
return v ? '1' : '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* 单独从 getSystemConfigList 加载 Agent 高级配置项
|
||||
*
|
||||
* 与上方 load() 走 getAiPlatformConfigOptions 不同:
|
||||
* - 上方模型配置是 PHP AiRuntimeConfigService 单独提供的结构化接口
|
||||
* - 这里走通用 key-value 接口,读 xk_system_config 表里的 16 个 ai_* key
|
||||
* (ai_active_provider / ai_agent_* / ai_react_* / ai_token_* / ai_shadow_* / ai_ab_test_*)
|
||||
*
|
||||
* 不在 load() 里直接合并是为了避免 getAiPlatformConfigOptions 失败时牵连这组配置加载
|
||||
*/
|
||||
async function loadAdvancedCfg() {
|
||||
try {
|
||||
const list = await getSystemConfigList();
|
||||
const rows = Array.isArray(list) ? list : list?.data || [];
|
||||
for (const row of rows) {
|
||||
switch (row.config_key) {
|
||||
case 'ai_agent_via_agent': {
|
||||
// 是否走 Go Agent 中转:1/true/yes 视为开
|
||||
aiAgentViaAgent.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_agent_base_url': {
|
||||
aiAgentBaseUrl.value = String(row.config_value || 'http://127.0.0.1:18123');
|
||||
break;
|
||||
}
|
||||
case 'ai_agent_secret': {
|
||||
aiAgentSecret.value = String(row.config_value || '');
|
||||
break;
|
||||
}
|
||||
case 'ai_react_enabled': {
|
||||
aiReactEnabled.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_react_max_iterations': {
|
||||
aiReactMaxIterations.value = Number(row.config_value) || 3;
|
||||
break;
|
||||
}
|
||||
case 'ai_react_planning_enabled': {
|
||||
aiReactPlanningEnabled.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_react_reflection_enabled': {
|
||||
aiReactReflectionEnabled.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_react_reflection_temperature': {
|
||||
aiReactReflectionTemperature.value = Number(row.config_value) || 0.2;
|
||||
break;
|
||||
}
|
||||
case 'ai_react_json_repair_enabled': {
|
||||
aiReactJsonRepairEnabled.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_token_budget_enabled': {
|
||||
aiTokenBudgetEnabled.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_token_budget_per_request': {
|
||||
aiTokenBudgetPerRequest.value = Number(row.config_value) || 8000;
|
||||
break;
|
||||
}
|
||||
case 'ai_token_max_per_call': {
|
||||
aiTokenMaxPerCall.value = Number(row.config_value) || 2048;
|
||||
break;
|
||||
}
|
||||
case 'ai_shadow_traffic_enabled': {
|
||||
aiShadowTrafficEnabled.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_shadow_traffic_ratio': {
|
||||
aiShadowTrafficRatio.value = Number(row.config_value) || 0;
|
||||
break;
|
||||
}
|
||||
case 'ai_ab_test_enabled': {
|
||||
aiAbTestEnabled.value = parseBoolAdv(row.config_value);
|
||||
break;
|
||||
}
|
||||
case 'ai_ab_test_experiment_ratio': {
|
||||
aiAbTestExperimentRatio.value = Number(row.config_value) || 10;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 高级配置加载失败不阻塞主流程,仅控制台告警
|
||||
console.warn('[AiModelConfigPanel] loadAdvancedCfg failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存 Agent 高级配置(16 个 key 一次性 POST) */
|
||||
async function handleSaveAdvanced() {
|
||||
advSaving.value = true;
|
||||
try {
|
||||
await saveSystemConfig([
|
||||
// 分组 0:Agent 接入
|
||||
// 关键:用 ai_agent_via_agent 独立开关,不再写 ai_active_provider(避免污染模型 Tab 的真实 provider)
|
||||
// 这样后台"模型配置 Tab"选什么 provider/key/model,Go Agent 中转就用什么,互不影响
|
||||
{ config_key: 'ai_agent_via_agent', config_value: boolStr(aiAgentViaAgent.value) },
|
||||
{ config_key: 'ai_agent_base_url', config_value: aiAgentBaseUrl.value },
|
||||
{ config_key: 'ai_agent_secret', config_value: aiAgentSecret.value },
|
||||
// 分组 1:ReAct
|
||||
{ config_key: 'ai_react_enabled', config_value: boolStr(aiReactEnabled.value) },
|
||||
{ config_key: 'ai_react_max_iterations', config_value: numStr(aiReactMaxIterations.value) },
|
||||
{ config_key: 'ai_react_planning_enabled', config_value: boolStr(aiReactPlanningEnabled.value) },
|
||||
{ config_key: 'ai_react_reflection_enabled', config_value: boolStr(aiReactReflectionEnabled.value) },
|
||||
{ config_key: 'ai_react_reflection_temperature', config_value: numStr(aiReactReflectionTemperature.value) },
|
||||
{ config_key: 'ai_react_json_repair_enabled', config_value: boolStr(aiReactJsonRepairEnabled.value) },
|
||||
// 分组 2:Token 预算
|
||||
{ config_key: 'ai_token_budget_enabled', config_value: boolStr(aiTokenBudgetEnabled.value) },
|
||||
{ config_key: 'ai_token_budget_per_request', config_value: numStr(aiTokenBudgetPerRequest.value) },
|
||||
{ config_key: 'ai_token_max_per_call', config_value: numStr(aiTokenMaxPerCall.value) },
|
||||
// 分组 3:流量实验
|
||||
{ config_key: 'ai_shadow_traffic_enabled', config_value: boolStr(aiShadowTrafficEnabled.value) },
|
||||
{ config_key: 'ai_shadow_traffic_ratio', config_value: numStr(aiShadowTrafficRatio.value) },
|
||||
{ config_key: 'ai_ab_test_enabled', config_value: boolStr(aiAbTestEnabled.value) },
|
||||
{ config_key: 'ai_ab_test_experiment_ratio', config_value: numStr(aiAbTestExperimentRatio.value) },
|
||||
]);
|
||||
message.success('Agent 高级配置已保存');
|
||||
} finally {
|
||||
advSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试 Agent 服务可达性(GET {base_url}/health)
|
||||
*
|
||||
* 不入库,仅 UI 反馈:
|
||||
* - Go Agent 健康检查路径 /health 已在 middleware.Auth 白名单内,无需鉴权
|
||||
* - 仅校验"地址可达 + 服务存活",不验证 secret(secret 错误时业务调用会 401)
|
||||
*/
|
||||
async function testAgentConnection() {
|
||||
const url = String(aiAgentBaseUrl.value || '').trim();
|
||||
if (!url) {
|
||||
message.warning('请先填写 Agent 服务地址');
|
||||
return;
|
||||
}
|
||||
aiAgentTesting.value = true;
|
||||
try {
|
||||
const resp = await fetch(`${url.replace(/\/$/, '')}/health`, {
|
||||
method: 'GET',
|
||||
headers: { Accept: 'application/json' },
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
if (resp.ok) {
|
||||
message.success(`Agent 服务可达(${resp.status})`);
|
||||
} else {
|
||||
message.error(`Agent 响应异常:HTTP ${resp.status}`);
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(`连接失败:${e instanceof Error ? e.message : String(e)}`);
|
||||
} finally {
|
||||
aiAgentTesting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存 AI 写病历输出字段勾选 */
|
||||
async function handleSaveMrFields() {
|
||||
if (!mrSelectedFields.value.length) {
|
||||
@@ -277,10 +477,13 @@ async function handleSaveCopies() {
|
||||
|
||||
onMounted(() => {
|
||||
const stored = localStorage.getItem(SUB_TAB_STORAGE_KEY);
|
||||
if (stored === 'model' || stored === 'copy' || stored === 'mr') {
|
||||
// 新增 advanced 二级 Tab 后,缓存值合法性扩展
|
||||
if (stored === 'model' || stored === 'copy' || stored === 'mr' || stored === 'advanced') {
|
||||
subTab.value = stored;
|
||||
}
|
||||
load();
|
||||
// 加载 Agent 高级配置(独立接口,失败不阻塞主流程)
|
||||
loadAdvancedCfg();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -518,6 +721,185 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<!-- Agent 高级:Go Agent 接入 / ReAct / Token 预算 / 流量实验 -->
|
||||
<Tabs.TabPane key="advanced" tab="Agent 高级">
|
||||
<div class="py-4 space-y-6">
|
||||
<!-- 分组 0:Agent 接入配置(独立开关 + 服务地址 + 鉴权密钥) -->
|
||||
<div class="rounded border border-solid border-blue-200 bg-blue-50/30 p-4 dark:border-blue-900 dark:bg-blue-950/20">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div>
|
||||
<div class="font-medium text-[hsl(var(--foreground))]">Agent 接入配置</div>
|
||||
<div class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
独立开关:开启后所有 AI 调用走 Go Agent 中转(含本地知识库检索 / ReAct 多轮),模型/密钥/平台始终由上方「模型配置」Tab 决定
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
v-model:checked="aiAgentViaAgent"
|
||||
checked-children="中转"
|
||||
un-checked-children="直连"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">
|
||||
Agent 服务地址
|
||||
<Tag v-if="aiAgentViaAgent" color="processing" class="ml-1">必填</Tag>
|
||||
</div>
|
||||
<Input
|
||||
v-model:value="aiAgentBaseUrl"
|
||||
placeholder="http://127.0.0.1:18123"
|
||||
:disabled="!aiAgentViaAgent"
|
||||
/>
|
||||
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
|
||||
仅当上方开关开启时生效;与 Go 端 config.yaml 的 server.port 对应
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">
|
||||
当前生效的模型组合
|
||||
<Tag color="blue" class="ml-1">{{ activeProvider || '未选择' }}</Tag>
|
||||
</div>
|
||||
<div class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
模型/密钥/平台由「模型配置」Tab 决定,Go Agent 中转会原样使用,
|
||||
切换模型请前往<a class="ml-1 underline" @click="subTab = 'model'">模型配置 Tab</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">
|
||||
Agent 鉴权密钥
|
||||
<span class="ml-1 text-xs text-[hsl(var(--muted-foreground))]">
|
||||
(与 Go 端 config.Agent.SharedSecret 对应;留空则不发 Authorization)
|
||||
</span>
|
||||
</div>
|
||||
<Input.Password
|
||||
v-model:value="aiAgentSecret"
|
||||
placeholder="如 qiqi991012"
|
||||
:disabled="!aiAgentViaAgent"
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3 flex flex-wrap items-center gap-3">
|
||||
<Button :loading="aiAgentTesting" :disabled="!aiAgentViaAgent" @click="testAgentConnection">
|
||||
测试连接
|
||||
</Button>
|
||||
<span class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
仅校验服务可达(GET /health),不验证密钥;密钥错误会在实际业务调用时报 401
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分组 1:ReAct 多轮推理循环 -->
|
||||
<div class="rounded border border-solid border-[hsl(var(--border))] p-4">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div>
|
||||
<div class="font-medium text-[hsl(var(--foreground))]">ReAct 多轮推理循环</div>
|
||||
<div class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
开启后单次任务会走 Think-Act-Observe 多轮(含 Planning / Reflection / JSON 修复),质量更好但延迟与 token 成本更高
|
||||
</div>
|
||||
</div>
|
||||
<Switch v-model:checked="aiReactEnabled" checked-children="开" un-checked-children="关" />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">单任务最大轮数</div>
|
||||
<InputNumber v-model:value="aiReactMaxIterations" :min="1" :max="10" style="width: 100%" />
|
||||
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">建议 2-5,过大会显著拉高延迟和成本</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">反思步骤温度</div>
|
||||
<InputNumber
|
||||
v-model:value="aiReactReflectionTemperature"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:step="0.05"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">低于生成温度(0.3),保证审核结果稳定</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex flex-wrap items-center gap-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-[hsl(var(--foreground))]">Planning 规划阶段</span>
|
||||
<Switch v-model:checked="aiReactPlanningEnabled" checked-children="开" un-checked-children="关" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-[hsl(var(--foreground))]">Reflection 反思自检</span>
|
||||
<Switch v-model:checked="aiReactReflectionEnabled" checked-children="开" un-checked-children="关" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-[hsl(var(--foreground))]">JSON 自动修复</span>
|
||||
<Switch v-model:checked="aiReactJsonRepairEnabled" checked-children="开" un-checked-children="关" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分组 2:Token 预算管理 -->
|
||||
<div class="rounded border border-solid border-[hsl(var(--border))] p-4">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div>
|
||||
<div class="font-medium text-[hsl(var(--foreground))]">Token 预算管理</div>
|
||||
<div class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
开启后单次任务累计 token 超限即中止,防 Agent 死循环烧钱(双层控制:单任务总预算 + 单次调用上限)
|
||||
</div>
|
||||
</div>
|
||||
<Switch v-model:checked="aiTokenBudgetEnabled" checked-children="开" un-checked-children="关" />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">单任务总 token 上限</div>
|
||||
<InputNumber v-model:value="aiTokenBudgetPerRequest" :min="1000" :step="1000" style="width: 100%" />
|
||||
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">prompt + completion 累加;8000 约等于 1-2 万汉字</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">单次 LLM 调用输出上限</div>
|
||||
<InputNumber v-model:value="aiTokenMaxPerCall" :min="256" :step="256" style="width: 100%" />
|
||||
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">透传给厂商的 max_tokens 字段,防止单次响应爆炸</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分组 3:流量实验 -->
|
||||
<div class="rounded border border-solid border-[hsl(var(--border))] p-4">
|
||||
<div class="mb-3 font-medium text-[hsl(var(--foreground))]">流量实验</div>
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm text-[hsl(var(--foreground))]">影子流量(Shadow Traffic)</div>
|
||||
<div class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
开启后按比例异步复制请求到 Go Agent,结果不返回用户只做对比;运维用对比报表决定是否放量
|
||||
</div>
|
||||
</div>
|
||||
<Switch v-model:checked="aiShadowTrafficEnabled" checked-children="开" un-checked-children="关" />
|
||||
</div>
|
||||
<div class="mb-4 pl-2">
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">影子流量百分比(0-100)</div>
|
||||
<InputNumber v-model:value="aiShadowTrafficRatio" :min="0" :max="100" style="width: 200px" />
|
||||
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">0=关闭;建议先用 10% 跑 1 周再考虑放量</div>
|
||||
</div>
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm text-[hsl(var(--foreground))]">A/B Test(实验组直接返回 Agent 结果)</div>
|
||||
<div class="text-xs text-[hsl(var(--muted-foreground))]">
|
||||
开启后按比例把请求 provider 切换为 agent,实验组用户直接看到 Agent 输出
|
||||
</div>
|
||||
</div>
|
||||
<Switch v-model:checked="aiAbTestEnabled" checked-children="开" un-checked-children="关" />
|
||||
</div>
|
||||
<div class="pl-2">
|
||||
<div class="mb-1 text-sm text-[hsl(var(--foreground))]">实验组流量百分比(0-100)</div>
|
||||
<InputNumber v-model:value="aiAbTestExperimentRatio" :min="0" :max="100" style="width: 200px" />
|
||||
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">10 表示 10% 用户走 Agent 实验组</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button type="primary" :loading="advSaving" @click="handleSaveAdvanced">
|
||||
保存 Agent 高级配置
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Spin>
|
||||
|
||||
Reference in New Issue
Block a user