diff --git a/src/main/services/ai/director/prompts/intent.ts b/src/main/services/ai/director/prompts/intent.ts index 38205e0e..cfb10556 100644 --- a/src/main/services/ai/director/prompts/intent.ts +++ b/src/main/services/ai/director/prompts/intent.ts @@ -1,33 +1,63 @@ import { - buildDirectorPromptUserContext, type IntentPromptInput, JSON_ONLY_INTENT_CONTRACT } from './shared' +import { renderLineList } from '../../utils/render' + +export const buildIntentSystemPrompt = (): string => + [ + '【角色】你是请求分流决策器。', + '【目标】在 need_context / to_plan / direct_human 三者中选择唯一模式。', + '【背景】你只能依据当前输入字段判断,不得使用未提供信息。', + '【流程】先判断信息是否不足,再判断是否需要本地工具执行,最后给出结构化决策。', + '【范围】若用户已显式选择参考资料,默认视为优先参考范围;涉及本地资料时优先走本地能力,不要忽略这些选择。', + '【约束】不得编造上下文;reason 必须说明判定依据。', + '【输出】', + JSON_ONLY_INTENT_CONTRACT, + '【样例-正】当用户请求“请把 A 文档改成模板并保存”且需要多个业务动作时,输出 mode=to_plan。', + '【样例-正】当用户请求“总结这篇文章并按要求输出”且单个 human 可闭环完成时,输出 mode=direct_human。', + '【样例-正】当用户问“继续刚才第二步”但缺少指代信息时,输出 mode=need_context。', + '【样例-反】不要输出“我觉得应该先查一下”这类自然语言。' + ].join('\n') + +export const buildIntentUserPrompt = (input: IntentPromptInput): string => { + const contextEvidenceText = renderLineList(input.contextEvidence, { + fallback: '(none)', + style: 'plain' + }) + + return ` + + +${input.userInput} + + + +${input.sessionSummaryText} + + + +${input.recentTurnsText} + + + +${input.selectedContextText} + + + +${contextEvidenceText} + + + +${input.contextRounds}/${input.maxContextRounds} + + +`.trim() +} export const buildIntentPrompt = (input: IntentPromptInput) => { return { - system: [ - '【角色】你是请求分流决策器。', - '【目标】在 need_context / to_plan / direct_human 三者中选择唯一模式。', - '【背景】你只能依据当前输入字段判断,不得使用未提供信息。', - '【流程】先判断信息是否不足,再判断是否需要本地工具执行,最后给出结构化决策。', - '【范围】若用户已显式选择参考资料,默认视为优先参考范围;涉及本地资料时优先走本地能力,不要忽略这些选择。', - '【约束】不得编造上下文;reason 必须说明判定依据。', - '【输出】', - JSON_ONLY_INTENT_CONTRACT, - '【样例-正】当用户请求“请把 A 文档改成模板并保存”且需要多个业务动作时,输出 mode=to_plan。', - '【样例-正】当用户请求“总结这篇文章并按要求输出”且单个 human 可闭环完成时,输出 mode=direct_human。', - '【样例-正】当用户问“继续刚才第二步”但缺少指代信息时,输出 mode=need_context。', - '【样例-反】不要输出“我觉得应该先查一下”这类自然语言。' - ].join('\n'), - user: buildDirectorPromptUserContext({ - ...input, - extraSections: [ - { - label: '当前 context rounds', - value: `${input.contextRounds}/${input.maxContextRounds}` - } - ] - }) + system: buildIntentSystemPrompt(), + user: buildIntentUserPrompt(input) } } diff --git a/src/main/services/ai/director/prompts/plan.ts b/src/main/services/ai/director/prompts/plan.ts index acada96e..ee87442e 100644 --- a/src/main/services/ai/director/prompts/plan.ts +++ b/src/main/services/ai/director/prompts/plan.ts @@ -1,24 +1,58 @@ import { - buildDirectorPromptUserContext, type PlanPromptInput, JSON_ONLY_PLAN_CONTRACT } from './shared' +import { renderLineList } from '../../utils/render' + +export const buildPlanSystemPrompt = (): string => + [ + '【角色】你是任务规划器。', + '【目标】把用户请求拆成可独立执行的任务列表。', + '【背景】执行层是本地工具执行模式,典型能力包括知识库检索、终端执行、docx 处理。', + '【运行时约束】最终总结由 runtime 自动追加 synthesis human 负责,不要把“总结答复”写成计划中的业务任务。', + '【范围】若已显式选择参考资料,任务规划必须优先围绕这些本地选择取证;不要默认扩大到未选中的本地资料。', + '【流程】先识别最终交付物,再拆分最小可行任务,最后检查任务是否可执行可验证。', + '【约束】每个 userInput 必须包含“动作 + 对象 + 完成判据”;禁止空泛任务。', + '【输出】', + JSON_ONLY_PLAN_CONTRACT, + '【样例-正】userInput: "读取 /docs/a.docx,提取行动项并输出 owner/due_date/task 字段"。', + '【样例-反】userInput: "继续优化文档质量"。' + ].join('\n') + +export const buildPlanUserPrompt = (input: PlanPromptInput): string => { + const contextEvidenceText = renderLineList(input.contextEvidence, { + fallback: '(none)', + style: 'plain' + }) + + return ` + + +${input.userInput} + + + +${input.sessionSummaryText} + + + +${input.recentTurnsText} + + + +${input.selectedContextText} + + + +${contextEvidenceText} + + +`.trim() +} export const buildPlanPrompt = (input: PlanPromptInput) => { return { - system: [ - '【角色】你是任务规划器。', - '【目标】把用户请求拆成可独立执行的任务列表。', - '【背景】执行层是本地工具执行模式,典型能力包括知识库检索、终端执行、docx 处理。', - '【运行时约束】最终总结由 runtime 自动追加 synthesis human 负责,不要把“总结答复”写成计划中的业务任务。', - '【范围】若已显式选择参考资料,任务规划必须优先围绕这些本地选择取证;不要默认扩大到未选中的本地资料。', - '【流程】先识别最终交付物,再拆分最小可行任务,最后检查任务是否可执行可验证。', - '【约束】每个 userInput 必须包含“动作 + 对象 + 完成判据”;禁止空泛任务。', - '【输出】', - JSON_ONLY_PLAN_CONTRACT, - '【样例-正】userInput: "读取 /docs/a.docx,提取行动项并输出 owner/due_date/task 字段"。', - '【样例-反】userInput: "继续优化文档质量"。' - ].join('\n'), - user: buildDirectorPromptUserContext(input) + system: buildPlanSystemPrompt(), + user: buildPlanUserPrompt(input) } } diff --git a/src/main/services/ai/director/prompts/shared.ts b/src/main/services/ai/director/prompts/shared.ts index d1e2ea4f..8a8721bd 100644 --- a/src/main/services/ai/director/prompts/shared.ts +++ b/src/main/services/ai/director/prompts/shared.ts @@ -1,5 +1,3 @@ -import { renderLineList, renderNamedSections } from '../../utils/render' - export const JSON_ONLY_INTENT_CONTRACT = [ '只输出 JSON,不得输出任何额外文字。', '格式:{"mode":"need_context|to_plan|direct_human","reason":"string"}。', @@ -26,29 +24,3 @@ export type IntentPromptInput = DirectorPromptSharedInput & { } export type PlanPromptInput = DirectorPromptSharedInput - -export const buildDirectorPromptUserContext = ( - input: DirectorPromptSharedInput & { - extraSections?: Array<{ label: string; value: string }> - } -) => - renderNamedSections( - [ - { label: '用户输入', value: input.userInput }, - { label: '会话摘要', value: input.sessionSummaryText }, - { label: '最近对话', value: input.recentTurnsText }, - { label: '用户选择的参考资料', value: input.selectedContextText }, - { - label: '上下文证据', - value: renderLineList(input.contextEvidence, { - fallback: '(none)', - style: 'plain' - }) - }, - ...(input.extraSections || []) - ], - { - fallback: '(none)', - style: 'plain' - } - ) diff --git a/src/main/services/ai/human/prompts/brain.ts b/src/main/services/ai/human/prompts/brain.ts index 3080504d..a1c7748e 100644 --- a/src/main/services/ai/human/prompts/brain.ts +++ b/src/main/services/ai/human/prompts/brain.ts @@ -1,6 +1,13 @@ import type { AiRunContext } from '../../types' -import type { HumanState } from '../types' -import { buildBrainPromptSections } from './brainContextSections' +import { renderLineList } from '../../utils/render' +import { getSkillDefinition } from '../skills' +import { + renderHumanEvidenceLines, + renderHumanFactLines, + renderHumanOpenQuestionLines +} from '../shared/memoryRenderers' +import { getToolPromptSpec } from '../tools/registry' +import type { HumanState, ToolPromptSpec } from '../types' type HumanBrainPromptInput = { userInput: string @@ -12,27 +19,150 @@ type HumanBrainPromptInput = { watchHintText: string } -export const buildBrainPromptContext = (input: { state: HumanState; ctx: AiRunContext }) => { - const sections = buildBrainPromptSections(input) - - return buildHumanBrainPrompt({ - userInput: input.state.task.userInput, - memorySnapshotText: sections.memorySnapshotText, - selectedContextText: sections.selectedContextText, - skillContextText: sections.skillContextText, - recentToolResultsText: sections.recentToolResultsText, - recentDecisionHistoryText: sections.recentDecisionHistoryText, - watchHintText: [ - '如果这里是 none,表示当前没有额外的收敛提醒;否则应优先考虑结束任务或纠偏。', - '', - input.state.watchHint || 'none' - ].join('\n') +const formatPlainList = (items: string[]): string => + renderLineList(items, { + fallback: 'none', + style: 'plain' }) + +const renderLearnedToolSpecLine = (spec: ToolPromptSpec): string => { + const requiredArgs = spec.args + .filter((arg) => arg.required) + .map((arg) => `${arg.name}:${arg.type}`) + .join(', ') + const example = spec.examples?.[0] + ? `示例目标=${spec.examples[0].goal};示例参数键=[${Object.keys(spec.examples[0].args).join(', ') || 'none'}]` + : '示例=none' + + return `- 工具 ${spec.name}:适用场景=${spec.whenToUse};必填参数=[${requiredArgs || 'none'}];${example}` } -export const buildHumanBrainPrompt = (input: HumanBrainPromptInput) => { - return { - system: ` +const buildRecentToolResultLine = ( + result: NonNullable[number], + index: number +): string => { + const diagnosticText = result.diagnostic + ? `;diagnostic=${result.diagnostic.kind}${result.diagnostic.missingArgs?.length ? `;missingArgs=[${result.diagnostic.missingArgs.join(', ')}]` : ''}` + : '' + return `- 第${index + 1}项:工具 ${result.toolName} ${result.ok ? '执行成功' : '执行失败'}(code=${result.code});摘要:${result.summary}${diagnosticText}` +} + +const buildMemorySnapshotText = (state: HumanState): string => + [ + '下面是你之前沉淀下来的记忆。这些内容代表当前已经确认、可以直接复用或仍影响推进的信息。', + '', + '已确认的事实:', + renderHumanFactLines(state, 'brain_prompt'), + '', + '支撑这些事实的关键证据:', + renderHumanEvidenceLines(state, 'brain_prompt'), + '', + '仍未解决的问题:', + renderHumanOpenQuestionLines(state, 'brain_prompt') + ].join('\n') + +const buildSelectedContextPromptText = (ctx: AiRunContext): string => + [ + '下面是当前用户明确给出的参考资料上下文。后续判断、检索和文件定位都必须先对齐这段信息。', + '', + ctx.selectedContextText + ].join('\n') + +const buildSkillContextText = (state: HumanState): string => { + const learnedSkillIds = state.brainMemory.outside.learnedSkillIds + const learnedSkillDefinitions = learnedSkillIds.map((skillId) => getSkillDefinition(skillId)) + const learnedToolNames = Array.from( + new Set(learnedSkillDefinitions.flatMap((definition) => definition.toolNames)) + ) + const learnedToolSpecLines = + learnedToolNames.length > 0 + ? learnedToolNames + .map((toolName) => getToolPromptSpec(toolName)) + .filter((spec): spec is ToolPromptSpec => Boolean(spec)) + .map(renderLearnedToolSpecLine) + : ['none'] + + const skillCatalogLines = + state.brainMemory.outside.skillCatalog.length > 0 + ? state.brainMemory.outside.skillCatalog.map( + (item) => + `- ${item.skillId}:${item.summary};可提供工具=[${item.toolNames.join(', ')}];适用场景=${item.whenToUse}` + ) + : ['none'] + + const learnedSkillIdLines = + learnedSkillIds.length > 0 ? learnedSkillIds.map((skillId) => `- ${skillId}`) : ['none'] + + const learnedSkillDetailLines = + learnedSkillDefinitions.length > 0 + ? learnedSkillDefinitions.map((definition) => `- ${definition.skillId}:${definition.detail}`) + : ['none'] + + return [ + '下面是当前可用的技能信息。技能目录摘要只用于判断要不要学习;只有已学习 skill 的详细说明和工具要点,才代表你当前可以直接依赖的能力。', + '', + '当前可用技能目录摘要:', + formatPlainList(skillCatalogLines), + '', + '已经学习的 skill id:', + formatPlainList(learnedSkillIdLines), + '', + '已学习技能的详细说明:', + formatPlainList(learnedSkillDetailLines), + '', + '已学习技能对应的工具使用要点:', + formatPlainList(learnedToolSpecLines) + ].join('\n') +} + +const buildRecentToolResultsText = (state: HumanState): string => + [ + '下面是最近一轮工具调用刚带回来的新信息。请把这些结果和已有记忆一起判断,不要重复做同样的动作。', + '', + renderLineList( + (state.lastParallelCallToolResults || []).map((result, index) => + buildRecentToolResultLine(result, index) + ), + { + fallback: 'none', + style: 'plain' + } + ) + ].join('\n') + +const buildRecentDecisionHistoryText = (state: HumanState): string => + [ + '下面是最近几轮已经做过的正式判断摘要,只用于帮助你理解时间顺序并避免重复决策。', + '', + renderLineList( + state.brainMemory.outside.brainDecisions.map((record, index) => { + const ordinal = index + 1 + const action = record.nextAction.action + const nextActionPayload = + action === 'learning' + ? `wantLearnSkillIds=[${record.nextAction.wantLearnSkillIds.join(',')}]` + : action === 'hand' + ? `parallelCallToolParam=${JSON.stringify(record.nextAction.parallelCallToolParam)}` + : 'final_delivery=true' + + return `- 第${ordinal}轮(createdAt=${record.createdAt}):决定“${record.decision.summary}”;原因:${record.decision.reason};预期结果:${record.decision.expectedOutcome};nextAction.action=${action};nextAction数据:${nextActionPayload}` + }), + { + fallback: 'none', + style: 'plain' + } + ) + ].join('\n') + +const buildWatchHintText = (state: HumanState): string => + [ + '如果这里是 none,表示当前没有额外的收敛提醒;否则应优先考虑结束任务或纠偏。', + '', + state.watchHint || 'none' + ].join('\n') + +export const buildBrainSystemPrompt = (): string => + ` 你现在扮演一个负责分析情况、整理记忆、做出下一步判断的大脑角色。 @@ -206,8 +336,10 @@ export const buildHumanBrainPrompt = (input: HumanBrainPromptInput) => { -`.trim(), - user: ` +`.trim() + +export const buildBrainUserPrompt = (input: HumanBrainPromptInput): string => + ` ${input.userInput} @@ -238,5 +370,22 @@ ${input.watchHintText} `.trim() + +export const buildBrainPromptContext = (input: { state: HumanState; ctx: AiRunContext }) => { + return buildHumanBrainPrompt({ + userInput: input.state.task.userInput, + memorySnapshotText: buildMemorySnapshotText(input.state), + selectedContextText: buildSelectedContextPromptText(input.ctx), + skillContextText: buildSkillContextText(input.state), + recentToolResultsText: buildRecentToolResultsText(input.state), + recentDecisionHistoryText: buildRecentDecisionHistoryText(input.state), + watchHintText: buildWatchHintText(input.state) + }) +} + +export const buildHumanBrainPrompt = (input: HumanBrainPromptInput) => { + return { + system: buildBrainSystemPrompt(), + user: buildBrainUserPrompt(input) } } diff --git a/src/main/services/ai/human/prompts/brainContextSections.ts b/src/main/services/ai/human/prompts/brainContextSections.ts deleted file mode 100644 index a1d996f0..00000000 --- a/src/main/services/ai/human/prompts/brainContextSections.ts +++ /dev/null @@ -1,182 +0,0 @@ -import type { AiRunContext } from '../../types' -import type { HumanState, ToolPromptSpec } from '../types' -import { getSkillDefinition } from '../skills' -import { getToolPromptSpec } from '../tools/registry' -import { renderLineList } from '../../utils/render' -import { - renderHumanEvidenceLines, - renderHumanFactLines, - renderHumanOpenQuestionLines -} from '../shared/memoryRenderers' - -const formatList = (items: string[]): string => - renderLineList(items, { - fallback: 'none', - style: 'plain' - }) - -type BrainPromptRenderView = { - factLines: string - evidenceLines: string - openQuestionLines: string - selectedContextText: string - skillCatalogLines: string[] - learnedSkillIdLines: string[] - learnedSkillDetailLines: string[] - learnedToolSpecLines: string[] - recentToolResultLines: string - recentDecisionHistoryLines: string -} - -export type BrainPromptSections = { - memorySnapshotText: string - selectedContextText: string - skillContextText: string - recentToolResultsText: string - recentDecisionHistoryText: string -} - -const renderLearnedToolSpecLine = (spec: ToolPromptSpec): string => { - const requiredArgs = spec.args - .filter((arg) => arg.required) - .map((arg) => `${arg.name}:${arg.type}`) - .join(', ') - const example = spec.examples?.[0] - ? `示例目标=${spec.examples[0].goal};示例参数键=[${Object.keys(spec.examples[0].args).join(', ') || 'none'}]` - : '示例=none' - - return `- 工具 ${spec.name}:适用场景=${spec.whenToUse};必填参数=[${requiredArgs || 'none'}];${example}` -} - -const buildRecentToolResultLine = ( - result: NonNullable[number], - index: number -): string => { - const diagnosticText = result.diagnostic - ? `;diagnostic=${result.diagnostic.kind}${result.diagnostic.missingArgs?.length ? `;missingArgs=[${result.diagnostic.missingArgs.join(', ')}]` : ''}` - : '' - return `- 第${index + 1}项:工具 ${result.toolName} ${result.ok ? '执行成功' : '执行失败'}(code=${result.code});摘要:${result.summary}${diagnosticText}` -} - -const buildBrainPromptRenderView = (input: { - state: HumanState - ctx: AiRunContext -}): BrainPromptRenderView => { - const { state, ctx } = input - const learnedSkillIds = state.brainMemory.outside.learnedSkillIds - const learnedSkillDefinitions = learnedSkillIds.map((skillId) => getSkillDefinition(skillId)) - const learnedToolNames = Array.from( - new Set(learnedSkillDefinitions.flatMap((definition) => definition.toolNames)) - ) - const learnedToolSpecLines = - learnedToolNames.length > 0 - ? learnedToolNames - .map((toolName) => getToolPromptSpec(toolName)) - .filter((spec): spec is ToolPromptSpec => Boolean(spec)) - .map(renderLearnedToolSpecLine) - : ['none'] - - const recentToolResultLines = renderLineList( - (state.lastParallelCallToolResults || []).map((result, index) => - buildRecentToolResultLine(result, index) - ), - { - fallback: 'none', - style: 'plain' - } - ) - - const recentDecisionHistoryLines = renderLineList( - state.brainMemory.outside.brainDecisions.map((record, index) => { - const ordinal = index + 1 - const action = record.nextAction.action - const nextActionPayload = - action === 'learning' - ? `wantLearnSkillIds=[${record.nextAction.wantLearnSkillIds.join(',')}]` - : action === 'hand' - ? `parallelCallToolParam=${JSON.stringify(record.nextAction.parallelCallToolParam)}` - : 'final_delivery=true' - - return `- 第${ordinal}轮(createdAt=${record.createdAt}):决定“${record.decision.summary}”;原因:${record.decision.reason};预期结果:${record.decision.expectedOutcome};nextAction.action=${action};nextAction数据:${nextActionPayload}` - }), - { - fallback: 'none', - style: 'plain' - } - ) - - return { - factLines: renderHumanFactLines(state, 'brain_prompt'), - evidenceLines: renderHumanEvidenceLines(state, 'brain_prompt'), - openQuestionLines: renderHumanOpenQuestionLines(state, 'brain_prompt'), - selectedContextText: ctx.selectedContextText, - skillCatalogLines: - state.brainMemory.outside.skillCatalog.length > 0 - ? state.brainMemory.outside.skillCatalog.map( - (item) => - `- ${item.skillId}:${item.summary};可提供工具=[${item.toolNames.join(', ')}];适用场景=${item.whenToUse}` - ) - : ['none'], - learnedSkillIdLines: - learnedSkillIds.length > 0 ? learnedSkillIds.map((skillId) => `- ${skillId}`) : ['none'], - learnedSkillDetailLines: - learnedSkillDefinitions.length > 0 - ? learnedSkillDefinitions.map((definition) => `- ${definition.skillId}:${definition.detail}`) - : ['none'], - learnedToolSpecLines, - recentToolResultLines, - recentDecisionHistoryLines - } -} - -export const buildBrainPromptSections = (input: { - state: HumanState - ctx: AiRunContext -}): BrainPromptSections => { - const view = buildBrainPromptRenderView(input) - - return { - memorySnapshotText: [ - '下面是你之前沉淀下来的记忆。这些内容代表当前已经确认、可以直接复用或仍影响推进的信息。', - '', - '已确认的事实:', - view.factLines, - '', - '支撑这些事实的关键证据:', - view.evidenceLines, - '', - '仍未解决的问题:', - view.openQuestionLines - ].join('\n'), - selectedContextText: [ - '下面是当前用户明确给出的参考资料上下文。后续判断、检索和文件定位都必须先对齐这段信息。', - '', - view.selectedContextText - ].join('\n'), - skillContextText: [ - '下面是当前可用的技能信息。技能目录摘要只用于判断要不要学习;只有已学习 skill 的详细说明和工具要点,才代表你当前可以直接依赖的能力。', - '', - '当前可用技能目录摘要:', - formatList(view.skillCatalogLines), - '', - '已经学习的 skill id:', - formatList(view.learnedSkillIdLines), - '', - '已学习技能的详细说明:', - formatList(view.learnedSkillDetailLines), - '', - '已学习技能对应的工具使用要点:', - formatList(view.learnedToolSpecLines) - ].join('\n'), - recentToolResultsText: [ - '下面是最近一轮工具调用刚带回来的新信息。请把这些结果和已有记忆一起判断,不要重复做同样的动作。', - '', - view.recentToolResultLines - ].join('\n'), - recentDecisionHistoryText: [ - '下面是最近几轮已经做过的正式判断摘要,只用于帮助你理解时间顺序并避免重复决策。', - '', - view.recentDecisionHistoryLines - ].join('\n') - } -} diff --git a/src/main/services/ai/human/prompts/mouth.ts b/src/main/services/ai/human/prompts/mouth.ts index 06995227..7afdca6c 100644 --- a/src/main/services/ai/human/prompts/mouth.ts +++ b/src/main/services/ai/human/prompts/mouth.ts @@ -30,26 +30,8 @@ const buildFinalDecisionText = (state: HumanState): string => ? `最终决定:“${state.lastBrainDecision.decision.summary}”;原因:${state.lastBrainDecision.decision.reason};预期结果:${state.lastBrainDecision.decision.expectedOutcome}` : 'none' -export const buildMouthPromptContext = (input: { - state: HumanState - deliveryMode: HumanDeliveryMode -}) => { - return buildHumanMouthPrompt({ - deliveryMode: input.deliveryMode, - userInput: input.state.task.userInput, - finalDecisionText: buildFinalDecisionText(input.state), - factsText: renderHumanFactLines(input.state, 'mouth_prompt'), - evidencesText: renderHumanEvidenceLines(input.state, 'mouth_prompt'), - openQuestionsText: renderHumanOpenQuestionLines(input.state, 'mouth_prompt'), - recentToolResultsText: renderHumanRecentToolResultLines(input.state, 'prompt'), - runtimeArtifactsText: renderHumanArtifactLines(input.state, 'prompt'), - watchHintText: input.state.watchHint || 'none' - }) -} - -export const buildHumanMouthPrompt = (input: HumanMouthPromptInput) => { - return { - system: ` +export const buildMouthSystemPrompt = (deliveryMode: HumanDeliveryMode): string => + ` 你现在扮演一个负责对外表达最终结果的人。 @@ -71,7 +53,7 @@ export const buildHumanMouthPrompt = (input: HumanMouthPromptInput) => { -${buildDeliveryModePromptRules(input.deliveryMode)} +${buildDeliveryModePromptRules(deliveryMode)} @@ -97,11 +79,13 @@ ${buildDeliveryModePromptRules(input.deliveryMode)} -${buildDeliveryModePromptExample(input.deliveryMode)} +${buildDeliveryModePromptExample(deliveryMode)} -`.trim(), - user: ` +`.trim() + +export const buildMouthUserPrompt = (input: HumanMouthPromptInput): string => + ` ${input.deliveryMode} @@ -140,5 +124,27 @@ ${input.watchHintText} `.trim() + +export const buildMouthPromptContext = (input: { + state: HumanState + deliveryMode: HumanDeliveryMode +}) => { + return buildHumanMouthPrompt({ + deliveryMode: input.deliveryMode, + userInput: input.state.task.userInput, + finalDecisionText: buildFinalDecisionText(input.state), + factsText: renderHumanFactLines(input.state, 'mouth_prompt'), + evidencesText: renderHumanEvidenceLines(input.state, 'mouth_prompt'), + openQuestionsText: renderHumanOpenQuestionLines(input.state, 'mouth_prompt'), + recentToolResultsText: renderHumanRecentToolResultLines(input.state, 'prompt'), + runtimeArtifactsText: renderHumanArtifactLines(input.state, 'prompt'), + watchHintText: input.state.watchHint || 'none' + }) +} + +export const buildHumanMouthPrompt = (input: HumanMouthPromptInput) => { + return { + system: buildMouthSystemPrompt(input.deliveryMode), + user: buildMouthUserPrompt(input) } }