- 从 llm.json 解析上下文与输出上限,并增强短模型 ID 匹配 - 切换模型、保存和试运行时按预算公式重算,目录缺失时回退 30K - 修复模型列表接口未返回能力元数据
261 lines
7.0 KiB
Vue
261 lines
7.0 KiB
Vue
<script setup lang="ts">
|
|
/* eslint-disable vue/no-mutating-props */
|
|
import type { AgentInfo, AgentOption, AgentToolBinding } from '../types';
|
|
|
|
import { InfoFilled } from '@element-plus/icons-vue';
|
|
import {
|
|
ElForm,
|
|
ElFormItem,
|
|
ElIcon,
|
|
ElInput,
|
|
ElInputNumber,
|
|
ElOption,
|
|
ElSelect,
|
|
ElSwitch,
|
|
ElTooltip,
|
|
} from 'element-plus';
|
|
|
|
import { resolveAgentCompressionTokenThreshold } from '../compression-threshold';
|
|
|
|
const props = defineProps<{
|
|
agent: AgentInfo;
|
|
categories: AgentOption[];
|
|
models: AgentOption[];
|
|
toolBindings: AgentToolBinding[];
|
|
}>();
|
|
|
|
const emit = defineEmits<{ change: [] }>();
|
|
|
|
function handleModelChange(modelId: AgentInfo['modelId']) {
|
|
const selectedModel = props.models.find(
|
|
(model) => model.value === String(modelId ?? ''),
|
|
);
|
|
const memoryConfig = (props.agent.memoryConfigJson ||= {});
|
|
const compressionParameter = (memoryConfig.compressionParameter ||= {});
|
|
compressionParameter.minCompressionTokenThreshold =
|
|
resolveAgentCompressionTokenThreshold({
|
|
contextWindowTokens: selectedModel?.raw?.contextWindowTokens,
|
|
generationConfig: props.agent.generationConfigJson,
|
|
maxOutputTokens: selectedModel?.raw?.maxOutputTokens,
|
|
systemPrompt: props.agent.promptConfigJson?.systemPrompt,
|
|
toolBindings: props.toolBindings,
|
|
});
|
|
emit('change');
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<ElForm label-position="top" class="agent-form">
|
|
<ElFormItem label="名称" required>
|
|
<ElInput
|
|
v-model="agent.name"
|
|
maxlength="50"
|
|
show-word-limit
|
|
@input="emit('change')"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="描述">
|
|
<ElInput
|
|
v-model="agent.description"
|
|
type="textarea"
|
|
:rows="3"
|
|
maxlength="200"
|
|
show-word-limit
|
|
@input="emit('change')"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="头像">
|
|
<ElInput
|
|
v-model="agent.avatar"
|
|
placeholder="图片地址"
|
|
@input="emit('change')"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem label="分类">
|
|
<ElSelect
|
|
v-model="agent.categoryId"
|
|
clearable
|
|
filterable
|
|
@change="emit('change')"
|
|
>
|
|
<ElOption
|
|
v-for="item in categories"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</ElSelect>
|
|
</ElFormItem>
|
|
<ElFormItem label="模型" required>
|
|
<ElSelect v-model="agent.modelId" filterable @change="handleModelChange">
|
|
<ElOption
|
|
v-for="item in models"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</ElSelect>
|
|
</ElFormItem>
|
|
<ElFormItem>
|
|
<template #label>
|
|
<span class="agent-form__label">
|
|
模型流式响应
|
|
<ElTooltip
|
|
content="关闭后,模型会在生成完成后一次性返回回答。"
|
|
effect="light"
|
|
placement="top"
|
|
>
|
|
<ElIcon class="agent-form__info" aria-label="模型流式响应说明">
|
|
<InfoFilled />
|
|
</ElIcon>
|
|
</ElTooltip>
|
|
</span>
|
|
</template>
|
|
<ElSwitch
|
|
v-model="agent.generationConfigJson!.stream"
|
|
aria-label="模型流式响应"
|
|
@change="emit('change')"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem>
|
|
<template #label>
|
|
<span class="agent-form__label">
|
|
文档上下文预算
|
|
<ElTooltip
|
|
:trigger-keys="['Enter', 'Space']"
|
|
content="控制单次对话最多注入多少文档内容,建议设置为 20K Token 或以上。数值过低可能导致长文档仅读取部分内容,影响回答完整性;请确保该值未超过模型的实际上下文能力。"
|
|
effect="light"
|
|
placement="top"
|
|
>
|
|
<ElIcon
|
|
class="agent-form__info"
|
|
aria-label="文档上下文预算说明"
|
|
tabindex="0"
|
|
>
|
|
<InfoFilled />
|
|
</ElIcon>
|
|
</ElTooltip>
|
|
</span>
|
|
</template>
|
|
<ElInputNumber
|
|
v-model="agent.executionConfigJson!.documentContextBudgetTokens"
|
|
:min="1"
|
|
:step="1000"
|
|
controls-position="right"
|
|
aria-label="文档上下文预算 Token 数"
|
|
@change="emit('change')"
|
|
/>
|
|
<div
|
|
v-if="
|
|
Number(agent.executionConfigJson!.documentContextBudgetTokens) <
|
|
20_000
|
|
"
|
|
class="agent-form__hint is-warning"
|
|
>
|
|
建议设置为 20K Token 或以上
|
|
</div>
|
|
</ElFormItem>
|
|
<ElFormItem label="系统提示词">
|
|
<ElInput
|
|
v-model="agent.promptConfigJson!.systemPrompt"
|
|
type="textarea"
|
|
:rows="7"
|
|
@input="emit('change')"
|
|
/>
|
|
</ElFormItem>
|
|
<div class="agent-form__grid">
|
|
<ElFormItem>
|
|
<template #label>
|
|
<span class="agent-form__label">
|
|
最近保留消息数
|
|
<ElTooltip
|
|
content="压缩上下文时,最近这 N 条消息会尽量保留原文;更早的消息会优先被压缩。"
|
|
effect="light"
|
|
placement="top"
|
|
>
|
|
<ElIcon class="agent-form__info" aria-label="最近保留消息数说明">
|
|
<InfoFilled />
|
|
</ElIcon>
|
|
</ElTooltip>
|
|
</span>
|
|
</template>
|
|
<ElInputNumber
|
|
v-model="agent.memoryConfigJson!.compressionParameter.lastKeep"
|
|
:min="0"
|
|
:max="100"
|
|
controls-position="right"
|
|
@change="emit('change')"
|
|
/>
|
|
</ElFormItem>
|
|
<ElFormItem>
|
|
<template #label>
|
|
<span class="agent-form__label">
|
|
最小压缩 Token 阈值
|
|
<ElTooltip
|
|
content="选择模型时按上下文 85% 与扣除输出、系统提示词、工具和安全余量后的可用值取较小值;模型目录未提供上下文时使用 30K。"
|
|
effect="light"
|
|
placement="top"
|
|
>
|
|
<ElIcon
|
|
class="agent-form__info"
|
|
aria-label="最小压缩 Token 阈值说明"
|
|
>
|
|
<InfoFilled />
|
|
</ElIcon>
|
|
</ElTooltip>
|
|
</span>
|
|
</template>
|
|
<ElInputNumber
|
|
v-model="
|
|
agent.memoryConfigJson!.compressionParameter
|
|
.minCompressionTokenThreshold
|
|
"
|
|
:min="0"
|
|
:step="500"
|
|
controls-position="right"
|
|
@change="emit('change')"
|
|
/>
|
|
</ElFormItem>
|
|
</div>
|
|
</ElForm>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.agent-form {
|
|
padding: 16px;
|
|
}
|
|
|
|
.agent-form__grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
gap: 8px;
|
|
}
|
|
|
|
.agent-form__label {
|
|
display: inline-flex;
|
|
gap: 4px;
|
|
align-items: center;
|
|
}
|
|
|
|
.agent-form__info {
|
|
font-size: 14px;
|
|
color: var(--el-text-color-secondary);
|
|
cursor: help;
|
|
}
|
|
|
|
.agent-form__hint {
|
|
margin-top: 4px;
|
|
font-size: 12px;
|
|
line-height: 20px;
|
|
}
|
|
|
|
.agent-form__hint.is-warning {
|
|
color: var(--el-color-warning);
|
|
}
|
|
|
|
.agent-form :deep(.el-select),
|
|
.agent-form :deep(.el-input-number) {
|
|
width: 100%;
|
|
}
|
|
</style>
|