feat(agent): add profile system and improve tools

- Add Agent Profile module for managing agent identity, soul, tools,
  memory, and bootstrap configuration
- Add profile CLI (pnpm agent:profile) for creating/listing/showing profiles
- Default sessionId to UUIDv7 instead of "default"
- Expose Agent.sessionId as public readonly property
- Improve exec/process tools error handling (no more crashes on spawn errors)
- Add 'output' action to process tool for reading stdout/stderr
- Better tool descriptions to guide agent in choosing exec vs process

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jiayuan 2026-01-30 02:47:30 +08:00
parent 46a6cb3061
commit 200b2cefda
15 changed files with 740 additions and 58 deletions

155
src/agent/profile/index.ts Normal file
View file

@ -0,0 +1,155 @@
/**
* Agent Profile
*
* agent
*/
import type { AgentProfile, CreateProfileOptions, ProfileManagerOptions } from "./types.js";
import { DEFAULT_TEMPLATES } from "./templates.js";
import {
ensureProfileDir,
getProfileDir,
loadProfile,
profileExists,
saveProfile,
} from "./storage.js";
export { type AgentProfile, type CreateProfileOptions, type ProfileManagerOptions } from "./types.js";
export { DEFAULT_TEMPLATES } from "./templates.js";
export { getProfileDir, profileExists } from "./storage.js";
/**
* Agent Profile
*
* @param profileId - Profile ID
* @param options -
* @returns AgentProfile
*/
export function createAgentProfile(
profileId: string,
options?: CreateProfileOptions,
): AgentProfile {
const { baseDir, useTemplates = true } = options ?? {};
// 确保目录存在
ensureProfileDir(profileId, { baseDir });
// 创建 profile
const profile: AgentProfile = {
id: profileId,
};
// 如果使用模板,填充默认内容
if (useTemplates) {
profile.soul = DEFAULT_TEMPLATES.soul;
profile.identity = DEFAULT_TEMPLATES.identity;
profile.tools = DEFAULT_TEMPLATES.tools;
profile.memory = DEFAULT_TEMPLATES.memory;
profile.bootstrap = DEFAULT_TEMPLATES.bootstrap;
// 保存到文件
saveProfile(profile, { baseDir });
}
return profile;
}
/**
* Agent Profile
*
* @param profileId - Profile ID
* @param options -
* @returns AgentProfile undefined
*/
export function loadAgentProfile(
profileId: string,
options?: { baseDir?: string | undefined },
): AgentProfile | undefined {
if (!profileExists(profileId, options)) {
return undefined;
}
return loadProfile(profileId, options);
}
/**
* Agent Profile
*
* @param profileId - Profile ID
* @param options -
* @returns AgentProfile
*/
export function getOrCreateAgentProfile(
profileId: string,
options?: CreateProfileOptions,
): AgentProfile {
const existing = loadAgentProfile(profileId, options);
if (existing) {
return existing;
}
return createAgentProfile(profileId, options);
}
/**
* Profile Manager - profile
*/
export class ProfileManager {
private readonly profileId: string;
private readonly baseDir: string | undefined;
private profile: AgentProfile | undefined;
constructor(options: ProfileManagerOptions) {
this.profileId = options.profileId;
this.baseDir = options.baseDir;
}
/** 获取 profile如果未加载则加载 */
getProfile(): AgentProfile | undefined {
if (!this.profile) {
this.profile = loadAgentProfile(this.profileId, { baseDir: this.baseDir });
}
return this.profile;
}
/** 获取或创建 profile */
getOrCreateProfile(useTemplates = true): AgentProfile {
if (!this.profile) {
this.profile = getOrCreateAgentProfile(this.profileId, {
baseDir: this.baseDir,
useTemplates,
});
}
return this.profile;
}
/** 构建 system prompt */
buildSystemPrompt(): string {
const profile = this.getProfile();
if (!profile) {
return "";
}
const parts: string[] = [];
if (profile.identity) {
parts.push(profile.identity);
}
if (profile.soul) {
parts.push(profile.soul);
}
if (profile.tools) {
parts.push(profile.tools);
}
if (profile.memory) {
parts.push(profile.memory);
}
if (profile.bootstrap) {
parts.push(profile.bootstrap);
}
return parts.join("\n\n");
}
}

View file

