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:
thzxx
2026-06-11 22:07:46 +08:00
parent b5d8d08986
commit 933ce7a082
26 changed files with 1040 additions and 1525 deletions
+30
View File
@@ -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) {
+20 -5
View File
@@ -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);
}
-12
View File
@@ -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', () => {
+1 -1
View File
@@ -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;