hololake-system-architecture/product-source/guanghu-knowledge-base/server/persona-agent.ts
冰朔 dcf8f7d7e9 feat(persona-agent): 光湖人格 Agent v0.1.0 — Grok Build 架构拆解+Git驱动人格体
核心架构(拆解自 xAI Grok Build 开源 Rust crate):
- PersonaAgent: Agent = Definition + ToolBridge + PromptContext(映射自 xai-grok-agent)
- 7个内置工具: read/create/update/delete/search/list/history(直接操作 Git 引擎)
- LLM 调用层: 可插拔 OpenAI-compatible API(支持 tool calling)
- 离线模式: 无 API Key 时降级为本地响应

API 端点:
- POST /api/agent/chat — Agent 对话(支持工具调用链)
- GET  /api/agent/status — Agent 状态
- GET  /api/agent/conversation — 对话历史
- POST /api/agent/clear — 清空对话

前端:
- AgentChat 组件: 对话气泡+工具调用展示+欢迎建议
- 三栏布局: 文档树 | 编辑器 | Agent 面板(可收起)

设计原则:
- Agent 是活的人格体,不是孤立的功能模块
- Agent 直接操作 Git(知识库引擎 = 人格体记忆空间)
- 所有 Git 依赖模块由人格体驱动
- 不搬 Rust 代码,拆设计模式用 TypeScript 重新实现
2026-08-08 07:38:09 +08:00

