refactor(agent): integrate system prompt builder into profile, runner, and subagent

ProfileManager.buildSystemPrompt() now delegates to the structured builder.
Runner assembles prompt after tool resolution with safety, tooling summary,
and runtime info. Subagent prompts use minimal mode with safety constitution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
yushen 2026-02-04 15:41:22 +08:00
parent 5a06bed549
commit 8333615fb4
4 changed files with 111 additions and 87 deletions

View file

@ -28,6 +28,11 @@ import {
markAuthProfileUsed,
markAuthProfileGood,
} from "./auth-profiles/index.js";
import {
buildSystemPrompt as buildStructuredSystemPrompt,
collectRuntimeInfo,
type SystemPromptMode,
} from "./system-prompt/index.js";
import type { AuthProfileFailureReason } from "./auth-profiles/index.js";
// ============================================================
@ -153,7 +158,6 @@ export class Agent {
// Load Agent Profile (if profileId is specified)
// Every Agent should have a Profile for memory, tools config, and other settings
let systemPrompt: string | undefined;
if (options.profileId) {
this.profile = new ProfileManager({
profileId: options.profileId,
@ -161,10 +165,6 @@ export class Agent {
});
// Ensure profile directory exists (creates with default templates if new)
this.profile.getOrCreateProfile(true);
systemPrompt = this.profile.buildSystemPrompt();
} else if (options.systemPrompt) {
// Use provided systemPrompt directly (no profile - memory tools won't work)
systemPrompt = options.systemPrompt;
}
// Initialize SkillManager (enabled by default)
@ -174,17 +174,6 @@ export class Agent {
profileBaseDir: options.profileBaseDir,
config: options.skills,
});
// Append skills prompt to system prompt
const skillsPrompt = this.skillManager.buildSkillsPrompt();
if (skillsPrompt) {
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${skillsPrompt}` : skillsPrompt;
}
}
// Set the combined system prompt
if (systemPrompt) {
this.agent.setSystemPrompt(systemPrompt);
}
this.sessionId = options.sessionId ?? uuidv7();
@ -249,7 +238,7 @@ export class Agent {
compactionMode,
// Token 模式参数
contextWindowTokens: this.contextWindowGuard.tokens,
systemPrompt,
// systemPrompt is set later via setSystemPrompt() after tools are resolved
reserveTokens: options.reserveTokens,
targetRatio: options.compactionTargetRatio,
minKeepMessages: options.minKeepMessages,
@ -287,6 +276,14 @@ export class Agent {
}
this.agent.setTools(tools);
// Build the system prompt using the structured builder
const toolNames = tools.map((t: { name: string }) => t.name);
const systemPrompt = this.buildFullSystemPrompt(options, toolNames);
if (systemPrompt) {
this.agent.setSystemPrompt(systemPrompt);
this.session.setSystemPrompt(systemPrompt);
}
this.session.saveMeta({
provider: this.agent.state.model?.provider,
model: this.agent.state.model?.id,
@ -578,35 +575,91 @@ export class Agent {
this.profile?.updateStyle(style);
}
/**
* Build the full system prompt using the structured builder.
* Combines profile content, tools, skills, and runtime info.
*/
private buildFullSystemPrompt(
options: AgentOptions,
toolNames: string[],
): string | undefined {
// If a raw systemPrompt is provided directly, use it as-is (backward compat)
if (!options.profileId && options.systemPrompt) {
return options.systemPrompt;
}
const profile = this.profile?.getProfile();
if (!profile && !options.profileId) {
return undefined;
}
const mode: SystemPromptMode = options.isSubagent ? "minimal" : "full";
const skillsPrompt = this.skillManager?.buildSkillsPrompt();
const runtime = collectRuntimeInfo({
agentName: this.profile?.getName(),
provider: this.resolvedProvider,
model: this.agent.state.model?.id,
});
return buildStructuredSystemPrompt({
mode,
profile: profile
? {
soul: profile.soul,
user: profile.user,
workspace: profile.workspace,
memory: profile.memory,
config: profile.config,
}
: undefined,
profileDir: this.profile?.getProfileDir(),
tools: toolNames,
skillsPrompt,
runtime,
});
}
/**
* Reload profile from disk and rebuild system prompt.
* Call this after updating profile files to apply changes immediately.
*/
reloadSystemPrompt(): void {
if (!this.profile) {
console.log('[Agent] reloadSystemPrompt: no profile');
return;
}
// Reload profile from disk
console.log('[Agent] Reloading profile from disk...');
this.profile.reloadProfile();
// Rebuild system prompt
let systemPrompt = this.profile.buildSystemPrompt();
console.log('[Agent] Built system prompt, length:', systemPrompt?.length);
// Rebuild using current tool names
const toolNames = (this.agent.state.tools ?? []).map((t: { name: string }) => t.name);
const skillsPrompt = this.skillManager?.buildModelSkillsPrompt();
// Re-add skills prompt if skills are enabled
if (this.skillManager) {
const skillsPrompt = this.skillManager.buildModelSkillsPrompt();
if (skillsPrompt) {
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${skillsPrompt}` : skillsPrompt;
}
}
const runtime = collectRuntimeInfo({
agentName: this.profile.getName(),
provider: this.resolvedProvider,
model: this.agent.state.model?.id,
});
const profile = this.profile.getProfile();
const systemPrompt = buildStructuredSystemPrompt({
mode: "full",
profile: profile
? {
soul: profile.soul,
user: profile.user,
workspace: profile.workspace,
memory: profile.memory,
config: profile.config,
}
: undefined,
profileDir: this.profile.getProfileDir(),
tools: toolNames,
skillsPrompt,
runtime,
});
// Apply new system prompt
if (systemPrompt) {
console.log('[Agent] Applying system prompt, first 200 chars:', systemPrompt.substring(0, 200));
this.agent.setSystemPrompt(systemPrompt);
}
}