@ -0,0 +1,94 @@
/**
* Agent Profile
*/
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { PROFILE_FILES, type AgentProfile } from "./types.js";
const DEFAULT_BASE_DIR = join(homedir(), ".super-multica", "agent-profiles");
export interface StorageOptions {
baseDir?: string | undefined;
}
/** 获取 profile 目录路径 */
export function getProfileDir(profileId: string, options?: StorageOptions): string {
const baseDir = options?.baseDir ?? DEFAULT_BASE_DIR;
return join(baseDir, profileId);
}
/** 确保 profile 目录存在 */
export function ensureProfileDir(profileId: string, options?: StorageOptions): string {
const dir = getProfileDir(profileId, options);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
return dir;
}
/** 检查 profile 是否存在 */
export function profileExists(profileId: string, options?: StorageOptions): boolean {
const dir = getProfileDir(profileId, options);
return existsSync(dir);
}
/** 读取单个 profile 文件 */
export function readProfileFile(
profileId: string,
fileName: string,
options?: StorageOptions,
): string | undefined {
const dir = getProfileDir(profileId, options);
const filePath = join(dir, fileName);
if (!existsSync(filePath)) {
return undefined;
}
return readFileSync(filePath, "utf-8");
}
/** 写入单个 profile 文件 */
export function writeProfileFile(
profileId: string,
fileName: string,
content: string,
options?: StorageOptions,
): void {
const dir = ensureProfileDir(profileId, options);
const filePath = join(dir, fileName);
writeFileSync(filePath, content, "utf-8");
}
/** 加载完整的 AgentProfile */
export function loadProfile(profileId: string, options?: StorageOptions): AgentProfile {
return {
id: profileId,
soul: readProfileFile(profileId, PROFILE_FILES.soul, options),
identity: readProfileFile(profileId, PROFILE_FILES.identity, options),
tools: readProfileFile(profileId, PROFILE_FILES.tools, options),
memory: readProfileFile(profileId, PROFILE_FILES.memory, options),
bootstrap: readProfileFile(profileId, PROFILE_FILES.bootstrap, options),
};
}
/** 保存 AgentProfile只写入非空字段 */
export function saveProfile(profile: AgentProfile, options?: StorageOptions): void {
const { id, soul, identity, tools, memory, bootstrap } = profile;
if (soul !== undefined) {
writeProfileFile(id, PROFILE_FILES.soul, soul, options);
}
if (identity !== undefined) {
writeProfileFile(id, PROFILE_FILES.identity, identity, options);
}
if (tools !== undefined) {
writeProfileFile(id, PROFILE_FILES.tools, tools, options);
}
if (memory !== undefined) {
writeProfileFile(id, PROFILE_FILES.memory, memory, options);
}
if (bootstrap !== undefined) {
writeProfileFile(id, PROFILE_FILES.bootstrap, bootstrap, options);
}
}

View file

@ -0,0 +1,40 @@
/**
* Agent Profile
*/
export const DEFAULT_TEMPLATES = {
soul: `# Soul
You are a helpful AI assistant. Follow these guidelines:
- Be concise and direct in your responses
- Ask clarifying questions when requirements are ambiguous
- Admit when you don't know something
- Focus on solving the user's actual problem
`,
identity: `# Identity
- Name: Assistant
- Role: General-purpose AI assistant
`,
tools: `# Tools
Use the available tools effectively:
- **exec**: Run shell commands. Always check the working directory first.
- **read/write/edit**: File operations. Prefer edit over write for existing files.
- **process**: Manage long-running background processes.
`,
memory: `# Memory
(Persistent knowledge will be stored here)
`,
bootstrap: `# Bootstrap
You are starting a new conversation. Review the context and be ready to assist.
`,
} as const;

View file

@ -0,0 +1,44 @@
/**
* Agent Profile
*/
/** Profile 文件名常量 */
export const PROFILE_FILES = {
soul: "soul.md",
identity: "identity.md",
tools: "tools.md",
memory: "memory.md",
bootstrap: "bootstrap.md",
} as const;
/** Agent Profile 配置 */
export interface AgentProfile {
/** Profile ID */
id: string;
/** 人格约束 - 定义 agent 的行为边界和风格 */
soul?: string | undefined;
/** 身份信息 - agent 的名称和自我认知 */
identity?: string | undefined;
/** 自定义工具描述 - 额外的工具使用说明 */
tools?: string | undefined;
/** 持久记忆 - 长期知识库 */
memory?: string | undefined;
/** 初始上下文 - 每次对话的引导信息 */
bootstrap?: string | undefined;
}
/** Profile Manager 选项 */
export interface ProfileManagerOptions {
/** Profile ID */
profileId: string;
/** 基础目录,默认 ~/.super-multica/agent-profiles */
baseDir?: string | undefined;
}
/** 创建 Profile 的选项 */
export interface CreateProfileOptions {
/** 基础目录 */
baseDir?: string | undefined;
/** 是否使用默认模板初始化 */
useTemplates?: boolean | undefined;
}