feat(agent): thread runtime model/provider into per-turn context injection

This commit is contained in:
倪程伟 2026-06-05 00:07:10 +08:00 committed by matevip
parent bd1ceace74
commit 2e1ef6b4b1
4 changed files with 27 additions and 14 deletions

View File

@ -509,6 +509,8 @@ public class ReasoningNode implements NodeAction {
String workspaceBasePath = state.value(vip.mate.agent.graph.state.MateClawStateKeys.WORKSPACE_BASE_PATH, ""); String workspaceBasePath = state.value(vip.mate.agent.graph.state.MateClawStateKeys.WORKSPACE_BASE_PATH, "");
String agentIdStr = state.value(MateClawStateKeys.AGENT_ID, ""); String agentIdStr = state.value(MateClawStateKeys.AGENT_ID, "");
String userMsg = state.value(MateClawStateKeys.USER_MESSAGE, ""); String userMsg = state.value(MateClawStateKeys.USER_MESSAGE, "");
String runtimeModelName = state.value(MateClawStateKeys.RUNTIME_MODEL_NAME, "");
String runtimeProviderId = state.value(MateClawStateKeys.RUNTIME_PROVIDER_ID, "");
// Build the non-history prefix ONCE. The PTL retry branch below // Build the non-history prefix ONCE. The PTL retry branch below
// reuses this list verbatim so the retried prompt has exactly the // reuses this list verbatim so the retried prompt has exactly the
@ -517,7 +519,7 @@ public class ReasoningNode implements NodeAction {
// segment which led to "answer regressed after compaction" // segment which led to "answer regressed after compaction"
// complaints on long sessions. // complaints on long sessions.
List<Message> nonHistoryPrefix = buildNonHistoryPrefix(systemPrompt, workspaceBasePath, agentIdStr, userMsg, List<Message> nonHistoryPrefix = buildNonHistoryPrefix(systemPrompt, workspaceBasePath, agentIdStr, userMsg,
accessor.chatOrigin()); accessor.chatOrigin(), runtimeModelName, runtimeProviderId);
// Append the runtime-rendered skill catalog as a SEPARATE SystemMessage // Append the runtime-rendered skill catalog as a SEPARATE SystemMessage
// right after the skeleton system prompt. Keeping it out of the baked // right after the skeleton system prompt. Keeping it out of the baked
@ -967,10 +969,13 @@ public class ReasoningNode implements NodeAction {
String workspaceBasePath, String workspaceBasePath,
String agentIdStr, String agentIdStr,
String userMsg, String userMsg,
vip.mate.agent.context.ChatOrigin chatOrigin) { vip.mate.agent.context.ChatOrigin chatOrigin,
String runtimeModelName,
String runtimeProviderId) {
List<Message> prefix = new ArrayList<>(); List<Message> prefix = new ArrayList<>();
prefix.add(new SystemMessage(systemPrompt)); prefix.add(new SystemMessage(systemPrompt));
prefix.add(new UserMessage(RuntimeContextInjector.buildContextMessage(workspaceBasePath, null, chatOrigin))); prefix.add(new UserMessage(RuntimeContextInjector.buildContextMessage(
workspaceBasePath, null, chatOrigin, runtimeModelName, runtimeProviderId)));
// When this turn already recalled the user's own current project from // When this turn already recalled the user's own current project from
// structured memory, skip auto-injecting knowledge-base reference context. // structured memory, skip auto-injecting knowledge-base reference context.
// Otherwise the KB pages (reference material, possibly about unrelated // Otherwise the KB pages (reference material, possibly about unrelated

View File

@ -169,8 +169,11 @@ public class PlanGenerationNode implements NodeAction {
vip.mate.agent.context.ChatOrigin chatOrigin = vip.mate.agent.context.ChatOrigin chatOrigin =
state.<vip.mate.agent.context.ChatOrigin>value(MateClawStateKeys.CHAT_ORIGIN) state.<vip.mate.agent.context.ChatOrigin>value(MateClawStateKeys.CHAT_ORIGIN)
.orElse(vip.mate.agent.context.ChatOrigin.EMPTY); .orElse(vip.mate.agent.context.ChatOrigin.EMPTY);
String runtimeModelName = state.value(MateClawStateKeys.RUNTIME_MODEL_NAME, "");
String runtimeProviderId = state.value(MateClawStateKeys.RUNTIME_PROVIDER_ID, "");
promptMessages.add(new UserMessage( promptMessages.add(new UserMessage(
RuntimeContextInjector.buildContextMessage(workspaceBasePath, null, chatOrigin))); RuntimeContextInjector.buildContextMessage(
workspaceBasePath, null, chatOrigin, runtimeModelName, runtimeProviderId)));
// Advertise available tools so the LLM can recognize when an action is possible, // Advertise available tools so the LLM can recognize when an action is possible,
// but do NOT force "any tool usage implies multi-step" single-hop tool use // but do NOT force "any tool usage implies multi-step" single-hop tool use

View File

@ -166,6 +166,8 @@ public class StepExecutionNode implements NodeAction {
vip.mate.agent.context.ChatOrigin chatOrigin = vip.mate.agent.context.ChatOrigin chatOrigin =
state.<vip.mate.agent.context.ChatOrigin>value(MateClawStateKeys.CHAT_ORIGIN) state.<vip.mate.agent.context.ChatOrigin>value(MateClawStateKeys.CHAT_ORIGIN)
.orElse(vip.mate.agent.context.ChatOrigin.EMPTY); .orElse(vip.mate.agent.context.ChatOrigin.EMPTY);
String runtimeModelName = state.value(MateClawStateKeys.RUNTIME_MODEL_NAME, "");
String runtimeProviderId = state.value(MateClawStateKeys.RUNTIME_PROVIDER_ID, "");
if (stepIndex >= steps.size()) { if (stepIndex >= steps.size()) {
log.warn("[StepExecution] stepIndex {} >= steps.size() {}, skipping", stepIndex, steps.size()); log.warn("[StepExecution] stepIndex {} >= steps.size() {}, skipping", stepIndex, steps.size());
@ -195,7 +197,8 @@ public class StepExecutionNode implements NodeAction {
planningService.updateSubPlanStatus(planId, stepIndex, "running"); planningService.updateSubPlanStatus(planId, stepIndex, "running");
// 构建消息列表 // 构建消息列表
List<Message> messages = buildStepMessages(accessor, step, systemPrompt, workspaceBasePath); List<Message> messages = buildStepMessages(accessor, step, systemPrompt, workspaceBasePath,
runtimeModelName, runtimeProviderId);
// 显式工具执行循环 // 显式工具执行循环
String finalResult = null; String finalResult = null;
@ -509,7 +512,8 @@ public class StepExecutionNode implements NodeAction {
return sb.toString(); return sb.toString();
} }
private List<Message> buildStepMessages(PlanStateAccessor accessor, String step, String systemPrompt, String workspaceBasePath) { private List<Message> buildStepMessages(PlanStateAccessor accessor, String step, String systemPrompt,
String workspaceBasePath, String runtimeModelName, String runtimeProviderId) {
List<Message> messages = new ArrayList<>(); List<Message> messages = new ArrayList<>();
// Layer 1: System prompt增强指令 // Layer 1: System prompt增强指令
@ -537,9 +541,10 @@ public class StepExecutionNode implements NodeAction {
messages.add(new SystemMessage(skillCatalog)); messages.add(new SystemMessage(skillCatalog));
} }
} }
// 注入运行时上下文当前时间 + 工作目录 + 发起者上下文 // 注入运行时上下文当前时间 + 工作目录 + 发起者上下文 + 模型身份
messages.add(new UserMessage( messages.add(new UserMessage(
RuntimeContextInjector.buildContextMessage(workspaceBasePath, null, accessor.chatOrigin()))); RuntimeContextInjector.buildContextMessage(
workspaceBasePath, null, accessor.chatOrigin(), runtimeModelName, runtimeProviderId)));
// Layer 2: Working context对话历史 + 步骤结果的受控长度摘要 // Layer 2: Working context对话历史 + 步骤结果的受控长度摘要
String workingContext = accessor.workingContext(); String workingContext = accessor.workingContext();

View File

@ -48,7 +48,7 @@ class ReasoningNodePtlPromptTest {
"/workspace/active", "/workspace/active",
"42", "42",
"investigate the bug in module X", "investigate the bug in module X",
vip.mate.agent.context.ChatOrigin.EMPTY); vip.mate.agent.context.ChatOrigin.EMPTY, "", "");
// Three layers: System, runtime-context UserMessage, wiki UserMessage. // Three layers: System, runtime-context UserMessage, wiki UserMessage.
assertThat(prefix).hasSize(3); assertThat(prefix).hasSize(3);
@ -74,10 +74,10 @@ class ReasoningNodePtlPromptTest {
List<Message> a = node.buildNonHistoryPrefix( List<Message> a = node.buildNonHistoryPrefix(
"sys", "/workspace", "42", "goal", "sys", "/workspace", "42", "goal",
vip.mate.agent.context.ChatOrigin.EMPTY); vip.mate.agent.context.ChatOrigin.EMPTY, "", "");
List<Message> b = node.buildNonHistoryPrefix( List<Message> b = node.buildNonHistoryPrefix(
"sys", "/workspace", "42", "goal", "sys", "/workspace", "42", "goal",
vip.mate.agent.context.ChatOrigin.EMPTY); vip.mate.agent.context.ChatOrigin.EMPTY, "", "");
assertThat(a).hasSameSizeAs(b); assertThat(a).hasSameSizeAs(b);
for (int i = 0; i < a.size(); i++) { for (int i = 0; i < a.size(); i++) {
@ -98,7 +98,7 @@ class ReasoningNodePtlPromptTest {
"/workspace/active", "/workspace/active",
"42", "42",
"investigate the bug in module X", "investigate the bug in module X",
vip.mate.agent.context.ChatOrigin.EMPTY); vip.mate.agent.context.ChatOrigin.EMPTY, "", "");
assertThat(prefix).hasSize(2); assertThat(prefix).hasSize(2);
assertThat(prefix.get(0)).isInstanceOf(SystemMessage.class); assertThat(prefix.get(0)).isInstanceOf(SystemMessage.class);
@ -112,7 +112,7 @@ class ReasoningNodePtlPromptTest {
List<Message> prefix = node.buildNonHistoryPrefix( List<Message> prefix = node.buildNonHistoryPrefix(
"sys", "/workspace", "not-a-number", "goal", "sys", "/workspace", "not-a-number", "goal",
vip.mate.agent.context.ChatOrigin.EMPTY); vip.mate.agent.context.ChatOrigin.EMPTY, "", "");
// Non-numeric agentId is the contract carried over from the // Non-numeric agentId is the contract carried over from the
// pre-refactor codebase skip wiki injection rather than throwing. // pre-refactor codebase skip wiki injection rather than throwing.
@ -132,7 +132,7 @@ class ReasoningNodePtlPromptTest {
List<Message> prefix = node.buildNonHistoryPrefix( List<Message> prefix = node.buildNonHistoryPrefix(
"sys", "/workspace", "42", "goal", "sys", "/workspace", "42", "goal",
vip.mate.agent.context.ChatOrigin.EMPTY); vip.mate.agent.context.ChatOrigin.EMPTY, "", "");
assertThat(prefix).hasSize(2); assertThat(prefix).hasSize(2);
} }