Files
EasyFlow/easyflow-ui-admin/app/src/views/ai/agents/components/AgentBaseForm.vue
陈子默 c78074a969 feat: 按模型上下文自动配置智能体压缩阈值
- 从 llm.json 解析上下文与输出上限,并增强短模型 ID 匹配

- 切换模型、保存和试运行时按预算公式重算,目录缺失时回退 30K

- 修复模型列表接口未返回能力元数据
2026-07-30 14:20:10 +08:00

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>