v0.11.1: Agent Loop稳定性增强 + 6个系统工具 + 搜索引擎替换
【Agent Loop 稳定性】 - P0-1: 工具消息硬限制40条,超出自动删旧 - P0-2: 截断周期从5轮缩短为3轮 - P1-1: 增量记忆提取改为fire-and-forget - P1-2: TOOLS_WITH_DATA_DEPS精简为仅web_fetch - P2: 重复检测改为注入警告而非强制终止 - Final Answer检测增强: >300字自动放行 + 收紧反过早停止 【新增工具】(40→44) - datetime: 系统精确时间(中文日期+时段+人性化) - calculator: 安全数学计算(递归下降解析器) - random: 随机数/随机选择(int/float/pick/string) - uuid: UUID v4生成(crypto.randomUUID) - json_format: JSON格式化+验证+键排序 - hash: MD5/SHA1/SHA256/SHA384/SHA512 【搜索引擎替换】 - Google+DuckDuckGo → 搜狗+360搜索 - 四引擎变为: Bing+百度+搜狗+360搜索 【删除】 - 联网搜索代理全部代码(search-proxy/ + 7文件代理逻辑) - https-proxy-agent依赖 【UI】 - 模型栏: 上下文总长(蓝色)+剩余上下文(绿色)实时显示 - 设置面板上下文长度移至模型栏 - SOUL.md/AGENT.md精简为纯抽象定义 【系统提示词】 - OS环境信息改为从preload同步获取真实值(os.homedir/os.arch/os.userInfo)
This commit is contained in:
@@ -435,6 +435,7 @@ async function handleRetry(): Promise<void> {
|
||||
}
|
||||
renderMessages();
|
||||
await saveCurrentSession();
|
||||
if (loopStats?.prompt_eval_count) updateCtxRemain(loopStats.prompt_eval_count);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -594,6 +595,33 @@ function stopGeneration(): void {
|
||||
if (abortController) abortController.abort();
|
||||
}
|
||||
|
||||
/** 更新剩余上下文显示 */
|
||||
function updateCtxRemain(promptEvalCount?: number): void {
|
||||
const el = document.querySelector('#ctxRemain') as HTMLElement;
|
||||
if (!el) return;
|
||||
const total = state.get<number>(KEYS.NUM_CTX, 0);
|
||||
if (!total || !promptEvalCount || promptEvalCount <= 0) {
|
||||
el.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
const remain = total - promptEvalCount;
|
||||
if (remain <= 0) {
|
||||
el.style.display = 'inline-flex';
|
||||
el.textContent = '⚠ 上下文已满';
|
||||
el.style.color = 'var(--danger, #e74c3c)';
|
||||
} else {
|
||||
el.style.display = 'inline-flex';
|
||||
el.style.color = '';
|
||||
const fmt = remain >= 1024 ? `${(remain / 1024).toFixed(0)}K` : String(remain);
|
||||
el.textContent = `剩余 ${fmt}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function clearCtxRemain(): void {
|
||||
const el = document.querySelector('#ctxRemain') as HTMLElement;
|
||||
if (el) el.style.display = 'none';
|
||||
}
|
||||
|
||||
/** Token 预算比例:文件内容最多占用上下文窗口的比例 */
|
||||
const FILE_TOKEN_BUDGET_RATIO = 0.3;
|
||||
|
||||
@@ -1006,6 +1034,8 @@ async function sendMessageWithAgentLoop(text: string, currentSession: ChatSessio
|
||||
}
|
||||
renderMessages();
|
||||
await saveCurrentSession();
|
||||
// 更新剩余上下文
|
||||
if (loopStats?.prompt_eval_count) updateCtxRemain(loopStats.prompt_eval_count);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { formatSize } from '../utils/utils.js';
|
||||
import { OllamaAPI } from '../api/ollama.js';
|
||||
import { ChatDB } from '../db/chat-db.js';
|
||||
import { logModel, logDebug, logError, logWarn } from '../services/log-service.js';
|
||||
import { clearCtxRemain } from './input-area.js';
|
||||
import type { OllamaModelDetail, ModelCaps } from '../types.js';
|
||||
|
||||
let modelSelectEl: HTMLSelectElement;
|
||||
@@ -159,11 +160,13 @@ async function filterEmbedModels(models: Array<{ name: string }>): Promise<void>
|
||||
}
|
||||
|
||||
async function checkModelCapability(modelName: string): Promise<void> {
|
||||
// 切换模型时清除旧上下文剩余显示
|
||||
clearCtxRemain();
|
||||
|
||||
if (modelCapabilityCache.has(modelName)) {
|
||||
const cached = modelCapabilityCache.get(modelName)!;
|
||||
state.set(KEYS.NUM_CTX, cached.contextLength);
|
||||
const displayEl = document.querySelector('#displayNumCtx');
|
||||
if (displayEl) displayEl.textContent = `${cached.contextLength.toLocaleString()} tokens`;
|
||||
updateCtxTotal(cached.contextLength);
|
||||
applyCapability(modelName, cached);
|
||||
return;
|
||||
}
|
||||
@@ -200,9 +203,8 @@ async function checkModelCapability(modelName: string): Promise<void> {
|
||||
const db = state.get<any>(KEYS.DB);
|
||||
if (db) await db.saveSetting('numCtx', contextLength);
|
||||
|
||||
// 更新设置面板显示
|
||||
const displayEl = document.querySelector('#displayNumCtx');
|
||||
if (displayEl) displayEl.textContent = `${contextLength.toLocaleString()} tokens`;
|
||||
// 更新模型栏上下文长度显示
|
||||
updateCtxTotal(contextLength);
|
||||
|
||||
applyCapability(modelName, caps);
|
||||
} catch (err) {
|
||||
@@ -275,3 +277,16 @@ export function isToolCallingSupported(): boolean {
|
||||
const caps = modelCapabilityCache.get(model);
|
||||
return caps?.tools === true;
|
||||
}
|
||||
|
||||
function formatCtx(n: number): string {
|
||||
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
||||
if (n >= 1024) return `${(n / 1024).toFixed(0)}K`;
|
||||
return String(n);
|
||||
}
|
||||
|
||||
function updateCtxTotal(contextLength: number): void {
|
||||
const el = document.querySelector('#ctxTotal') as HTMLElement;
|
||||
if (!el) return;
|
||||
el.style.display = 'inline-flex';
|
||||
el.textContent = formatCtx(contextLength);
|
||||
}
|
||||
|
||||
@@ -39,18 +39,6 @@ export function initSettingsModal(): void {
|
||||
}, 500);
|
||||
document.querySelector('#inputServerUrl')!.addEventListener('input', saveServerUrl);
|
||||
|
||||
// 代理设置
|
||||
const inputProxyUrl = document.querySelector('#inputProxyUrl') as HTMLInputElement;
|
||||
if (inputProxyUrl) {
|
||||
const saveProxyUrl = debounce(async () => {
|
||||
const db = state.get<ChatDB | null>(KEYS.DB);
|
||||
const url = inputProxyUrl.value.trim();
|
||||
if (db) await db.saveSetting('proxyUrl', url);
|
||||
logSetting('代理', url || '已禁用');
|
||||
}, 500);
|
||||
inputProxyUrl.addEventListener('input', saveProxyUrl);
|
||||
}
|
||||
|
||||
const tempSlider = document.querySelector('#inputTemperature') as HTMLInputElement;
|
||||
const tempDisplay = document.querySelector('#tempValue')!;
|
||||
tempSlider.addEventListener('input', () => {
|
||||
|
||||
@@ -834,7 +834,7 @@ function renderToolCalls(): void {
|
||||
</div>
|
||||
<div class="ws-idle-divider"></div>
|
||||
<div class="ws-idle-desc">AI 对话中自动调用工具,结果在此展示</div>
|
||||
<div class="ws-idle-hint">共 38 个内置工具 · MCP 动态扩展</div>
|
||||
<div class="ws-idle-hint">共 44 个内置工具 · MCP 动态扩展</div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user