import type { AgentEvent, AgentStatus, Message, TokenUsage } from '../types.js'; import type { ToolDefinition } from '../tool/types.js'; import type { ToolRegistry } from '../tool/registry.js'; import type { AgentConfig, RunOptions } from './types.js'; import type { HookEvent, HookContext, HookResult } from '../hooks/types.js'; import type { ResponseFormat } from '../llm/types.js'; import { type ToolLoopState } from './tool-executor.js'; /** Agent Loop 运行时状态(在私有方法间传递) */ export interface LoopState extends ToolLoopState { toolDefs: ToolDefinition[]; /** 结构化输出约束(请求级 options.responseFormat 覆盖 Profile 级 config.responseFormat) */ responseFormat?: ResponseFormat; lastContent: string; totalUsage?: TokenUsage; /** 迭代预算提醒是否已注入(防止重复注入) */ iterationGuardInjected?: boolean; /** 连续仅有 tool_calls 无 content 的迭代计数(用于自动注入进度叙述提醒) */ consecutiveSilentIterations: number; /** 上一轮迭代是否执行了工具调用(用于检测 LLM 提前停止) */ lastIterationHadToolCalls: boolean; /** 已注入的继续提示次数(防止无限注入) */ continuationNudges: number; /** 数据获取循环提醒是否已注入(防止重复注入) */ dataFetchGuardInjected?: boolean; /** 连续全部工具失败的迭代计数(全局熔断:连续 N 轮所有工具都失败时强制终止) */ consecutiveAllFailIterations: number; /** 最终总结提示是否已注入(达到 maxIterations 后给 LLM 最后一次机会) */ finalSummaryInjected?: boolean; /** 是否发生过致命错误(用于 emitDone 传递 runStatus='error' 给 observability) */ hadFatalError?: boolean; /** 致命错误信息(配合 hadFatalError 传递给 observability trace.error) */ fatalErrorMsg?: string; /** 不含工具区的稳定系统提示词(每次 LLM 调用前追加动态工具区) */ stableSystemPrompt: string; } export declare class AgentEngine { private config; /** sessionId → AgentStatus */ private statusMap; /** sessionId → AbortController (internal) */ private abortMap; /** sessionId → steering messages(实时转向队列) */ private steeringMap; /** sessionId -> pause state (user pause: agent loop suspends at turn boundary until resume) */ private pauseMap; /** parentSessionId → Set(父子会话关系跟踪,用于级联 abort) */ private childSessionMap; /** childSessionId → parentSessionId(反向索引,用于快速查找父会话) */ private parentSessionMap; /** 被摘要化的工具名集合(progressive loading:这些工具的 prompt 不注入 system prompt) */ private summarizedToolNames; /** 子模块:历史管理器 */ private historyManager; /** 子模块:LLM 调用器 */ private llmCaller; /** 子模块:工具执行器 */ private toolExecutor; /** 历史摘要缓存:sessionId → { summary, lastSummarizedCount }(LRU 限制防止内存泄漏) */ private summaryCache; /** 摘要缓存上限 */ private static readonly SUMMARY_CACHE_MAX; /** 数据获取类工具集合(用于循环检测) */ private dataFetchTools; /** 产出型工具集合(有这些调用时不触发数据获取循环检测) */ private outputTools; /** 外部数据源获取工具集合(用于数据出处水印判定) */ private externalDataFetchTools; /** Guard 策略配置(阈值可注入,不硬编码业务逻辑) */ private guardConfig; /** 错误恢复策略配置 */ private errorRecovery; /** 为 ToolSectionBuilder 提供依赖 */ private get toolSectionDeps(); /** 为 GuardController 提供依赖 */ private get guardControllerDeps(); /** 为 SessionStateManager 提供依赖 */ private get sessionStateDeps(); constructor(config: AgentConfig); /** 获取当前 Agent 的 ToolRegistry(供 read_tool_doc 等内置工具使用) */ getToolRegistry(): ToolRegistry; /** 检查工具是否在当前 registry 中注册(含 deniedTools 排除) */ private hasTool; /** 动态调整最大迭代上限(用于子 Agent 场景,避免子任务消耗过多迭代) */ setMaxIterations(max: number): void; /** 获取当前 Agent 的 SkillManager(供 load_skill 等内置工具使用,确保 scope 内操作) */ getSkillManager(): import('../skill/manager.ts').SkillManager | undefined; /** * 注入会话历史(从外部恢复,如持久化存储) * 用于跨引擎实例保持对话上下文 */ setHistory(sessionId: string, messages: Message[]): void; /** * 获取会话历史副本(用于持久化保存) */ getHistorySnapshot(sessionId: string): Message[]; /** * 从持久化存储加载会话历史(重启后恢复 Agent 上下文) * 需要在 run() 之前调用 * @returns true 如果加载成功且有历史消息 */ loadHistory(sessionId: string): Promise; /** * 截断会话历史到指定位置(消息编辑场景) * @param sessionId 会话 ID * @param toIndex 保留的消息数量 * @returns 被截断掉的消息数量 */ truncateHistory(sessionId: string, toIndex: number): number; /** * 从现有会话分叉创建新会话(对话分支场景) * @param sourceSessionId 源会话 ID * @param targetSessionId 目标会话 ID * @param fromIndex 从源会话复制到哪个位置 * @returns 新会话中的消息数量 */ forkHistory(sourceSessionId: string, targetSessionId: string, fromIndex?: number): number; /** 获取会话历史的消息数量 */ getHistoryLength(sessionId: string): number; /** * 注册父子会话关系。 * * 典型场景:主 Agent 通过 delegate_task 工具派生子任务时, * 上层调用此方法将子会话与父会话关联。当父会话因异常终止时, * 如果配置了 `cascadeAbortChildren`,所有子会话也会被自动 abort。 * * @param parentSessionId 父会话 ID * @param childSessionId 子会话 ID */ linkChildSession(parentSessionId: string, childSessionId: string): void; /** * 解除父子会话关系(子会话完成后调用)。 */ unlinkChildSession(childSessionId: string): void; /** * 获取指定父会话的所有子会话 ID。 */ getChildSessions(parentSessionId: string): string[]; /** * 获取指定子会话的父会话 ID(如有)。 */ getParentSession(childSessionId: string): string | undefined; /** * 执行 Agent 任务(流式推送事件) * 上层通过 for-await-of 消费事件流 */ run(options: RunOptions): AsyncGenerator; /** 解析 Skill 路由,返回 Skill 内容、实际消息和路由命中状态 */ private resolveSkill; /** 构建初始消息列表(system + history + user) */ private buildInitialMessages; /** * 将对话历史压缩为摘要,用 XML 标签包裹, * 注入为 user 消息 + assistant 确认对。 * * 摘要是陈述句,当前消息是指令句,语义上天然区分。 * ≤2 条消息时直接拼接,省去 LLM 调用;LLM 失败时回退到简单拼接。 * * P0 优化:使用摘要缓存避免每次请求都做额外 LLM 调用。 * - 缓存命中(增量 ≤ 4 条)→ 复用缓存 + 追加增量,零 LLM 调用 * - 缓存未命中 → 使用快速拼接(不阻塞),异步刷新缓存供下次使用 * * @param history 历史消息列表 * @param currentText 当前用户消息文本(用于排除最后一条匹配的 user 消息,null 表示全部纳入摘要) * @param sessionId 会话 ID(用于摘要缓存) * @returns 摘要消息对 [user: ..., assistant: 确认] */ private buildHistoryMessages; /** * 通过轻量 LLM 调用将多轮对话历史压缩为 ≤800 字的陈述句摘要。 * LLM 失败时回退到简单拼接。 */ private summarizeHistory; /** * 异步刷新历史摘要缓存(fire-and-forget,不阻塞主流程) * * 在后台调用 LLM 生成摘要并缓存,下次请求可直接命中缓存。 * 如果 LLM 调用失败,不影响主流程(已有快速拼接兜底)。 */ private refreshSummaryCache; /** 获取工具定义列表(含预过滤 + 渐进式发现) */ private resolveToolDefs; /** * 将工具定义转为摘要形式(截断 description + 清空 parameters)。 * 实现抽离至 ./tool-section-builder.ts -> compaction.ts,保持单一来源。 */ private summarizeToolDef; /** 构建 ToolContext */ private buildToolContext; /** 汇总准备阶段,生成 LoopState */ private prepareRun; /** 动态构建工具区 prompt(Available Tools + Tool Usage Guidelines)。 * 从 state.toolDefs 现场拼装,确保与可执行工具集一致,消除静态穿透。 */ private buildDynamicToolSection; /** * 渲染工具提示中对其它工具的引用,避免「提示穿透」: * 文案中使用 `{{TOOL:name}}` 包裹对其它工具的引用。 * - 若该工具在当前能力集(available)中,渲染为 `name`; * - 若该工具未注册(不在 available,也未出现在 relatedTools 声明中),则整段(含该占位符的整行/整句)被剔除, * 避免诱导 LLM 调用不存在的工具。 * * @param text 原始文案 * @param available 当前可用的工具名集合(用于判定 {{TOOL:name}} 是否可见) * @param relatedTools 声明式引用清单(可选)。留空的占位符若不在 available 中则被剔除; * 显式声明在 relatedTools 中的工具名也可被识别为「已知引用」参与裁剪。 */ private renderToolRefs; /** 刷新 system prompt 中的工具区,确保与 state.toolDefs 一致 */ private refreshToolSection; /** 主循环:迭代调用 LLM → 执行工具 → 直到完成或达到上限 */ private agentLoop; /** * 数据出处水印判定。 * * 业界对准确性敏感数据的标准做法是「grounding + 出处透明」:能检索到就引用, * 检索不到就明确标注「未经核验」。此方法实现「执行层可强制」的出处透明—— * 不依赖模型自诚实(73.json 证明模型会一边写着不该编造、一边照样编)。 * * 触发条件(精准,避免误伤): * - 本轮**尝试过**外部数据获取工具 → 说明任务确实需要外部数据; * - 且这些调用**全部失败** → 最终产出的数据必然来自模型记忆(真实历史或编造,执行层无法区分); * - → 追加「未经实时核验」声明。 * /** * 对最终回复应用数据出处水印(幂等)。 * 仅当有实际文本内容、需要水印、且尚未包含水印标记时追加。 * * 触发(添加水印): * - 本轮调用过外部数据获取工具且全部失败 → 最终回复中的数据必然来自记忆 → 需要声明; * * 不触发(正确放行): * - 从未调用外部数据工具(纯本地/推理任务)→ 无需水印; * - 外部数据工具**有成功** → 数据已核验,无需水印。 */ private applyProvenanceNotice; /** * 生成兜底总结(当 LLM 调用失败或无内容输出时使用)。 * 从工具调用记录中提取摘要信息,确保用户始终能收到有意义的回复, * 而非仅看到 "Max iterations reached"。 */ private generateFallbackSummary; /** * 向运行中的 Agent 注入 steering 消息 * 消息将在下次 LLM 调用前注入对话上下文 */ steer(sessionId: string, message: string): boolean; /** 消费当前会话的 steering 消息,注入到 messages 中 */ private drainSteering; /** * 进度叙述提醒 * 当 LLM 连续 2 次迭代仅发出 tool_calls 而无 content 时,注入 user 消息提醒输出进度说明。 * 确保用户在对话中有可见的文字输出,而不是所有内容都收在执行步骤中。 */ private injectProgressNudge; /** * 迭代预算提醒 * 当剩余迭代不足 30% 时,注入 user 消息提醒 LLM 停止数据收集、转向完成任务。 * 解决 agent 在数据查询循环中耗尽迭代、永远无法进入文档生成/委托阶段的问题。 */ private injectIterationGuard; /** * 从可用工具列表中移除数据获取类工具,强制 LLM 转向产出。 * 在 iterationGuard / dataFetchGuard 触发后调用。 * 仅移除尚未执行的 toolDefs,不影响正在执行的工具。 */ private disableDataFetchTools; /** * 数据获取循环检测 + 连续失败熔断 * * 两类触发条件: * 1. 数据获取循环:最近 3+ 条工具调用全部是数据获取类且无产出型工具 * 2. 连续失败熔断:最近 5+ 条工具调用中 4+ 条失败,强制停止试错、转向产出 */ private checkDataFetchLoop; /** 注入数据获取循环/连续失败熔断提示 */ private injectDataFetchGuard; /** * 任务继续提示 * 当 LLM 在工具调用后提前停止(无新工具调用也无明确总结)时,注入提示让其继续完成剩余任务。 * 典型场景:多步委托任务中,第一步数据采集完成后 LLM 未生成后续步骤的 delegate_task。 */ private injectContinuationNudge; /** * 渐进式结果交付 * 当主 Agent 并行分发了多个 delegate_task 后,注入提示让 LLM 逐步呈现每个子任务的结果, * 而不是等所有结果全部返回后才开始输出。减少用户感知等待时间。 */ private injectProgressiveNudge; /** 执行指定事件类型的所有 Hook */ runHooks(event: HookEvent, ctx: HookContext): Promise; /** 执行中间件的 before/after 阶段 */ private runMiddlewarePhase; /** 执行中间件的 onToolBefore/onToolAfter 阶段 */ runMiddlewareToolPhase(phase: 'onToolBefore' | 'onToolAfter', ctx: HookContext): Promise; private emitDone; /** 中断指定会话的 Agent 执行 */ abort(sessionId: string): void; /** * 暂停指定会话的 Agent 执行:当前工具/LLM 调用继续完成, * agent loop 在下一轮 turn 边界优雅退出(发送 paused 事件后正常结束流)。 * 恢复由新建 SSE 流重建上下文实现(不唤醒本循环)。 * 对不存在的会话调用是幂等 no-op。 */ pause(sessionId: string): void; /** * 清理指定会话的暂停标志(恢复由新建 SSE 流完成,不再唤醒挂起循环)。 * 对未暂停的会话调用是幂等 no-op。 */ resume(sessionId: string): void; /** 查询指定会话是否处于暂停状态 */ isPaused(sessionId: string): boolean; /** * 中断指定会话及其所有子会话的 Agent 执行(级联 abort)。 * * 递归遍历子会话树,从叶子节点到根节点依次 abort。 * 用于确保父会话异常终止时,所有 delegate_task 派生的子任务也被终止。 */ abortCascade(sessionId: string): void; /** 查询指定会话的 Agent 状态 */ getStatus(sessionId: string): AgentStatus; /** 销毁引擎,释放所有会话资源 */ destroy(): void; /** * 统一的致命错误处理入口。 * * 由引擎内部(agentLoop、catch 块)在遇到不可恢复错误时调用。 * 按以下顺序执行: * 1. 调用 config.errorRecovery.onFatalError 回调(允许上层记录/通知) * 2. 如果 config.errorRecovery.autoAbortOnFatal,abort 当前会话 * 3. 如果 config.errorRecovery.cascadeAbortChildren,级联 abort 所有子会话 * * @param error 致命错误信息(含 sessionId) */ private handleFatalError; /** * 清理指定会话的父子关系记录。 * * 在会话自然完成或 abort 后调用,防止内存泄漏。 * 处理两个方向: * - 如果是父会话:移除所有子会话引用(但子会话若仍在运行则保留其关系) * - 如果是子会话:从父会话的子集中移除自身 */ private cleanupSessionRelations; createEvent(sessionId: string, event: Record): AgentEvent; private createNoopShell; } //# sourceMappingURL=engine.d.ts.map