v0.16.1: 优化Reflecting阶段说了做但没做检测逻辑,降低误判率
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/version-v0.16.0-E8734A?style=flat-square" alt="version">
|
<img src="https://img.shields.io/badge/version-v0.16.1-E8734A?style=flat-square" alt="version">
|
||||||
<img src="https://img.shields.io/badge/electron-33+-47848F?style=flat-square&logo=electron" alt="electron">
|
<img src="https://img.shields.io/badge/electron-33+-47848F?style=flat-square&logo=electron" alt="electron">
|
||||||
<img src="https://img.shields.io/badge/typescript-5.7+-3178C6?style=flat-square&logo=typescript" alt="typescript">
|
<img src="https://img.shields.io/badge/typescript-5.7+-3178C6?style=flat-square&logo=typescript" alt="typescript">
|
||||||
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license">
|
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license">
|
||||||
@@ -253,7 +253,7 @@ npm start
|
|||||||
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
|
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
|
||||||
```
|
```
|
||||||
|
|
||||||
产出:`release/Metona Ollama Setup v0.16.0.exe`
|
产出:`release/Metona Ollama Setup v0.16.1.exe`
|
||||||
|
|
||||||
## 🛠️ 常用命令
|
## 🛠️ 常用命令
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ npm start
|
|||||||
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
|
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
|
||||||
```
|
```
|
||||||
|
|
||||||
Output: `release/Metona Ollama Setup v0.16.0.exe`
|
Output: `release/Metona Ollama Setup v0.16.1.exe`
|
||||||
|
|
||||||
## 🛠️ Common Commands
|
## 🛠️ Common Commands
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "metona-ollama-desktop",
|
"name": "metona-ollama-desktop",
|
||||||
"version": "0.16.0",
|
"version": "0.16.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "metona-ollama-desktop",
|
"name": "metona-ollama-desktop",
|
||||||
"version": "0.16.0",
|
"version": "0.16.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ffmpeg-static": "^5.2.0",
|
"ffmpeg-static": "^5.2.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "metona-ollama-desktop",
|
"name": "metona-ollama-desktop",
|
||||||
"version": "0.16.0",
|
"version": "0.16.1",
|
||||||
"description": "Metona Ollama - TypeScript + Electron 桌面 AI 聊天客户端",
|
"description": "Metona Ollama - TypeScript + Electron 桌面 AI 聊天客户端",
|
||||||
"main": "dist/main/main.js",
|
"main": "dist/main/main.js",
|
||||||
"author": "thzxx",
|
"author": "thzxx",
|
||||||
|
|||||||
+1
-1
@@ -101,7 +101,7 @@ export function createMenu(): void {
|
|||||||
dialog.showMessageBox(mainWindow!, {
|
dialog.showMessageBox(mainWindow!, {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: '关于 Metona Ollama',
|
title: '关于 Metona Ollama',
|
||||||
message: 'Metona Ollama Desktop v0.16.0',
|
message: 'Metona Ollama Desktop v0.16.1',
|
||||||
detail: 'TypeScript + Electron Ollama AI 聊天客户端\n\nhttps://gitee.com/thzxx/metona-ollama',
|
detail: 'TypeScript + Electron Ollama AI 聊天客户端\n\nhttps://gitee.com/thzxx/metona-ollama',
|
||||||
icon: getIconPath()
|
icon: getIconPath()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<img class="logo" src="./assets/icons/llama.png" alt="logo" />
|
<img class="logo" src="./assets/icons/llama.png" alt="logo" />
|
||||||
<span class="app-title">Metona Ollama</span>
|
<span class="app-title">Metona Ollama</span>
|
||||||
<span class="app-version">v0.16.0</span>
|
<span class="app-version">v0.16.1</span>
|
||||||
<button class="icon-btn help-btn" id="btnHelp" title="使用帮助">
|
<button class="icon-btn help-btn" id="btnHelp" title="使用帮助">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||||
|
|||||||
@@ -2601,16 +2601,56 @@ async function handleReflecting(
|
|||||||
|
|
||||||
// ── 异常3: 模型说了要做但没调用工具 → 注入提醒再给一次机会 ──
|
// ── 异常3: 模型说了要做但没调用工具 → 注入提醒再给一次机会 ──
|
||||||
// 场景:AI 回复"我来写入文件"但未发出 write_file 工具调用
|
// 场景:AI 回复"我来写入文件"但未发出 write_file 工具调用
|
||||||
|
// 优化:区分"意图声明"(将来时)与"结果描述"(完成时),避免对总结性回复误判
|
||||||
if (ctx.toolCalls.length === 0 && ctx.content.trim() && ctx.loopCount <= 3) {
|
if (ctx.toolCalls.length === 0 && ctx.content.trim() && ctx.loopCount <= 3) {
|
||||||
const text = ctx.content.toLowerCase();
|
const text = ctx.content.toLowerCase();
|
||||||
|
const contentLen = ctx.content.trim().length;
|
||||||
|
|
||||||
|
// 结果描述迹象 — 回复中包含这些,说明模型在总结/描述而非空谈
|
||||||
|
const resultIndicators = [
|
||||||
|
/根据.*(结果|内容|返回|输出)/, /搜索结果/, /查询结果/, /查找结果/,
|
||||||
|
/结果显示/, /结果如下/, /已找到/, /已获取/, /已完成/, /已经.*(写入|保存|创建|搜索|执行)/,
|
||||||
|
/从.*获取/, /已写入/, /已保存/, /已创建/, /已执行/,
|
||||||
|
/search result/, /found that/, /according to/i,
|
||||||
|
];
|
||||||
|
const isDescribingResult = resultIndicators.some(p => p.test(text));
|
||||||
|
|
||||||
|
// 意图声明模式 — 只有明确表达"将要做"时才视为"说了没做"
|
||||||
|
const intentPatterns = [
|
||||||
|
/我来.*(写入|写文件|创建|生成|保存|搜索|查找|抓取|运行|执行|下载)/,
|
||||||
|
/让我.*(写入|写文件|创建|生成|保存|搜索|查找|抓取|运行|执行|下载)/,
|
||||||
|
/我将.*(写入|写文件|创建|生成|保存|搜索|查找|抓取|运行|执行|下载)/,
|
||||||
|
/接下来.*(写入|写文件|创建|生成|保存|搜索|查找|抓取|运行|执行|下载)/,
|
||||||
|
/现在.*(写入|写文件|创建|生成|保存|搜索|查找|抓取|运行|执行|下载)/,
|
||||||
|
/需要.*(写入|写文件|创建|生成|保存|搜索|查找|抓取|运行|执行|下载)/,
|
||||||
|
/i('?)ll (write|create|search|fetch|run|execute|download)/i,
|
||||||
|
/let me (write|create|search|fetch|run|execute|download)/i,
|
||||||
|
/i will (write|create|search|fetch|run|execute|download)/i,
|
||||||
|
/i need to (write|create|search|fetch|run|execute|download)/i,
|
||||||
|
];
|
||||||
|
const hasIntentStatement = intentPatterns.some(p => p.test(text));
|
||||||
|
|
||||||
|
// 上一轮已调用的工具 — 本轮可能在总结结果,不触发
|
||||||
|
const lastLoopToolNames = (ctx.prevToolCalls || []).map(c => c.function.name);
|
||||||
|
|
||||||
|
// 触发条件:有意图声明(高置信度),或短回复中匹配关键词且非结果描述(低置信度)
|
||||||
|
const shouldCheck = hasIntentStatement || (contentLen < 300 && !isDescribingResult);
|
||||||
|
|
||||||
|
if (shouldCheck) {
|
||||||
const actionHints: Array<{ pattern: RegExp; tool: string; hint: string }> = [
|
const actionHints: Array<{ pattern: RegExp; tool: string; hint: string }> = [
|
||||||
{ pattern: /写入|写文件|写入文件|保存.*文件|创建.*文件|生成.*文件|输出.*文件|write.*file/i, tool: 'write_file', hint: '你提到了写入/创建文件,但没有调用 write_file 工具。请直接调用 write_file 工具执行写入操作。' },
|
{ pattern: /写入|写文件|写入文件|保存.*文件|创建.*文件|生成.*文件|输出.*文件|write.*file/i, tool: 'write_file', hint: '你提到了写入/创建文件,但没有调用 write_file 工具。请直接调用 write_file 工具执行写入操作。' },
|
||||||
{ pattern: /搜索|查找|搜一下|查一下|检索|search/i, tool: 'web_search', hint: '你提到了搜索,但没有调用 web_search 工具。请直接调用 web_search 工具执行搜索。' },
|
// web_search 正则保持宽泛,由 resultIndicators + lastLoopToolNames 两层防护避免误判
|
||||||
|
{ pattern: /搜索|搜一下|检索|search/i, tool: 'web_search', hint: '你提到了搜索,但没有调用 web_search 工具。请直接调用 web_search 工具执行搜索。' },
|
||||||
{ pattern: /抓取|获取.*内容|fetch|获取.*网页/i, tool: 'web_fetch', hint: '你提到了抓取网页,但没有调用 web_fetch 工具。请直接调用 web_fetch 工具获取网页内容。' },
|
{ pattern: /抓取|获取.*内容|fetch|获取.*网页/i, tool: 'web_fetch', hint: '你提到了抓取网页,但没有调用 web_fetch 工具。请直接调用 web_fetch 工具获取网页内容。' },
|
||||||
{ pattern: /运行.*命令|执行.*命令|run.*command/i, tool: 'run_command', hint: '你提到了运行命令,但没有调用 run_command 工具。请直接调用 run_command 工具执行命令。' },
|
{ pattern: /运行.*命令|执行.*命令|run.*command/i, tool: 'run_command', hint: '你提到了运行命令,但没有调用 run_command 工具。请直接调用 run_command 工具执行命令。' },
|
||||||
{ pattern: /下载/i, tool: 'download_file', hint: '你提到了下载,但没有调用 download_file 工具。请直接调用 download_file 工具执行下载。' },
|
{ pattern: /下载/i, tool: 'download_file', hint: '你提到了下载,但没有调用 download_file 工具。请直接调用 download_file 工具执行下载。' },
|
||||||
];
|
];
|
||||||
const matched = actionHints.find(h => h.pattern.test(text) && !ctx.allToolRecords.some(r => r.name === h.tool));
|
const matched = actionHints.find(h => {
|
||||||
|
if (!h.pattern.test(text)) return false;
|
||||||
|
if (ctx.allToolRecords.some(r => r.name === h.tool)) return false; // 整个会话已调用过
|
||||||
|
if (lastLoopToolNames.includes(h.tool)) return false; // 上一轮调用过,本轮可能在总结
|
||||||
|
return true;
|
||||||
|
});
|
||||||
if (matched) {
|
if (matched) {
|
||||||
logWarn(`Reflecting: 检测到"说了做但没做" → ${matched.tool},注入提醒`);
|
logWarn(`Reflecting: 检测到"说了做但没做" → ${matched.tool},注入提醒`);
|
||||||
ctx.messages.push({
|
ctx.messages.push({
|
||||||
@@ -2622,6 +2662,7 @@ async function handleReflecting(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 本轮调用了工具 → 模型需要处理工具结果 → 继续循环 ──
|
// ── 本轮调用了工具 → 模型需要处理工具结果 → 继续循环 ──
|
||||||
if (ctx.toolCalls.length > 0) {
|
if (ctx.toolCalls.length > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user