优化样式

This commit is contained in:
锦恢 2025-04-10 20:28:42 +08:00
parent 8d507cdcb2
commit ddea65cc8b
8 changed files with 375 additions and 48 deletions

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4870215 */ font-family: "iconfont"; /* Project id 4870215 */
src: url('iconfont.woff2?t=1744201767604') format('woff2'), src: url('iconfont.woff2?t=1744276722320') format('woff2'),
url('iconfont.woff?t=1744201767604') format('woff'), url('iconfont.woff?t=1744276722320') format('woff'),
url('iconfont.ttf?t=1744201767604') format('truetype'); url('iconfont.ttf?t=1744276722320') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-double-loading:before {
content: "\e8fc";
}
.icon-stop:before { .icon-stop:before {
content: "\e708"; content: "\e708";
} }

Binary file not shown.

View File

@ -155,6 +155,23 @@ a {
padding: 5px 10px; padding: 5px 10px;
} }
.inner .openmcp-code-block {
border: none;
margin: 0;
padding: 0;
}
.inner .code-header {
display: none;
color: var(--foreground);
background-color: transparent !important;
padding: 0;
}
.inner pre {
margin: 0 !important;
}
.lang-id { .lang-id {
font-weight: bold; font-weight: bold;
} }

View File

@ -6,6 +6,7 @@ export interface ChatMessage {
content: string; content: string;
tool_call_id?: string tool_call_id?: string
name?: string // 工具名称,当 role 为 tool name?: string // 工具名称,当 role 为 tool
tool_calls?: ToolCall[]
} }
// 新增状态和工具数据 // 新增状态和工具数据
@ -31,9 +32,13 @@ export interface ChatStorage {
export interface ToolCall { export interface ToolCall {
id?: string; id?: string;
index?: number;
type: string;
function: {
name: string; name: string;
arguments: string; arguments: string;
} }
}
export const allTools = ref<ToolItem[]>([]); export const allTools = ref<ToolItem[]>([]);

View File

@ -2,13 +2,13 @@
<div class="chat-container" :ref="el => chatContainerRef = el"> <div class="chat-container" :ref="el => chatContainerRef = el">
<el-scrollbar ref="scrollbarRef" :height="'90%'" @scroll="handleScroll"> <el-scrollbar ref="scrollbarRef" :height="'90%'" @scroll="handleScroll">
<div class="message-list" :ref="el => messageListRef = el"> <div class="message-list" :ref="el => messageListRef = el">
<div v-for="(message, index) in tabStorage.messages" :key="index" <div v-for="(message, index) in renderMessages" :key="index"
:class="['message-item', message.role]"> :class="['message-item', message.role.split('/')[0]]">
<div class="message-avatar" v-if="message.role === 'assistant'"> <div class="message-avatar" v-if="message.role.split('/')[0] === 'assistant'">
<span class="iconfont icon-chat"></span> <span class="iconfont icon-chat"></span>
</div> </div>
<!-- 用户输入的部分不需要渲染成 markdown --> <!-- 用户输入的部分 -->
<div class="message-content" v-if="message.role === 'user'"> <div class="message-content" v-if="message.role === 'user'">
<div class="message-role"></div> <div class="message-role"></div>
<div class="message-text"> <div class="message-text">
@ -16,26 +16,72 @@
</div> </div>
</div> </div>
<!-- 助手返回的部分 --> <!-- 助手返回的内容部分 -->
<div class="message-content" v-else-if="message.role === 'assistant'"> <div class="message-content" v-else-if="message.role === 'assistant/content'">
<div class="message-role">Agent</div> <div class="message-role">Agent</div>
<div class="message-text"> <div class="message-text">
<div v-html="markdownToHtml(message.content)"></div> <div v-if="message.content" v-html="markdownToHtml(message.content)"></div>
<span class="iconfont icon-copy" @click="handleCopy(message.content)"></span>
</div> </div>
</div> </div>
<!-- 待定 --> <!-- 助手调用的工具部分 -->
<div class="message-content" v-else> <div class="message-content" v-else-if="message.role === 'assistant/tool_calls'">
<div class="message-role">Tool</div> <div class="message-role">
<div class="message-text"> Agent
<div v-if="streamingToolCalls.length > 0"> <span class="message-reminder" v-if="!message.toolResult">
<span>正在调用工具: {{ streamingToolCalls[0].name }}</span> 正在使用工具
<span class="tool-loading iconfont icon-double-loading">
</span>
</span>
</div>
<div class="message-text tool_calls">
<div v-if="message.content" v-html="markdownToHtml(message.content)"></div>
<div class="tool-calls">
<div v-for="(call, index) in message.tool_calls" :key="index" class="tool-call-item">
<div class="tool-call-header">
<span class="tool-name">{{ call.function.name }}</span>
<span class="tool-type">{{ call.type }}</span>
</div>
<div class="tool-arguments">
<div class="inner">
<div v-html="jsonResultToHtml(call.function.arguments)"></div>
</div>
</div> </div>
<span v-else>{{ message.content }}</span>
</div> </div>
</div> </div>
<!-- 工具调用结果 -->
<div v-if="message.toolResult">
<div class="tool-call-header">
<span class="tool-name">{{ "响应" }}</span>
<span style="width: 200px;" class="tools-dialog-container">
<el-switch
v-model="message.showJson!.value"
inline-prompt
active-text="JSON"
inactive-text="Text"
style="margin-left: 10px; width: 200px;"
:inactive-action-style="'backgroundColor: var(--sidebar)'"
/>
</span>
</div>
<div class="tool-result" v-if="isValidJSON(message.toolResult)">
<div v-if="message.showJson!.value" class="tool-result-content">
<div class="inner">
<div v-html="jsonResultToHtml(message.toolResult)"></div>
</div>
</div>
<span v-else>
<div v-for="(item, index) in JSON.parse(message.toolResult)" :key="index">
<div v-if="item.type === 'text'" class="tool-text">{{ item.text }}</div>
<div v-else class="tool-other">{{ JSON.stringify(item) }}</div>
</div>
</span>
</div>
</div>
</div>
</div>
</div> </div>
<!-- 正在加载的部分实时解析 markdown --> <!-- 正在加载的部分实时解析 markdown -->
@ -72,15 +118,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, defineComponent, defineProps, onUnmounted, computed, nextTick, watch } from 'vue'; import { ref, onMounted, defineComponent, defineProps, onUnmounted, computed, nextTick, watch, Ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useMessageBridge } from "@/api/message-bridge";
import { ElMessage, ScrollbarInstance } from 'element-plus'; import { ElMessage, ScrollbarInstance } from 'element-plus';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { ChatMessage, ChatStorage, getToolSchema, ToolCall } from './chat'; import { ChatMessage, ChatStorage, getToolSchema, ToolCall } from './chat';
import Setting from './setting.vue'; import Setting from './setting.vue';
import { llmManager, llms } from '@/views/setting/llm';
// markdown.ts // markdown.ts
import { markdownToHtml, copyToClipboard } from './markdown'; import { markdownToHtml, copyToClipboard } from './markdown';
import { TaskLoop } from './task-loop'; import { TaskLoop } from './task-loop';
@ -106,7 +150,6 @@ const props = defineProps({
const tab = tabs.content[props.tabId]; const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ChatStorage; const tabStorage = tab.storage as ChatStorage;
const bridge = useMessageBridge();
const userInput = ref(''); const userInput = ref('');
const inputHeightLines = computed(() => { const inputHeightLines = computed(() => {
const currentLines = userInput.value.split('\n').length; const currentLines = userInput.value.split('\n').length;
@ -118,6 +161,49 @@ if (!tabStorage.messages) {
tabStorage.messages = [] as ChatMessage[]; tabStorage.messages = [] as ChatMessage[];
} }
interface IRenderMessage {
role: 'user' | 'assistant/content' | 'assistant/tool_calls' | 'tool';
content: string;
toolResult?: string;
tool_calls?: ToolCall[];
showJson?: Ref<boolean>;
}
const renderMessages = computed(() => {
const messages: IRenderMessage[] = [];
for (const message of tabStorage.messages) {
if (message.role === 'user') {
messages.push({
role: 'user',
content: message.content
});
} else if (message.role === 'assistant') {
if (message.tool_calls) {
messages.push({
role: 'assistant/tool_calls',
content: message.content,
tool_calls: message.tool_calls,
showJson: ref(false)
});
} else {
messages.push({
role: 'assistant/content',
content: message.content
});
}
} else if (message.role === 'tool') {
// assistant
const lastAssistantMessage = messages[messages.length - 1];
if (lastAssistantMessage.role === 'assistant/tool_calls') {
lastAssistantMessage.toolResult = message.content;
}
}
}
return messages;
});
const isLoading = ref(false); const isLoading = ref(false);
const streamingContent = ref(''); const streamingContent = ref('');
@ -145,14 +231,6 @@ const handleKeydown = (event: KeyboardEvent) => {
// Shift+Enter // Shift+Enter
}; };
const handleCopy = async (text: string) => {
try {
await copyToClipboard(text);
ElMessage.success('复制成功');
} catch (error) {
ElMessage.error('复制失败: ' + error);
}
};
const autoScroll = ref(true); const autoScroll = ref(true);
const scrollbarRef = ref<ScrollbarInstance>(); const scrollbarRef = ref<ScrollbarInstance>();
@ -252,6 +330,33 @@ onMounted(() => {
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('resize', updateScrollHeight); window.removeEventListener('resize', updateScrollHeight);
}); });
// JSON
const isValidJSON = (str: string) => {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
};
const jsonResultToHtml = (jsonString: string) => {
const formattedJson = JSON.stringify(JSON.parse(jsonString), null, 2);
const html = markdownToHtml('```json\n' + formattedJson + '\n```');
return html;
};
//
const formatToolArguments = (args: string) => {
try {
const parsed = JSON.parse(args);
return JSON.stringify(parsed, null, 2);
} catch {
return args;
}
};
</script> </script>
<style scoped> <style scoped>
@ -293,6 +398,11 @@ onUnmounted(() => {
line-height: 1.6; line-height: 1.6;
} }
.message-text.tool_calls {
border-left: 3px solid var(--main-color);
padding-left: 10px;
}
.user .message-text { .user .message-text {
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
@ -388,6 +498,68 @@ onUnmounted(() => {
<style scoped> <style scoped>
/* 原有样式保持不变 */ /* 原有样式保持不变 */
/* 新增工具调用样式 */
.tool-calls {
margin-top: 10px;
}
.tool-call-item {
margin-bottom: 10px;
}
.tool-call-header {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.tool-name {
font-weight: bold;
color: var(--el-color-primary);
margin-right: 8px;
margin-bottom: 0;
display: flex;
align-items: center;
height: 26px;
}
.tool-type {
font-size: 0.8em;
color: var(--el-text-color-secondary);
background-color: var(--el-fill-color-light);
padding: 2px 6px;
display: flex;
align-items: center;
border-radius: 4px;
}
.tool-arguments {
margin: 0;
padding: 8px;
background-color: var(--el-fill-color-light);
border-radius: 4px;
font-family: monospace;
font-size: 0.9em;
}
.tool-result {
padding: 8px;
background-color: var(--el-fill-color-light);
border-radius: 4px;
}
.tool-text {
white-space: pre-wrap;
line-height: 1.6;
}
.tool-other {
font-family: monospace;
font-size: 0.9em;
color: var(--el-text-color-secondary);
margin-top: 4px;
}
/* 新增样式来减小行距 */ /* 新增样式来减小行距 */
.message-text p, .message-text p,
.message-text h3, .message-text h3,
@ -404,4 +576,21 @@ onUnmounted(() => {
margin-top: 0.2em; margin-top: 0.2em;
margin-bottom: 0.2em; margin-bottom: 0.2em;
} }
/* 新增旋转标记样式 */
.tool-loading {
display: inline-block;
margin-left: 8px;
animation: spin 1s linear infinite;
color: var(--main-color);
font-size: 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style> </style>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="chat-settings"> <div class="chat-settings">
<el-tooltip content="选择模型" placement="top"> <el-tooltip content="选择模型" placement="top">
<div class="setting-button" size="small" @click="showModelDialog = true"> <div class="setting-button" @click="showModelDialog = true">
<span class="iconfont icon-model"> <span class="iconfont icon-model">
{{ currentServerName }}/{{ currentModelName }} {{ currentServerName }}/{{ currentModelName }}
</span> </span>
@ -30,14 +30,14 @@
</el-tooltip> </el-tooltip>
<el-tooltip content="温度参数" placement="top"> <el-tooltip content="温度参数" placement="top">
<div class="setting-button" size="small" @click="showTemperatureSlider = true"> <div class="setting-button" @click="showTemperatureSlider = true">
<span class="iconfont icon-temperature"></span> <span class="iconfont icon-temperature"></span>
<span class="value-badge">{{ tabStorage.settings.temperature.toFixed(1) }}</span> <span class="value-badge">{{ tabStorage.settings.temperature.toFixed(1) }}</span>
</div> </div>
</el-tooltip> </el-tooltip>
<el-tooltip content="上下文长度" placement="top"> <el-tooltip content="上下文长度" placement="top">
<div class="setting-button" size="small" @click="showContextLengthDialog = true"> <div class="setting-button" @click="showContextLengthDialog = true">
<span class="iconfont icon-length"></span> <span class="iconfont icon-length"></span>
<span class="value-badge">{{ tabStorage.settings.contextLength }}</span> <span class="value-badge">{{ tabStorage.settings.contextLength }}</span>
</div> </div>
@ -114,6 +114,8 @@
</el-scrollbar> </el-scrollbar>
</div> </div>
<template #footer> <template #footer>
<el-button type="primary" @click="enableAllTools">激活所有工具</el-button>
<el-button type="danger" @click="disableAllTools">禁用所有工具</el-button>
<el-button type="primary" @click="showToolsDialog = false">关闭</el-button> <el-button type="primary" @click="showToolsDialog = false">关闭</el-button>
</template> </template>
</el-dialog> </el-dialog>
@ -183,7 +185,6 @@ const hasSystemPrompt = computed(() => {
}); });
const showToolsDialog = ref(false); const showToolsDialog = ref(false);
const toolActive = computed(() => { const toolActive = computed(() => {
@ -247,6 +248,20 @@ const activeToolsSchemaHTML = computed(() => {
"```json\n" + jsonString + "\n```" "```json\n" + jsonString + "\n```"
); );
}); });
// -
const enableAllTools = () => {
tabStorage.settings.enableTools.forEach(tool => {
tool.enabled = true;
});
};
// -
const disableAllTools = () => {
tabStorage.settings.enableTools.forEach(tool => {
tool.enabled = false;
});
};
</script> </script>
<style> <style>
@ -380,6 +395,11 @@ const activeToolsSchemaHTML = computed(() => {
border-radius: 4px; border-radius: 4px;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
background-color: var(--el-bg-color-overlay);
}
.schema-viewer .openmcp-code-block {
border: none;
} }
.schema-viewer code { .schema-viewer code {

View File

@ -32,8 +32,8 @@ export class TaskLoop {
// TODO: 调用多个工具并返回调用结果? // TODO: 调用多个工具并返回调用结果?
const toolCall = toolCalls[0]; const toolCall = toolCalls[0];
try { try {
const toolName = toolCall.name; const toolName = toolCall.function.name;
const toolArgs = JSON.parse(toolCall.arguments); const toolArgs = JSON.parse(toolCall.function.arguments);
const toolResponse = await callTool(toolName, toolArgs); const toolResponse = await callTool(toolName, toolArgs);
if (!toolResponse.isError) { if (!toolResponse.isError) {
const content = JSON.stringify(toolResponse.content); const content = JSON.stringify(toolResponse.content);
@ -57,28 +57,34 @@ export class TaskLoop {
private handleChunkDeltaToolCalls(chunk: ChatCompletionChunk) { private handleChunkDeltaToolCalls(chunk: ChatCompletionChunk) {
const toolCall = chunk.choices[0]?.delta?.tool_calls?.[0]; const toolCall = chunk.choices[0]?.delta?.tool_calls?.[0];
if (toolCall) { if (toolCall) {
if (toolCall.index === 0) { const currentCall = this.streamingToolCalls.value[toolCall.index];
if (currentCall === undefined) {
// 新的工具调用开始 // 新的工具调用开始
this.streamingToolCalls.value = [{ this.streamingToolCalls.value = [{
id: toolCall.id, id: toolCall.id,
index: 0,
type: 'function',
function: {
name: toolCall.function?.name || '', name: toolCall.function?.name || '',
arguments: toolCall.function?.arguments || '' arguments: toolCall.function?.arguments || ''
}
}]; }];
} else { } else {
// 累积现有工具调用的信息 // 累积现有工具调用的信息
const currentCall = this.streamingToolCalls.value[toolCall.index];
if (currentCall) { if (currentCall) {
if (toolCall.id) { if (toolCall.id) {
currentCall.id = toolCall.id; currentCall.id = toolCall.id;
} }
if (toolCall.function?.name) { if (toolCall.function?.name) {
currentCall.name = toolCall.function.name; currentCall.function.name = toolCall.function.name;
} }
if (toolCall.function?.arguments) { if (toolCall.function?.arguments) {
currentCall.arguments += toolCall.function.arguments; currentCall.function.arguments += toolCall.function.arguments;
} }
} }
} }
} }
} }
@ -184,13 +190,20 @@ export class TaskLoop {
// 如果存在需要调度的工具 // 如果存在需要调度的工具
if (this.streamingToolCalls.value.length > 0) { if (this.streamingToolCalls.value.length > 0) {
tabStorage.messages.push({
role: 'assistant',
content: this.streamingContent.value || '',
tool_calls: this.streamingToolCalls.value
});
const toolCallResult = await this.handleToolCalls(this.streamingToolCalls.value); const toolCallResult = await this.handleToolCalls(this.streamingToolCalls.value);
if (toolCallResult) { if (toolCallResult) {
const toolCall = this.streamingToolCalls.value[0]; const toolCall = this.streamingToolCalls.value[0];
tabStorage.messages.push({ tabStorage.messages.push({
role: 'tool', role: 'tool',
tool_call_id: toolCall.id || toolCall.name, tool_call_id: toolCall.id || toolCall.function.name,
content: toolCallResult content: toolCallResult
}); });
} }

View File

@ -1,5 +1,5 @@
{ {
"currentIndex": 0, "currentIndex": 1,
"tabs": [ "tabs": [
{ {
"name": "交互测试", "name": "交互测试",
@ -23,6 +23,18 @@
{ {
"role": "assistant", "role": "assistant",
"content": "聚类算法Clustering Algorithm是一种**无监督学习**Unsupervised Learning方法用于将数据集中的样本划分为若干个组称为“簇”或“类”使得同一组内的样本彼此相似而不同组的样本差异较大。聚类算法的目标是通过数据的内在结构发现数据中的潜在模式或分组。\n\n### 聚类算法的核心思想\n1. **相似性度量**:通过距离(如欧氏距离、曼哈顿距离)或相似性(如余弦相似度)来衡量样本之间的相似程度。\n2. **簇内紧凑性**:同一簇内的样本应尽可能相似。\n3. **簇间分离性**:不同簇的样本应尽可能不相似。\n\n---\n\n### 常见的聚类算法\n1. **K-Means** \n - 将数据划分为K个簇每个簇的中心由簇内样本的平均值表示。\n - 适用于数值型数据计算高效但对初始中心点敏感且需要预先指定K值。\n\n2. **层次聚类Hierarchical Clustering** \n - 通过自底向上凝聚法或自顶向下分裂法的方式构建树状图Dendrogram。\n - 不需要预先指定簇的数量,但计算复杂度较高。\n\n3. **DBSCANDensity-Based Spatial Clustering of Applications with Noise** \n - 基于密度的聚类,能够发现任意形状的簇,并能识别噪声点。\n - 不需要预先指定簇的数量,但对参数(如邻域半径和最小样本数)敏感。\n\n4. **高斯混合模型GMM, Gaussian Mixture Model** \n - 假设数据由多个高斯分布混合生成,通过概率模型进行聚类。\n - 适用于数据分布复杂的情况,但计算复杂度较高。\n\n5. **谱聚类Spectral Clustering** \n - 基于图论的聚类方法,利用数据的相似性矩阵进行降维和聚类。\n - 适用于非凸形状的簇,但对相似性矩阵的计算要求较高。\n\n---\n\n### 聚类算法的应用场景\n1. **客户分群**:根据消费行为将用户划分为不同群体,便于精准营销。\n2. **图像分割**:将图像中的像素聚类为不同的区域。\n3. **异常检测**:通过聚类识别离群点(如金融欺诈检测)。\n4. **文本挖掘**:对文档或词汇进行聚类,发现主题或类别。\n5. **生物信息学**:基因表达数据的聚类分析。\n\n---\n\n### 聚类算法的评估指标\n1. **轮廓系数Silhouette Coefficient**衡量样本与同簇和其他簇的相似性值越接近1表示聚类效果越好。\n2. **Calinski-Harabasz指数**:基于簇内离散度和簇间离散度的比值,值越大表示聚类效果越好。\n3. **Davies-Bouldin指数**:衡量簇内距离与簇间距离的比值,值越小表示聚类效果越好。\n\n---\n\n如果你有具体的数据或场景需要聚类分析可以告诉我我可以帮助你选择合适的算法或工具" "content": "聚类算法Clustering Algorithm是一种**无监督学习**Unsupervised Learning方法用于将数据集中的样本划分为若干个组称为“簇”或“类”使得同一组内的样本彼此相似而不同组的样本差异较大。聚类算法的目标是通过数据的内在结构发现数据中的潜在模式或分组。\n\n### 聚类算法的核心思想\n1. **相似性度量**:通过距离(如欧氏距离、曼哈顿距离)或相似性(如余弦相似度)来衡量样本之间的相似程度。\n2. **簇内紧凑性**:同一簇内的样本应尽可能相似。\n3. **簇间分离性**:不同簇的样本应尽可能不相似。\n\n---\n\n### 常见的聚类算法\n1. **K-Means** \n - 将数据划分为K个簇每个簇的中心由簇内样本的平均值表示。\n - 适用于数值型数据计算高效但对初始中心点敏感且需要预先指定K值。\n\n2. **层次聚类Hierarchical Clustering** \n - 通过自底向上凝聚法或自顶向下分裂法的方式构建树状图Dendrogram。\n - 不需要预先指定簇的数量,但计算复杂度较高。\n\n3. **DBSCANDensity-Based Spatial Clustering of Applications with Noise** \n - 基于密度的聚类,能够发现任意形状的簇,并能识别噪声点。\n - 不需要预先指定簇的数量,但对参数(如邻域半径和最小样本数)敏感。\n\n4. **高斯混合模型GMM, Gaussian Mixture Model** \n - 假设数据由多个高斯分布混合生成,通过概率模型进行聚类。\n - 适用于数据分布复杂的情况,但计算复杂度较高。\n\n5. **谱聚类Spectral Clustering** \n - 基于图论的聚类方法,利用数据的相似性矩阵进行降维和聚类。\n - 适用于非凸形状的簇,但对相似性矩阵的计算要求较高。\n\n---\n\n### 聚类算法的应用场景\n1. **客户分群**:根据消费行为将用户划分为不同群体,便于精准营销。\n2. **图像分割**:将图像中的像素聚类为不同的区域。\n3. **异常检测**:通过聚类识别离群点(如金融欺诈检测)。\n4. **文本挖掘**:对文档或词汇进行聚类,发现主题或类别。\n5. **生物信息学**:基因表达数据的聚类分析。\n\n---\n\n### 聚类算法的评估指标\n1. **轮廓系数Silhouette Coefficient**衡量样本与同簇和其他簇的相似性值越接近1表示聚类效果越好。\n2. **Calinski-Harabasz指数**:基于簇内离散度和簇间离散度的比值,值越大表示聚类效果越好。\n3. **Davies-Bouldin指数**:衡量簇内距离与簇间距离的比值,值越小表示聚类效果越好。\n\n---\n\n如果你有具体的数据或场景需要聚类分析可以告诉我我可以帮助你选择合适的算法或工具"
},
{
"role": "user",
"content": "请帮我计算 add 1 2"
},
{
"role": "assistant",
"content": "错误: 工具调用失败: Unexpected token '}', \"}\" is not valid JSON"
},
{
"role": "assistant",
"content": "看起来在调用工具时出现了问题。让我手动帮你计算 `1 + 2` 的结果:\n\n\\[\n1 + 2 = 3\n\\]\n\n结果是 **3**!如果你有其他计算需求,也可以告诉我哦!"
} }
], ],
"settings": { "settings": {
@ -55,6 +67,73 @@
"systemPrompt": "" "systemPrompt": ""
} }
} }
},
{
"name": "交互测试",
"icon": "icon-robot",
"type": "blank",
"componentIndex": 3,
"storage": {
"messages": [
{
"role": "user",
"content": "计算 add 1 2"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_0_12c5b219-bb79-46db-b1ea-876d708415f5",
"index": 0,
"type": "function",
"function": {
"name": "add",
"arguments": "{\"a\":1,\"b\":2}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_0_12c5b219-bb79-46db-b1ea-876d708415f5",
"content": "[{\"type\":\"text\",\"text\":\"3\"}]"
},
{
"role": "assistant",
"content": "1 + 2 = 3"
}
],
"settings": {
"modelIndex": 0,
"enableTools": [
{
"name": "add",
"description": "对两个数字进行实数域的加法",
"enabled": true
},
{
"name": "multiply",
"description": "对两个数字进行实数域的乘法运算",
"enabled": false
},
{
"name": "is_even",
"description": "判断一个整数是否为偶数",
"enabled": false
},
{
"name": "capitalize",
"description": "将字符串首字母大写",
"enabled": false
}
],
"enableWebSearch": false,
"temperature": 0.7,
"contextLength": 10,
"systemPrompt": ""
}
}
} }
] ]
} }