diff --git a/src/main/services/ai/human/prompts/brain.ts b/src/main/services/ai/human/prompts/brain.ts index 1b425383..d04677f6 100644 --- a/src/main/services/ai/human/prompts/brain.ts +++ b/src/main/services/ai/human/prompts/brain.ts @@ -171,32 +171,22 @@ ${renderRoundResult(record)} `.trim() const renderRoundSection = (input: { - intro: string records: HumanRoundRecord[] detail: 'focus' | 'chain' | 'loaded' }): string => - ` -${input.intro} - -${ input.records.length > 0 ? input.records.map((record) => renderRoundRecord(record, input.detail)).join('\n\n') : 'none' -} -`.trim() -const renderLoadedHistorySection = ( +const renderLoadedHistoryText = ( loadedHistoryContext: HumanLoadedHistoryContext | undefined -): string => { - const intro = loadedHistoryContext - ? `这是你主动加载的更详细历史。只有在你明确调用 history-recall 后才会出现。 -本次过滤条件:roundIds=${renderInlineList( - (loadedHistoryContext.filters.roundIds || []).map(String), - { - separator: '、', - fallback: 'none' - } - )};executionKinds=${renderInlineList(loadedHistoryContext.filters.executionKinds || [], { +): string => + loadedHistoryContext + ? ` +过滤条件:roundIds=${renderInlineList((loadedHistoryContext.filters.roundIds || []).map(String), { + separator: '、', + fallback: 'none' + })};executionKinds=${renderInlineList(loadedHistoryContext.filters.executionKinds || [], { separator: '、', fallback: 'none' })};toolNames=${renderInlineList(loadedHistoryContext.filters.toolNames || [], { @@ -205,15 +195,15 @@ const renderLoadedHistorySection = ( })};watchHintCodes=${renderInlineList(loadedHistoryContext.filters.watchHintCodes || [], { separator: '、', fallback: 'none' - })};limit=${String(loadedHistoryContext.filters.limit || loadedHistoryContext.rounds.length)}。` - : '这是你主动回看后加载的历史细节。只有在明确回看历史后这里才会出现内容。' + })};limit=${String(loadedHistoryContext.filters.limit || loadedHistoryContext.rounds.length)} - return renderRoundSection({ - intro, - records: loadedHistoryContext?.rounds || [], - detail: 'loaded' - }) -} +${renderRoundSection({ + records: loadedHistoryContext.rounds, + detail: 'loaded' +})} +` + .trim() + : 'none' const renderAvailableSkillsSection = (state: HumanState): string => { const effectiveSkillIds = getEffectiveLearnedSkillIds(state.skills.learnedSkillIds) @@ -235,8 +225,6 @@ const renderAvailableSkillsSection = (state: HumanState): string => { .map(renderLearnedToolSpecLine) return ` -这是你当前已经可直接依赖、或仍可补充学习的技能与工具信息。 - 始终可用的技能: ${ listBuiltinSkillDefinitions() @@ -283,7 +271,7 @@ export const buildBrainPrompt = (input: { state: HumanState; ctx: AiRunContext } - 你会收到任务、记忆、技能、历史和提示等不同类型的信息。 - 这些信息是提供给你判断当前局面的参考材料,不是固定流程。 - 你需要自行判断哪些信息重要、哪些信息只是背景,以及下一步最合适的动作。 - - 除了明确写出的职责边界、输出规则和硬约束,不要把上下文说明理解成固定操作指令。 + - 除了明确写出的职责边界、输出契约和硬约束,不要把上下文说明理解成固定操作指令。 @@ -298,39 +286,54 @@ export const buildBrainPrompt = (input: { state: HumanState; ctx: AiRunContext } - 选择 mouth 时,readyToDeliver 必须为 true。 - - 你必须只输出 JSON,不要输出任何额外说明。 + + - task_goal:用户原始诉求与限制。 + - reference_scope:当前工作区、默认知识范围、显式缩圈范围和可用边界。 + - your_notes:稳定任务笔记,按“已确认 / 关键依据 / 未决问题”组织。 + - recent_progress:最近一轮完整过程记录,用于判断刚做了什么、得到什么。 + - earlier_attempts:更早轮次过程链,用于识别重复路径、失败模式和已经验证过的动作。 + - recalled_history:主动回看的详细历史;只有明确回看后才会出现,并可能附带过滤条件。 + - available_skills:当前已生效或仍可学习的 skills,以及当前可直接调用的工具说明。 + - correction_hint:当前额外的收敛或纠偏提示;若无则为 none。 + - 输出结构固定为: - { - "updatedStableMemory": { - "confirmedFacts": "string", - "keyEvidence": "string", - "openQuestions": "string" - }, - "decision": { - "summary": "string", - "reason": "string", - "expectedOutcome": "string" - }, - "readyToDeliver": false, - "nextAction": - | { "action": "learning", "wantLearnSkillIds": ["skill-id"] } - | { "action": "hand", "toolUsePlan": { "mode": "parallel", "calls": [{ "toolName": "tool_name", "args": {} }] } } - | { "action": "mouth" } - } - - 字段解释: - - updatedStableMemory:可选对象。表示本轮对任务笔记的局部更新。 - - updatedStableMemory 缺失:表示本轮不更新任务笔记。 - - updatedStableMemory 出现但某个字段缺失:表示该字段保持原值。 - - updatedStableMemory 的字段值是字符串:表示用该字符串覆盖对应字段。 - - updatedStableMemory 的字段值是空字符串:表示显式清空对应字段。 - - decision:对象。表示你本轮的正式判断。 - - readyToDeliver:布尔值。只有当当前已经具备最终交付所需的信息、证据和产物时,才为 true。 - - nextAction:对象。表示下一跳动作,且只能输出 learning、hand、mouth 三种结构之一。 - - 不要输出 markdown。 - + + - 你必须只输出 JSON,不要输出任何额外说明,也不要输出 markdown。 + - 输出顶层结构固定为: + { + "updatedStableMemory"?: { + "confirmedFacts"?: "string", + "keyEvidence"?: "string", + "openQuestions"?: "string" + }, + "decision": { + "summary": "string", + "reason": "string", + "expectedOutcome": "string" + }, + "readyToDeliver": "boolean", + "nextAction": + | { "action": "learning", "wantLearnSkillIds": ["skill-id"] } + | { "action": "hand", "toolUsePlan": { "mode": "parallel|serial", "calls": [{ "toolName": "tool_name", "args": {} }] } } + | { "action": "mouth" } + } + - updatedStableMemory 是可选的 partial patch;缺失表示本轮不更新任务笔记。 + - updatedStableMemory 中某个字段缺失:表示该字段保持原值。 + - updatedStableMemory 中某个字段为空字符串:表示显式清空该字段。 + - updatedStableMemory.confirmedFacts:只写当前已经确认、可以直接成立的事实。 + - updatedStableMemory.keyEvidence:只写支撑事实的关键依据、证据来源或命中摘要。 + - updatedStableMemory.openQuestions:只写仍阻碍收敛的未知点、限制或待确认项。 + - decision.summary:一句话概括你本轮的判断或下一步意图。 + - decision.reason:说明你为什么这样判断,必须基于已给上下文、记忆或真实结果。 + - decision.expectedOutcome:说明你希望下一跳拿到什么结果,帮助后续节点收敛。 + - readyToDeliver=true 只表示当前已经具备最终交付所需的信息、证据和产物。 + - nextAction=learning:当你已经知道缺少哪类能力说明,且补齐后能明显降低不确定性时选择。 + - nextAction=hand:当下一步已经明确成一批工具调用,且你能给出完整合法的 toolUsePlan 时选择。 + - hand.toolUsePlan.mode:parallel 只用于只读且彼此独立、并行安全的调用;其余情况必须用 serial。 + - hand.toolUsePlan.calls[*].toolName:必须严格使用当前工具说明里的精确名称。 + - hand.toolUsePlan.calls[*].args:必须是对象,字段名、必填参数和参数层级都必须与工具说明一致。 + - nextAction=mouth:只在 readyToDeliver=true 且当前已经足够形成最终交付时选择。 + @@ -348,6 +351,31 @@ export const buildBrainPrompt = (input: { state: HumanState; ctx: AiRunContext } } + + { + "decision": { + "summary": "下一步已经明确,准备执行真实工具调用。", + "reason": "当前已具备所需能力,且已经能写出完整合法的工具参数。", + "expectedOutcome": "拿到当前工作区路径,供后续判断继续使用。" + }, + "readyToDeliver": false, + "nextAction": { + "action": "hand", + "toolUsePlan": { + "mode": "serial", + "calls": [ + { + "toolName": "terminal_run_command", + "args": { + "command": "pwd" + } + } + ] + } + } + } + + { "updatedStableMemory": { @@ -376,23 +404,16 @@ ${input.state.task.userInput} -这是当前任务的资料范围、工作区边界与默认可检索范围说明。 -它描述当前任务默认可以接触的资料范围,以及用户是否已经显式缩圈。 - ${renderReferenceScopeText(input.ctx.referenceScope)} -这是你之前沉淀下来的任务笔记。 -它记录当前任务中已经确认的事实、关键依据和仍未解决的问题。 - ${renderHumanStableNotes(input.state, 'brain_prompt')} ${renderRoundSection({ - intro: '这是最近一轮过程记录。', records: getLastRoundFocus(input.state) ? [getLastRoundFocus(input.state)!] : [], detail: 'focus' })} @@ -400,14 +421,13 @@ ${renderRoundSection({ ${renderRoundSection({ - intro: '这是更早几轮过程记录。', records: getHistoryChainRounds(input.state), detail: 'chain' })} -${renderLoadedHistorySection(getLoadedHistoryContext(input.state))} +${renderLoadedHistoryText(getLoadedHistoryContext(input.state))} @@ -416,8 +436,6 @@ ${renderAvailableSkillsSection(input.state)} -这是当前的收敛或纠偏提示。 - ${input.state.execution.watchHint ? renderWatchHintForBrain(input.state.execution.watchHint) : 'none'} diff --git a/src/main/services/ai/human/prompts/mouth.ts b/src/main/services/ai/human/prompts/mouth.ts index c8ca3e28..487b4fe6 100644 --- a/src/main/services/ai/human/prompts/mouth.ts +++ b/src/main/services/ai/human/prompts/mouth.ts @@ -11,11 +11,7 @@ import { renderWatchHintForMouth } from './watchHints' const renderRecentProgressSection = (record: HumanRoundRecord | undefined): string => { if (!record) { - return ` -这是最近一轮过程记录。 - -none -`.trim() + return 'none' } const progressText = @@ -55,8 +51,6 @@ ${record.execution.toolResults.map((result) => `- \`${result.toolName}\`:${res : `- mouth 输出摘要:${record.execution.summary}` return ` -这是最近一轮过程记录。 - - 第 ${record.roundId} 轮判断:${record.decision.decision.summary} - 原因:${record.decision.decision.reason} - 预期结果:${record.decision.decision.expectedOutcome} @@ -83,6 +77,24 @@ export const buildMouthPrompt = (input: { state: HumanState; deliveryMode: Human 只把已经确认的信息组织成最终交付。 + + - final_decision 是 brain 已经确认的最终判断;你负责表达,不负责重新推翻或改写它。 + - your_notes 是当前任务的稳定事实来源,包括已确认事实、关键依据和未决问题;正文中的事实与依据优先从这里取。 + - recent_progress 只用于补充最近完成到哪里、最近结果如何,不必把内部过程完整复述出来。 + - runtime_artifacts 只允许准确引用已经给出的路径、文件名、状态和摘要,不要补编额外元数据。 + - correction_hint 只用于如实说明限制、阻断或未完成点,不要把它扩写成新的事实。 + + + + - delivery_mode:当前交付模式,决定结果面向谁,以及结果应该如何组织。 + - task_goal:用户原始诉求与限制。 + - final_decision:brain 已经确认的最终判断与收口方向。 + - your_notes:稳定任务笔记,按“已确认 / 关键依据 / 未决问题”组织。 + - recent_progress:最近一轮过程记录,用于补充当前完成到哪里。 + - runtime_artifacts:运行中已经产生的真实产物列表。 + - correction_hint:当前额外的收敛或纠偏提醒;若无则为 none。 + + 1. 如果任务已经完成,先直接给出结论,再给关键依据,再给结果或产物说明。 2. 如果当前结果有限,先说明已经完成到哪里,再说明限制和继续需要什么。 @@ -96,17 +108,14 @@ export const buildMouthPrompt = (input: { state: HumanState; deliveryMode: Human ${DELIVERY_MODE_PROMPT_RULES[input.deliveryMode]} - - 你必须只输出最终 markdown 正文,不要输出任何额外说明,不要包 JSON,不要包代码块。 - - - + + - 你必须只输出最终 markdown 正文,不要输出任何额外说明、JSON 或代码块。 - 不要引入新的事实、依据、步骤或承诺。 - 不要暴露内部字段名、节点名或系统术语。 - 如果依据不足,就保持保守表达,不要写成确定结论。 - 不要把未来可能执行的工具动作写成“后续会自动完成”或“已经在进行中”。 - 输出必须是完整结果,而不是零散句子或局部片段。 - + ${DELIVERY_MODE_PROMPT_EXAMPLES[input.deliveryMode]} diff --git a/tests/unit/ai/human-brain-history-prompt.test.ts b/tests/unit/ai/human-brain-history-prompt.test.ts index 4c79fe77..2d45b1d3 100644 --- a/tests/unit/ai/human-brain-history-prompt.test.ts +++ b/tests/unit/ai/human-brain-history-prompt.test.ts @@ -204,14 +204,19 @@ describe('ai unit brain prompt history blocks', () => { expect(prompt.user).toContain('优先参考资料:') expect(prompt.system).toContain('') expect(prompt.system).toContain('') - expect(prompt.system).toContain('') + expect(prompt.system).toContain('') + expect(prompt.system).toContain('') expect(prompt.system).not.toContain('') expect(prompt.system).not.toContain('') expect(prompt.system).not.toContain('human graph') expect(prompt.system).not.toContain('') - expect(prompt.user).toContain('这是你之前沉淀下来的任务笔记。') - expect(prompt.user).toContain('这是最近一轮过程记录。') - expect(prompt.user).toContain('这是更早几轮过程记录。') + expect(prompt.system).toContain('task_goal:用户原始诉求与限制。') + expect(prompt.system).toContain('recalled_history:主动回看的详细历史') + expect(prompt.user).not.toContain('这是用户这次直接提出的自然语言任务目标与限制。') + expect(prompt.user).not.toContain('这是你之前沉淀下来的任务笔记。') + expect(prompt.user).not.toContain('这是最近一轮最值得优先看的完整过程记录。') + expect(prompt.user).not.toContain('这是更早几轮的过程链。') + expect(prompt.user).not.toContain('这是当前的收敛或纠偏提示。') expect(prompt.user).toContain('history-recall') expect(prompt.user).toContain('决策了什么:') expect(prompt.user).toContain('做了什么:') diff --git a/tests/unit/ai/learning-tool-budget.test.ts b/tests/unit/ai/learning-tool-budget.test.ts index 9514c09c..2635424b 100644 --- a/tests/unit/ai/learning-tool-budget.test.ts +++ b/tests/unit/ai/learning-tool-budget.test.ts @@ -133,8 +133,11 @@ describe('ai unit learning tool budget', () => { expect(prompt).toContain('') expect(prompt).toContain('你只负责判断下一步') expect(prompt).toContain('learning 只负责补齐当前真正缺失的技能说明') - expect(prompt).toContain('') + expect(prompt).toContain('') + expect(prompt).toContain('') expect(prompt).toContain('updatedStableMemory') + expect(prompt).toContain('updatedStableMemory 是可选的 partial patch') + expect(prompt).toContain('hand.toolUsePlan.calls[*].args') expect(prompt).toContain('mouth 只负责整理最终结果,不新增事实,不继续执行工具') expect(prompt).not.toContain('') expect(prompt).not.toContain('') diff --git a/tests/unit/ai/mouth-ready-to-deliver.test.ts b/tests/unit/ai/mouth-ready-to-deliver.test.ts index 883cd6ba..6a7b327e 100644 --- a/tests/unit/ai/mouth-ready-to-deliver.test.ts +++ b/tests/unit/ai/mouth-ready-to-deliver.test.ts @@ -72,10 +72,35 @@ describe('ai unit mouth readyToDeliver guard', () => { deliveryMode: 'user_answer' }).system + expect(prompt).toContain('') + expect(prompt).toContain('') + expect(prompt).toContain('') + expect(prompt).toContain('final_decision 是 brain 已经确认的最终判断') + expect(prompt).toContain('runtime_artifacts 只允许准确引用已经给出的路径') expect(prompt).toContain('你不会调用工具,也不会继续推进未完成步骤') expect(prompt).toContain('不要把未来可能执行的工具动作写成“后续会自动完成”') expect(prompt).toContain('你必须只输出最终 markdown 正文') expect(prompt).not.toContain('"deliveryMarkdown"') expect(prompt).not.toContain('"summary"') }) + + it('keeps mouth user-context blocks data-only and moves explanations into system', () => { + const result = buildMouthPrompt({ + state: createInitialHumanState({ + taskId: 'task-2', + userInput: '请根据当前信息给我一个最终答复' + }), + deliveryMode: 'user_answer' + }) + + expect(result.system).toContain('delivery_mode:当前交付模式') + expect(result.system).toContain('final_decision:brain 已经确认的最终判断') + expect(result.user).not.toContain('这是当前交付模式。') + expect(result.user).not.toContain('这是用户这次原始诉求与限制。') + expect(result.user).not.toContain('这是 brain 已经做出的最终决定。') + expect(result.user).not.toContain('这是任务的稳定笔记') + expect(result.user).not.toContain('这是最近一轮最相关的过程记录。') + expect(result.user).not.toContain('这是运行中已经产生的真实产物列表。') + expect(result.user).not.toContain('这是当前额外的收敛或纠偏提醒。') + }) })