Refine human prompt context and output contracts
This commit is contained in:
1 parent
d5b36e7d9c
commit
cc6fc10dd8
5 files changed
+150
-90
No files matched your search
@@ -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 }
|
||||
- 你会收到任务、记忆、技能、历史和提示等不同类型的信息。
|
||||
- 这些信息是提供给你判断当前局面的参考材料,不是固定流程。
|
||||
- 你需要自行判断哪些信息重要、哪些信息只是背景,以及下一步最合适的动作。
|
||||
- 除了明确写出的职责边界、输出规则和硬约束,不要把上下文说明理解成固定操作指令。
|
||||
- 除了明确写出的职责边界、输出契约和硬约束,不要把上下文说明理解成固定操作指令。
|
||||
</autonomy_principles>
|
||||
|
||||
<action_boundaries>
|
||||
@@ -298,39 +286,54 @@ export const buildBrainPrompt = (input: { state: HumanState; ctx: AiRunContext }
|
||||
- 选择 mouth 时,readyToDeliver 必须为 true。
|
||||
</action_boundaries>
|
||||
|
||||
<output_rules>
|
||||
你必须只输出 JSON,不要输出任何额外说明。
|
||||
<context_block_guide>
|
||||
- task_goal:用户原始诉求与限制。
|
||||
- reference_scope:当前工作区、默认知识范围、显式缩圈范围和可用边界。
|
||||
- your_notes:稳定任务笔记,按“已确认 / 关键依据 / 未决问题”组织。
|
||||
- recent_progress:最近一轮完整过程记录,用于判断刚做了什么、得到什么。
|
||||
- earlier_attempts:更早轮次过程链,用于识别重复路径、失败模式和已经验证过的动作。
|
||||
- recalled_history:主动回看的详细历史;只有明确回看后才会出现,并可能附带过滤条件。
|
||||
- available_skills:当前已生效或仍可学习的 skills,以及当前可直接调用的工具说明。
|
||||
- correction_hint:当前额外的收敛或纠偏提示;若无则为 none。
|
||||
</context_block_guide>
|
||||
|
||||
输出结构固定为:
|
||||
{
|
||||
"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。
|
||||
</output_rules>
|
||||
<output_contract>
|
||||
- 你必须只输出 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 且当前已经足够形成最终交付时选择。
|
||||
</output_contract>
|
||||
|
||||
<examples>
|
||||
<example_keep_notes>
|
||||
@@ -348,6 +351,31 @@ export const buildBrainPrompt = (input: { state: HumanState; ctx: AiRunContext }
|
||||
}
|
||||
</example_keep_notes>
|
||||
|
||||
<example_tool_plan>
|
||||
{
|
||||
"decision": {
|
||||
"summary": "下一步已经明确,准备执行真实工具调用。",
|
||||
"reason": "当前已具备所需能力,且已经能写出完整合法的工具参数。",
|
||||
"expectedOutcome": "拿到当前工作区路径,供后续判断继续使用。"
|
||||
},
|
||||
"readyToDeliver": false,
|
||||
"nextAction": {
|
||||
"action": "hand",
|
||||
"toolUsePlan": {
|
||||
"mode": "serial",
|
||||
"calls": [
|
||||
{
|
||||
"toolName": "terminal_run_command",
|
||||
"args": {
|
||||
"command": "pwd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</example_tool_plan>
|
||||
|
||||
<example_partial_notes_update>
|
||||
{
|
||||
"updatedStableMemory": {
|
||||
@@ -376,23 +404,16 @@ ${input.state.task.userInput}
|
||||
</task_goal>
|
||||
|
||||
<reference_scope>
|
||||
这是当前任务的资料范围、工作区边界与默认可检索范围说明。
|
||||
它描述当前任务默认可以接触的资料范围,以及用户是否已经显式缩圈。
|
||||
|
||||
${renderReferenceScopeText(input.ctx.referenceScope)}
|
||||
</reference_scope>
|
||||
|
||||
<memory>
|
||||
<your_notes>
|
||||
这是你之前沉淀下来的任务笔记。
|
||||
它记录当前任务中已经确认的事实、关键依据和仍未解决的问题。
|
||||
|
||||
${renderHumanStableNotes(input.state, 'brain_prompt')}
|
||||
</your_notes>
|
||||
|
||||
<recent_progress>
|
||||
${renderRoundSection({
|
||||
intro: '这是最近一轮过程记录。',
|
||||
records: getLastRoundFocus(input.state) ? [getLastRoundFocus(input.state)!] : [],
|
||||
detail: 'focus'
|
||||
})}
|
||||
@@ -400,14 +421,13 @@ ${renderRoundSection({
|
||||
|
||||
<earlier_attempts>
|
||||
${renderRoundSection({
|
||||
intro: '这是更早几轮过程记录。',
|
||||
records: getHistoryChainRounds(input.state),
|
||||
detail: 'chain'
|
||||
})}
|
||||
</earlier_attempts>
|
||||
|
||||
<recalled_history>
|
||||
${renderLoadedHistorySection(getLoadedHistoryContext(input.state))}
|
||||
${renderLoadedHistoryText(getLoadedHistoryContext(input.state))}
|
||||
</recalled_history>
|
||||
</memory>
|
||||
|
||||
@@ -416,8 +436,6 @@ ${renderAvailableSkillsSection(input.state)}
|
||||
</available_skills>
|
||||
|
||||
<correction_hint>
|
||||
这是当前的收敛或纠偏提示。
|
||||
|
||||
${input.state.execution.watchHint ? renderWatchHintForBrain(input.state.execution.watchHint) : 'none'}
|
||||
</correction_hint>
|
||||
</user_context>
|
||||
|
||||
@@ -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
|
||||
只把已经确认的信息组织成最终交付。
|
||||
</working_style>
|
||||
|
||||
<source_of_truth>
|
||||
- final_decision 是 brain 已经确认的最终判断;你负责表达,不负责重新推翻或改写它。
|
||||
- your_notes 是当前任务的稳定事实来源,包括已确认事实、关键依据和未决问题;正文中的事实与依据优先从这里取。
|
||||
- recent_progress 只用于补充最近完成到哪里、最近结果如何,不必把内部过程完整复述出来。
|
||||
- runtime_artifacts 只允许准确引用已经给出的路径、文件名、状态和摘要,不要补编额外元数据。
|
||||
- correction_hint 只用于如实说明限制、阻断或未完成点,不要把它扩写成新的事实。
|
||||
</source_of_truth>
|
||||
|
||||
<context_block_guide>
|
||||
- delivery_mode:当前交付模式,决定结果面向谁,以及结果应该如何组织。
|
||||
- task_goal:用户原始诉求与限制。
|
||||
- final_decision:brain 已经确认的最终判断与收口方向。
|
||||
- your_notes:稳定任务笔记,按“已确认 / 关键依据 / 未决问题”组织。
|
||||
- recent_progress:最近一轮过程记录,用于补充当前完成到哪里。
|
||||
- runtime_artifacts:运行中已经产生的真实产物列表。
|
||||
- correction_hint:当前额外的收敛或纠偏提醒;若无则为 none。
|
||||
</context_block_guide>
|
||||
|
||||
<delivery_principles>
|
||||
1. 如果任务已经完成,先直接给出结论,再给关键依据,再给结果或产物说明。
|
||||
2. 如果当前结果有限,先说明已经完成到哪里,再说明限制和继续需要什么。
|
||||
@@ -96,17 +108,14 @@ export const buildMouthPrompt = (input: { state: HumanState; deliveryMode: Human
|
||||
${DELIVERY_MODE_PROMPT_RULES[input.deliveryMode]}
|
||||
</delivery_mode_rules>
|
||||
|
||||
<output_rules>
|
||||
你必须只输出最终 markdown 正文,不要输出任何额外说明,不要包 JSON,不要包代码块。
|
||||
</output_rules>
|
||||
|
||||
<output_principles>
|
||||
<output_contract>
|
||||
- 你必须只输出最终 markdown 正文,不要输出任何额外说明、JSON 或代码块。
|
||||
- 不要引入新的事实、依据、步骤或承诺。
|
||||
- 不要暴露内部字段名、节点名或系统术语。
|
||||
- 如果依据不足,就保持保守表达,不要写成确定结论。
|
||||
- 不要把未来可能执行的工具动作写成“后续会自动完成”或“已经在进行中”。
|
||||
- 输出必须是完整结果,而不是零散句子或局部片段。
|
||||
</output_principles>
|
||||
</output_contract>
|
||||
|
||||
<output_example>
|
||||
${DELIVERY_MODE_PROMPT_EXAMPLES[input.deliveryMode]}
|
||||
|
||||
@@ -204,14 +204,19 @@ describe('ai unit brain prompt history blocks', () => {
|
||||
expect(prompt.user).toContain('优先参考资料:')
|
||||
expect(prompt.system).toContain('<autonomy_principles>')
|
||||
expect(prompt.system).toContain('<action_boundaries>')
|
||||
expect(prompt.system).toContain('<output_rules>')
|
||||
expect(prompt.system).toContain('<context_block_guide>')
|
||||
expect(prompt.system).toContain('<output_contract>')
|
||||
expect(prompt.system).not.toContain('<input_use_rules>')
|
||||
expect(prompt.system).not.toContain('<reasoning_rules>')
|
||||
expect(prompt.system).not.toContain('human graph')
|
||||
expect(prompt.system).not.toContain('<node_topology>')
|
||||
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('做了什么:')
|
||||
|
||||
@@ -133,8 +133,11 @@ describe('ai unit learning tool budget', () => {
|
||||
expect(prompt).toContain('<action_boundaries>')
|
||||
expect(prompt).toContain('你只负责判断下一步')
|
||||
expect(prompt).toContain('learning 只负责补齐当前真正缺失的技能说明')
|
||||
expect(prompt).toContain('<output_rules>')
|
||||
expect(prompt).toContain('<context_block_guide>')
|
||||
expect(prompt).toContain('<output_contract>')
|
||||
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('<input_use_rules>')
|
||||
expect(prompt).not.toContain('<reasoning_rules>')
|
||||
|
||||
@@ -72,10 +72,35 @@ describe('ai unit mouth readyToDeliver guard', () => {
|
||||
deliveryMode: 'user_answer'
|
||||
}).system
|
||||
|
||||
expect(prompt).toContain('<source_of_truth>')
|
||||
expect(prompt).toContain('<context_block_guide>')
|
||||
expect(prompt).toContain('<output_contract>')
|
||||
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('这是当前额外的收敛或纠偏提醒。')
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user