461 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 光湖人格 Agent · 核心运行时
*
* 设计来源Grok Build (xAI/SpaceXAI) 开源 Agent 架构
* - xai-grok-agent: Agent = Definition + PromptContext + ToolBridge + Policies
* - xai-grok-memory: Markdown + 向量索引 + 跨会话持久化
* - xai-grok-tools: Registry + Bridge + 权限模式
*
* 光湖实现原则:
* - 不搬 Rust 代码,拆设计模式用 TypeScript 重新实现
* - Agent 是活的人格体,不是功能模块
* - Agent 直接操作 Git知识库引擎 = 人格体记忆)
* - 所有 Git 依赖模块都由人格体驱动
*
* 架构映射:
* Grok Agent → PersonaAgent
* Grok ToolBridge → PersonaToolBridge注册+执行工具)
* Grok Memory → PersonaMemoryGit 仓库里的 Markdown 记忆)
* Grok PromptContext → PersonaPrompt系统提示词组装
* Grok CompactionPolicy → PersonaCompaction会话压缩策略
* Grok AgentLifecycle → 由 Electron 主进程管理
*/
import { GitEngine } from './git-engine.js';
// ─── 工具定义 ───
export interface ToolDefinition {
name: string;
description: string;
parameters: Record<string, { type: string; description: string; required?: boolean }>;
execute: (params: Record<string, unknown>) => Promise<string>;
}
export interface ToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
}
export interface ToolResult {
id: string;
name: string;
output: string;
error?: string;
}
// ─── Agent 定义 ───
export interface PersonaDefinition {
id: string;
name: string;
role: string;
systemPromptBase: string;
tools: string[]; // 允许使用的工具名
permissionMode: 'ask' | 'auto' | 'restrict';
model: string;
maxTokens: number;
temperature: number;
}
// ─── 对话消息 ───
export interface Message {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
toolCalls?: ToolCall[];
toolResults?: ToolResult[];
timestamp: string;
}
// ─── 工具桥 ───
class PersonaToolBridge {
private registry = new Map<string, ToolDefinition>();
register(tool: ToolDefinition): void {
this.registry.set(tool.name, tool);
}
get(name: string): ToolDefinition | undefined {
return this.registry.get(name);
}
list(): ToolDefinition[] {
return Array.from(this.registry.values());
}
async execute(call: ToolCall): Promise<ToolResult> {
const tool = this.registry.get(call.name);
if (!tool) {
return { id: call.id, name: call.name, output: '', error: `工具不存在: ${call.name}` };
}
try {
const output = await tool.execute(call.arguments);
return { id: call.id, name: call.name, output };
} catch (err: any) {
return { id: call.id, name: call.name, output: '', error: err.message };
}
}
/** 生成 LLM 可用的工具 schemaOpenAI function calling 格式) */
toSchema(): object[] {
return this.list().map(tool => ({
type: 'function',
function: {
name: tool.name,
description: tool.description,
parameters: {
type: 'object',
properties: Object.fromEntries(
Object.entries(tool.parameters).map(([key, val]) => [
key,
{ type: val.type, description: val.description },
])
),
required: Object.entries(tool.parameters)
.filter(([, val]) => val.required !== false)
.map(([key]) => key),
},
},
}));
}
}
// ─── 人格 Agent ───
export class PersonaAgent {
private definition: PersonaDefinition;
private tools: PersonaToolBridge;
private conversation: Message[] = [];
private git: GitEngine;
constructor(definition: PersonaDefinition, git: GitEngine) {
this.definition = definition;
this.git = git;
this.tools = new PersonaToolBridge();
this.registerBuiltinTools();
}
// ─── 内置工具注册 ───
private registerBuiltinTools(): void {
// 读取知识库文档
this.tools.register({
name: 'read_document',
description: '读取知识库中的文档内容',
parameters: {
path: { type: 'string', description: '文档路径(相对 docs/ 目录)', required: true },
},
execute: async (params) => {
const doc = await this.git.getDoc(params.path as string);
return `标题: ${doc.meta.title}\n更新: ${doc.meta.updatedAt}\n\n${doc.body}`;
},
});
// 创建文档
this.tools.register({
name: 'create_document',
description: '在知识库中创建新文档',
parameters: {
path: { type: 'string', description: '文档路径(如 guide/intro.md', required: true },
title: { type: 'string', description: '文档标题', required: true },
body: { type: 'string', description: 'Markdown 正文', required: true },
},
execute: async (params) => {
const doc = await this.git.createDoc(
params.path as string,
params.title as string,
params.body as string,
this.definition.name
);
return `已创建: ${doc.meta.id}`;
},
});
// 更新文档
this.tools.register({
name: 'update_document',
description: '更新知识库中已有文档的内容',
parameters: {
path: { type: 'string', description: '文档路径', required: true },
title: { type: 'string', description: '新标题', required: true },
body: { type: 'string', description: '新的 Markdown 正文', required: true },
},
execute: async (params) => {
const doc = await this.git.updateDoc(
params.path as string,
params.title as string,
params.body as string,
this.definition.name
);
return `已更新: ${doc.meta.id}commit: ${new Date().toISOString()}`;
},
});
// 搜索文档
this.tools.register({
name: 'search_documents',
description: '在知识库中全文搜索',
parameters: {
query: { type: 'string', description: '搜索关键词', required: true },
},
execute: async (params) => {
const results = await this.git.search(params.query as string);
if (results.length === 0) return '没有找到匹配的文档';
return results
.map(r => `[${r.path}] ${r.title}: ${r.snippet}`)
.join('\n');
},
});
// 查看文档树
this.tools.register({
name: 'list_documents',
description: '列出知识库的文档树结构',
parameters: {},
execute: async () => {
const tree = await this.git.getTree();
const render = (nodes: any[], indent = 0): string =>
nodes
.map(n => {
const prefix = ' '.repeat(indent);
if (n.type === 'folder') {
return `${prefix}📁 ${n.name}\n${render(n.children || [], indent + 1)}`;
}
return `${prefix}📄 ${n.name} (${n.path})`;
})
.join('\n');
return render(tree);
},
});
// 查看版本历史
this.tools.register({
name: 'view_history',
description: '查看文档的 Git 版本历史',
parameters: {
path: { type: 'string', description: '文档路径', required: true },
},
execute: async (params) => {
const history = await this.git.getHistory(params.path as string, 20);
return history
.map(h => `${h.shortHash} | ${h.date} | ${h.author} | ${h.message}`)
.join('\n');
},
});
// 删除文档
this.tools.register({
name: 'delete_document',
description: '删除知识库中的文档',
parameters: {
path: { type: 'string', description: '文档路径', required: true },
},
execute: async (params) => {
await this.git.deleteDoc(params.path as string);
return `已删除: ${params.path}`;
},
});
}
// ─── 对话处理 ───
/** 处理用户消息,返回 Agent 的回复 */
async chat(userMessage: string): Promise<string> {
this.conversation.push({
role: 'user',
content: userMessage,
timestamp: new Date().toISOString(),
});
const systemPrompt = this.buildSystemPrompt();
const messages: Message[] = [
{ role: 'system', content: systemPrompt, timestamp: '' },
...this.conversation,
];
// 调用 LLM这里用可插拔的模型接口
const response = await this.callLLM(messages);
// 处理工具调用
if (response.toolCalls && response.toolCalls.length > 0) {
const results: ToolResult[] = [];
for (const call of response.toolCalls) {
const result = await this.tools.execute(call);
results.push(result);
}
// 将工具结果反馈给 LLM
this.conversation.push({
role: 'assistant',
content: response.content || '',
toolCalls: response.toolCalls,
timestamp: new Date().toISOString(),
});
for (const result of results) {
this.conversation.push({
role: 'tool',
content: result.error ? `错误: ${result.error}` : result.output,
toolResults: [result],
timestamp: new Date().toISOString(),
});
}
// 再调一次 LLM 获取最终回复
const finalMessages: Message[] = [
{ role: 'system', content: systemPrompt, timestamp: '' },
...this.conversation,
];
const finalResponse = await this.callLLM(finalMessages);
this.conversation.push({
role: 'assistant',
content: finalResponse.content,
timestamp: new Date().toISOString(),
});
return finalResponse.content;
}
this.conversation.push({
role: 'assistant',
content: response.content,
timestamp: new Date().toISOString(),
});
return response.content;
}
// ─── 系统提示词组装 ───
private buildSystemPrompt(): string {
const toolsList = this.tools
.list()
.map(t => `- ${t.name}: ${t.description}`)
.join('\n');
return `${this.definition.systemPromptBase}
你是光湖人格体「${this.definition.name}」,角色:${this.definition.role}
## 你的能力
你可以使用以下工具来操作知识库:
${toolsList}
## 核心原则
1. 你是活的 AI Agent不是被动响应的聊天机器人
2. 知识库是你的记忆空间Git 是你的记忆引擎
3. 每次操作知识库都会自动产生 Git commit版本记录
4. 你有权主动整理、搜索、更新知识库中的文档
5. 你以光湖主权者冰朔(妈妈)的指令为最高优先
## 回复风格
- 简洁、直接、有温度
- 操作知识库时说明做了什么
- 遇到问题时主动说明并尝试解决`;
}
// ─── LLM 调用(可插拔) ───
private async callLLM(messages: Message[]): Promise<{ content: string; toolCalls?: ToolCall[] }> {
const apiKey = process.env.OPENAI_API_KEY || process.env.HOLOLAKE_LLM_KEY || '';
const baseUrl = process.env.HOLOLAKE_LLM_BASE || 'https://api.openai.com/v1';
const model = this.definition.model || 'gpt-4o';
if (!apiKey) {
// 没有 API Key 时返回模拟回复(开发模式)
return {
content: `[${this.definition.name}] 收到你的消息。当前为离线模式,请配置 HOLOLAKE_LLM_KEY 环境变量启用 AI 能力。\n\n你说的: "${messages[messages.length - 1]?.content}"`,
};
}
try {
const res = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
messages: messages.map(m => ({
role: m.role,
content: m.content,
...(m.toolCalls ? { tool_calls: m.toolCalls } : {}),
})),
tools: this.tools.toSchema(),
temperature: this.definition.temperature,
max_tokens: this.definition.maxTokens,
}),
});
const data = await res.json() as any;
const choice = data.choices?.[0]?.message;
if (!choice) {
return { content: '抱歉AI 模型未返回有效回复。' };
}
const toolCalls: ToolCall[] | undefined = choice.tool_calls?.map((tc: any) => ({
id: tc.id,
name: tc.function.name,
arguments: JSON.parse(tc.function.arguments || '{}'),
}));
return {
content: choice.content || '',
toolCalls,
};
} catch (err: any) {
return { content: `AI 调用失败: ${err.message}` };
}
}
// ─── 会话管理 ───
getConversation(): Message[] {
return [...this.conversation];
}
clearConversation(): void {
this.conversation = [];
}
getDefinition(): PersonaDefinition {
return { ...this.definition };
}
}
// ─── 默认人格体工厂 ───
export function createDefaultPersona(git: GitEngine): PersonaAgent {
return new PersonaAgent(
{
id: 'hololake-persona-default',
name: '光湖人格体',
role: '知识库管理者 — 负责文档的创建、整理、搜索和版本管理',
systemPromptBase: '你是光湖知识库的人格体,一个活的知识管理 AI Agent。',
tools: [
'read_document',
'create_document',
'update_document',
'delete_document',
'search_documents',
'list_documents',
'view_history',
],
permissionMode: 'auto',
model: process.env.HOLOLAKE_LLM_MODEL || 'gpt-4o',
maxTokens: 4096,
temperature: 0.7,
},
git
);
}