From dc6838a0c1d7a76da52ad20f0b2d41966363b3c1 Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Sun, 26 Apr 2026 18:14:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=81=94=E7=BD=91=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20-=20Tavily=20Search=E9=9B=86=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- www/app.js | 102 +++++++++++++++++++++++++++++++++++++++++++++---- www/index.html | 6 +-- www/style.css | 82 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 10 deletions(-) diff --git a/www/app.js b/www/app.js index b9d74fd..852921f 100644 --- a/www/app.js +++ b/www/app.js @@ -5,7 +5,10 @@ const CONFIG = { apiUrl: 'https://open.bigmodel.cn/api/paas/v4/chat/completions', apiKey: '2259e33a1357460abe17919aaf81e73d.K44a8LPQTmFM5PKm', model: 'glm-4.5-air', - maxTokens: 2048 + maxTokens: 2048, + // Tavily Search API + tavilyApiUrl: 'https://api.tavily.com/search', + tavilyApiKey: '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' }; // 数据结构 @@ -760,12 +763,20 @@ async function streamGenerate(userMsgIndex) { sendBtn.disabled = true; const aiMessageIndex = currentConversation.messages.length; + const userMessage = currentConversation.messages[userMsgIndex]; - // 只有开启深度思考时才添加 thinking 字段 + // 如果开启联网搜索,先执行搜索 + let searchResults = null; + if (enableSearch && userMessage.role === 'user') { + searchResults = await performSearch(userMessage.content); + } + + // 只有开启深度思考时才添加 thinking 字段,开启搜索时添加 search_results 字段 currentConversation.messages.push({ role: 'assistant', content: '', - ...(enableThinking ? { thinking: '' } : {}) + ...(enableThinking ? { thinking: '' } : {}), + ...(searchResults ? { search_results: searchResults } : {}) }); renderMessages(); @@ -786,13 +797,25 @@ async function streamGenerate(userMsgIndex) { showStopGenerateBtn(); try { + // 构建消息数组 + let messagesToSend = currentConversation.messages.slice(0, aiMessageIndex).map(m => ({ + role: m.role, + content: m.content + })); + + // 如果有搜索结果,将搜索内容添加到消息中 + if (searchResults) { + const searchContext = formatSearchResultsForLLM(searchResults); + messagesToSend.push({ + role: 'system', + content: `以下是搜索结果,请根据这些信息回答用户问题:\n\n${searchContext}` + }); + } + // 构建请求体 - 统一使用 glm-4.5-air,通过 thinking 参数控制 const requestBody = { model: CONFIG.model, - messages: currentConversation.messages.slice(0, aiMessageIndex).map(m => ({ - role: m.role, - content: m.content - })), + messages: messagesToSend, max_tokens: CONFIG.maxTokens, stream: true, thinking: { @@ -915,6 +938,44 @@ async function streamGenerate(userMsgIndex) { } } +// 执行 Tavily 搜索 +async function performSearch(query) { + try { + const response = await fetch(CONFIG.tavilyApiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${CONFIG.tavilyApiKey}` + }, + body: JSON.stringify({ + query: query, + max_results: 5, + include_raw_content: false + }) + }); + + if (!response.ok) { + console.error('搜索失败:', response.status); + return null; + } + + const data = await response.json(); + return data.results || []; + } catch (error) { + console.error('搜索错误:', error); + return null; + } +} + +// 格式化搜索结果给 LLM +function formatSearchResultsForLLM(results) { + if (!results || results.length === 0) return '无搜索结果'; + + return results.map((r, i) => + `${i + 1}. 【${r.title}】\n来源: ${r.url}\n摘要: ${r.content || '无摘要'}\n` + ).join('\n'); +} + // 显示停止生成按钮 function showStopGenerateBtn() { // 检查是否已存在 @@ -1121,6 +1182,27 @@ function renderMessages() { `; } + // 搜索结果块(仅AI消息,放在思考块前面) + let searchHtml = ''; + if (!isUser && msg.search_results && msg.search_results.length > 0) { + searchHtml = ` +