后端修复: Ollama adapter 移除死代码/修复超时硬编码/iteration硬编码/pullModel无超时/流异常断开补发DONE; SSE解析器支持非字符串arguments; Sandbox fail-closed安全加固; IPC移除未使用变量 新增功能: TaskOrchestrator子任务编排器; DelegateTaskTool委派工具; Agent Loop并行工具执行; 上下文自动压缩; 记忆检索注入System Prompt 前端修复: useAgentStream text_delta thought累积bug; 工具调用状态正确流转; 修复重复stateChange事件; TraceStep.thought正确填充
64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
/**
|
|
* Post-Tool Hooks — 工具执行后钩子
|
|
*
|
|
* 用途:结果验证、审计日志写入、记忆触发、错误恢复建议。
|
|
*
|
|
* @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第五章
|
|
*/
|
|
|
|
import type { MetonaToolCall, MetonaToolResult } from '../types';
|
|
import type { AuditService } from '../../services/audit.service';
|
|
import type { MemoryManager } from '../memory/manager';
|
|
|
|
export interface PostToolHook {
|
|
afterExecute(toolCall: MetonaToolCall, result: MetonaToolResult, sessionId: string): Promise<void>;
|
|
}
|
|
|
|
/** 审计日志钩子 */
|
|
export class AuditLogHook implements PostToolHook {
|
|
constructor(private auditService: AuditService) {}
|
|
|
|
async afterExecute(toolCall: MetonaToolCall, result: MetonaToolResult, sessionId: string): Promise<void> {
|
|
this.auditService.logToolCall({
|
|
sessionId,
|
|
iteration: toolCall.iteration,
|
|
toolName: toolCall.name,
|
|
args: toolCall.args,
|
|
outcome: result.success ? 'success' : 'error',
|
|
result: result.result,
|
|
error: result.error,
|
|
durationMs: result.durationMs,
|
|
});
|
|
}
|
|
}
|
|
|
|
/** 记忆触发钩子 */
|
|
export class MemoryTriggerHook implements PostToolHook {
|
|
/** 仅对搜索类工具触发记忆,避免每次 read_file 都产生低价值记忆条目 */
|
|
private memorableTools = ['web_search', 'memory_search'];
|
|
|
|
/** 单次工具结果存储上限(字符),防止过大内容淹没记忆系统 */
|
|
private readonly MAX_MEMORY_CONTENT = 500;
|
|
|
|
constructor(private memoryManager: MemoryManager) {}
|
|
|
|
async afterExecute(toolCall: MetonaToolCall, result: MetonaToolResult, sessionId: string): Promise<void> {
|
|
if (this.memorableTools.includes(toolCall.name) && result.success) {
|
|
const content = typeof result.result === 'string' ? result.result : JSON.stringify(result.result);
|
|
try {
|
|
this.memoryManager.store({
|
|
type: 'episodic',
|
|
content: `Tool ${toolCall.name} returned: ${content.slice(0, this.MAX_MEMORY_CONTENT)}`,
|
|
source: 'tool_result',
|
|
sessionId,
|
|
importance: 0.6,
|
|
});
|
|
} catch (error) {
|
|
// 记忆存储失败不应影响工具执行结果
|
|
// eslint-disable-next-line no-console
|
|
console.error('[MemoryTriggerHook] Failed to store episodic memory:', error);
|
|
}
|
|
}
|
|
}
|
|
}
|