del: 去除无用旧代码
This commit is contained in:
parent
004644efa3
commit
ef3e829436
@ -1,60 +0,0 @@
|
||||
# LLM-to-Agent 框架知识库
|
||||
|
||||
## Agent 工具调用流程
|
||||
|
||||
Agent 的 `run()` 方法执行以下循环:
|
||||
1. 将用户消息加入上下文
|
||||
2. 调用 LLM,传入可用工具列表
|
||||
3. 如果 LLM 返回 `tool_calls`,逐个执行工具,将结果加入上下文,回到步骤 2
|
||||
4. 如果 LLM 直接返回文本,结束循环,返回最终回复
|
||||
|
||||
工具定义需包含 `name`、`description`、`parameters`(JSON Schema)和 `execute` 函数。
|
||||
|
||||
## 子 Agent 管理
|
||||
|
||||
通过 `spawn_agent` 工具可以创建具有独立人设的子 Agent(如销售、顾客)。
|
||||
每个子 Agent 拥有独立的上下文和基础工具集(不含 sub-agent 管理工具,防止无限递归)。
|
||||
通过 `run_conversation` 工具可以在两个子 Agent 之间运行多轮对话。
|
||||
|
||||
## 知识库功能
|
||||
|
||||
知识库位于 `knowledge/` 目录,支持 `.md` 格式文档。
|
||||
文档按 `##` 二级标题自动分块,通过 `search_knowledge` 工具进行关键词检索。
|
||||
检索算法基于关键词命中次数,标题命中加权 ×3。
|
||||
也提供 `list_knowledge` 工具查看知识库全貌。
|
||||
|
||||
## 常用命令
|
||||
|
||||
- 启动框架:`pnpm dev`
|
||||
- 指定提示词模式:`pnpm dev orchestrator`
|
||||
- 可用模式:`default`、`toxic`、`json`、`agent`、`reAct`、`orchestrator`
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
src/
|
||||
index.ts -- CLI 入口
|
||||
llm.ts -- LLM 客户端(OpenAI 兼容)
|
||||
context.ts -- 上下文管理器
|
||||
types/index.ts -- 类型定义
|
||||
agents/
|
||||
agent.ts -- Agent 核心类
|
||||
manager.ts -- 子 Agent 管理器
|
||||
tools.ts -- spawn_agent / run_conversation 工具
|
||||
tools/
|
||||
registry.ts -- 工具注册表
|
||||
weather.ts -- 天气查询工具
|
||||
calculator.ts -- 计算器工具
|
||||
guess.ts -- 猜数字游戏工具
|
||||
knowledge/
|
||||
knowledge-base.ts -- 知识库类
|
||||
search-tool.ts -- 知识库搜索工具
|
||||
hooks/
|
||||
index.ts -- Hook 总线
|
||||
log.hooks.ts -- 日志 Hook
|
||||
registry.ts -- Hook 注册
|
||||
prompts/
|
||||
system.ts -- 系统提示词
|
||||
orchestrator.ts -- Orchestrator 提示词
|
||||
reAct.ts -- ReAct 提示词
|
||||
```
|
||||
@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "@llm-to-agent/old",
|
||||
"version": "0.2.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "tsx src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.x",
|
||||
"openai": "^4.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.9.1",
|
||||
"tsx": "^4.x",
|
||||
"typescript": "^5.x"
|
||||
}
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
import { ContextManager } from '../context.js';
|
||||
import { chat } from '../llm.js';
|
||||
import { getTools } from '../tools/registry.js';
|
||||
import { Tool } from '../types/index.js';
|
||||
import { hooks } from '../hooks/index.js';
|
||||
|
||||
export interface AgentConfig {
|
||||
name: string;
|
||||
systemPrompt: string;
|
||||
/** 该 Agent 可用的工具列表,默认使用全局注册的所有工具 */
|
||||
tools?: Tool[];
|
||||
}
|
||||
|
||||
export class Agent {
|
||||
public name: string;
|
||||
private context: ContextManager;
|
||||
private tools: Tool[];
|
||||
|
||||
constructor(config: AgentConfig) {
|
||||
this.name = config.name;
|
||||
this.tools = config.tools ?? getTools();
|
||||
this.context = new ContextManager();
|
||||
this.context.add({ role: 'system', content: config.systemPrompt });
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行 Agent 循环:发送消息 → 处理 tool_calls → 返回最终回复
|
||||
*/
|
||||
async run(userMessage: string): Promise<string> {
|
||||
this.context.add({ role: 'user', content: userMessage });
|
||||
|
||||
const openaiTools = this.tools.length > 0
|
||||
? this.tools.map((t) => ({
|
||||
type: 'function' as const,
|
||||
function: {
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.parameters,
|
||||
},
|
||||
}))
|
||||
: undefined;
|
||||
|
||||
let reply = await chat(this.context.getMessages(), openaiTools);
|
||||
|
||||
// 工具调用循环
|
||||
while (reply.tool_calls && reply.tool_calls.length > 0) {
|
||||
this.context.add({
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
tool_calls: reply.tool_calls,
|
||||
} as any);
|
||||
|
||||
for (const tc of reply.tool_calls) {
|
||||
const tool = this.tools.find((t) => t.name === tc.function.name);
|
||||
if (!tool) {
|
||||
this.context.add({
|
||||
role: 'tool',
|
||||
tool_call_id: tc.id,
|
||||
content: `错误: 未知工具 ${tc.function.name}`,
|
||||
} as any);
|
||||
continue;
|
||||
}
|
||||
|
||||
hooks.emit('tool:before', { toolCall: tc, agentName: this.name });
|
||||
|
||||
try {
|
||||
const args = JSON.parse(tc.function.arguments);
|
||||
const result = await tool.execute(args);
|
||||
this.context.add({
|
||||
role: 'tool',
|
||||
tool_call_id: tc.id,
|
||||
content: result,
|
||||
} as any);
|
||||
hooks.emit('tool:after', { toolCall: tc, result, agentName: this.name });
|
||||
} catch (e: any) {
|
||||
this.context.add({
|
||||
role: 'tool',
|
||||
tool_call_id: tc.id,
|
||||
content: `工具执行错误: ${e.message}`,
|
||||
} as any);
|
||||
hooks.emit('tool:after', { toolCall: tc, result: `错误: ${e.message}`, agentName: this.name });
|
||||
}
|
||||
}
|
||||
|
||||
reply = await chat(this.context.getMessages(), openaiTools);
|
||||
}
|
||||
|
||||
const content = reply.content!;
|
||||
this.context.add({ role: 'assistant', content });
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export { Agent } from './agent.js';
|
||||
export type { AgentConfig } from './agent.js';
|
||||
export { SubAgentManager, subAgentManager } from './manager.js';
|
||||
export { spawnAgentTool, runConversationTool, subAgentTools } from './tools.js';
|
||||
@ -1,90 +0,0 @@
|
||||
import { Agent } from './agent.js';
|
||||
import { getTools } from '../tools/registry.js';
|
||||
|
||||
/**
|
||||
* SubAgentManager 单例 — 管理所有子 Agent 的创建、查询和多轮对话编排
|
||||
*/
|
||||
export class SubAgentManager {
|
||||
private agents: Map<string, Agent> = new Map();
|
||||
|
||||
/**
|
||||
* 创建一个子 Agent(只有普通工具,没有 sub-agent 管理工具,防止无限递归)
|
||||
*/
|
||||
spawn(name: string, systemPrompt: string): Agent {
|
||||
if (this.agents.has(name)) {
|
||||
throw new Error(`子 Agent "${name}" 已存在`);
|
||||
}
|
||||
const agent = new Agent({
|
||||
name,
|
||||
systemPrompt,
|
||||
tools: getTools(), // 只有全局注册的普通工具
|
||||
});
|
||||
this.agents.set(name, agent);
|
||||
return agent;
|
||||
}
|
||||
|
||||
get(name: string): Agent | undefined {
|
||||
return this.agents.get(name);
|
||||
}
|
||||
|
||||
list(): Agent[] {
|
||||
return Array.from(this.agents.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行两个 Agent 之间的多轮对话
|
||||
* @param agent1Name 发起方 Agent 名称
|
||||
* @param agent2Name 回应方 Agent 名称
|
||||
* @param maxTurns 最大对话轮次(一轮 = agent1 发言 + agent2 回应)
|
||||
* @param topic 对话主题
|
||||
* @returns 完整对话记录
|
||||
*/
|
||||
async runConversation(
|
||||
agent1Name: string,
|
||||
agent2Name: string,
|
||||
maxTurns: number,
|
||||
topic: string,
|
||||
): Promise<string> {
|
||||
const agent1 = this.agents.get(agent1Name);
|
||||
const agent2 = this.agents.get(agent2Name);
|
||||
|
||||
if (!agent1) {
|
||||
return `错误: 子 Agent "${agent1Name}" 不存在。可用的 Agent: ${this.listNames()}`;
|
||||
}
|
||||
if (!agent2) {
|
||||
return `错误: 子 Agent "${agent2Name}" 不存在。可用的 Agent: ${this.listNames()}`;
|
||||
}
|
||||
|
||||
const transcript: string[] = [];
|
||||
transcript.push(`=== 对话开始: ${agent1Name} vs ${agent2Name},主题: ${topic},轮次: ${maxTurns} ===\n`);
|
||||
|
||||
// 第一轮:agent1 发起对话
|
||||
let currentMessage = `请就以下话题开始对话:${topic}。你是对话的发起方,请先发言。`;
|
||||
let speaker = agent1;
|
||||
let listener = agent2;
|
||||
|
||||
for (let turn = 1; turn <= maxTurns; turn++) {
|
||||
// 当前发言者回复
|
||||
const response = await speaker.run(currentMessage);
|
||||
const line = `[${speaker.name}]: ${response}`;
|
||||
console.log(line);
|
||||
transcript.push(line);
|
||||
|
||||
// 将回复传给另一方
|
||||
currentMessage = `[${speaker.name}]: ${response}\n请回复。`;
|
||||
|
||||
// 交换发言者
|
||||
[speaker, listener] = [listener, speaker];
|
||||
}
|
||||
|
||||
transcript.push(`\n=== 对话结束 ===`);
|
||||
return transcript.join('\n');
|
||||
}
|
||||
|
||||
private listNames(): string {
|
||||
return Array.from(this.agents.keys()).join(', ') || '(无)';
|
||||
}
|
||||
}
|
||||
|
||||
/** 全局单例 */
|
||||
export const subAgentManager = new SubAgentManager();
|
||||
@ -1,57 +0,0 @@
|
||||
import { Tool } from '../types/index.js';
|
||||
import { subAgentManager } from './manager.js';
|
||||
|
||||
/**
|
||||
* spawn_agent — 创建一个指定角色和名称的子 Agent
|
||||
*/
|
||||
export const spawnAgentTool: Tool = {
|
||||
name: 'spawn_agent',
|
||||
description: '创建一个子Agent,指定其名称和角色/系统提示词。用于创建具有特定人设的对话角色(如销售、顾客等)。',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: '子Agent的唯一名称,如"sales"、"customer"' },
|
||||
role_prompt: { type: 'string', description: '子Agent的角色描述/系统提示词,如"你是一个热情的汽车销售"' },
|
||||
},
|
||||
required: ['name', 'role_prompt'],
|
||||
},
|
||||
execute: async (args) => {
|
||||
const { name, role_prompt } = args;
|
||||
try {
|
||||
const agent = subAgentManager.spawn(name as string, role_prompt as string);
|
||||
return `子Agent "${agent.name}" 创建成功。`;
|
||||
} catch (e: any) {
|
||||
return `创建失败: ${e.message}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* run_conversation — 运行两个子 Agent 之间的多轮对话
|
||||
*/
|
||||
export const runConversationTool: Tool = {
|
||||
name: 'run_conversation',
|
||||
description: '在两个已创建的子Agent之间运行多轮对话。一方先发起,另一方回应,交替进行。返回完整对话记录。',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
agent1: { type: 'string', description: '发起方Agent名称(先说话的那个)' },
|
||||
agent2: { type: 'string', description: '回应方Agent名称' },
|
||||
max_turns: { type: 'number', description: '最大对话轮次,如5表示agent1发起 + 4轮交替 = 共5次发言' },
|
||||
topic: { type: 'string', description: '对话主题/场景描述,如"汽车购买谈判"' },
|
||||
},
|
||||
required: ['agent1', 'agent2', 'max_turns', 'topic'],
|
||||
},
|
||||
execute: async (args) => {
|
||||
const { agent1, agent2, max_turns, topic } = args;
|
||||
return await subAgentManager.runConversation(
|
||||
agent1 as string,
|
||||
agent2 as string,
|
||||
max_turns as number,
|
||||
topic as string,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
/** 所有 sub-agent 管理工具(仅供主 Agent 使用) */
|
||||
export const subAgentTools: Tool[] = [spawnAgentTool, runConversationTool];
|
||||
@ -1,16 +0,0 @@
|
||||
type Message = { role: 'system' | 'user' | 'assistant'; content: string };
|
||||
|
||||
export class ContextManager {
|
||||
private messages: Message[] = [];
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
add(message: Message) {
|
||||
this.messages.push(message);
|
||||
}
|
||||
|
||||
getMessages(): Message[] {
|
||||
return [...this.messages];
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
type EventName = 'process:start' | 'agent:start' | 'step:before' | 'tool:before' | 'tool:after' | 'agent:end';
|
||||
type Listener = (data: any) => void | Promise<void>;
|
||||
|
||||
export class HookBus {
|
||||
private listeners = new Map<EventName, Listener[]>();
|
||||
|
||||
on(event: EventName, fn: Listener) {
|
||||
if (!this.listeners.has(event)) {
|
||||
this.listeners.set(event, []);
|
||||
}
|
||||
this.listeners.get(event)!.push(fn);
|
||||
}
|
||||
|
||||
async emit(event: EventName, data: any) {
|
||||
const fns = this.listeners.get(event);
|
||||
if (!fns) return;
|
||||
for (const fn of fns) {
|
||||
await fn(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const hooks = new HookBus();
|
||||
@ -1,30 +0,0 @@
|
||||
import type { HookBus } from './index.js';
|
||||
|
||||
export default (hooks: HookBus) => {
|
||||
// hooks.on('agent:start', async (data) => {
|
||||
// console.log('🚀 ~ agent:start ~ data:', data);
|
||||
// });
|
||||
|
||||
// hooks.on('step:before', async (data) => {
|
||||
// console.log('🚀 ~ step:before ~ data:', data);
|
||||
// });
|
||||
|
||||
hooks.on('tool:before', async (data) => {
|
||||
const { toolCall: tc, agentName } = data;
|
||||
const prefix = agentName ? `[${agentName}] ` : '';
|
||||
console.log(` ${prefix}[工具调用] ${tc.function.name}(${tc.function.arguments})`);
|
||||
});
|
||||
|
||||
hooks.on('tool:after', async (data) => {
|
||||
const { toolCall: tc, result, agentName } = data;
|
||||
const prefix = agentName ? `[${agentName}] ` : '';
|
||||
console.log(` ${prefix}[工具] ${tc.function.name}(${tc.function.arguments}) -> ${result}`);
|
||||
});
|
||||
|
||||
hooks.on('agent:end', async (data) => {
|
||||
const { reply } = data;
|
||||
// reply 可能是字符串(Agent.run 返回值)或 OpenAI message 对象
|
||||
const content = typeof reply === 'string' ? reply : reply.content;
|
||||
console.log('助手:', content);
|
||||
});
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
// 从内置hooks目录中获取所有hooks并注册到HookBus
|
||||
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { hooks } from './index.js';
|
||||
|
||||
// 这里可以自动扫描hooks目录下的所有文件并导入它们,假设每个文件都默认导出一个函数来注册hook
|
||||
|
||||
const getAllHooks = async () => {
|
||||
// 动态读取hooks目录下的所有 .hooks.ts 结尾的文件
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const hooksDir = path.join(__dirname); // hooks 目录即当前目录
|
||||
const hookFiles = fs.readdirSync(hooksDir).filter(file => file.endsWith('.hooks.ts'));
|
||||
return Promise.all(
|
||||
hookFiles.map(async (file) => {
|
||||
const mod = await import(pathToFileURL(path.join(hooksDir, file)).href);
|
||||
return mod.default;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export const registerHooks = async () => {
|
||||
const hookModules = await getAllHooks();
|
||||
for (const hookModule of hookModules) {
|
||||
// 每个hook模块默认导出一个函数,调用它并传入hooks实例
|
||||
hookModule(hooks);
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
import { Agent } from './agents/index.js';
|
||||
import { getOrchestratorTools } from './tools/registry.js';
|
||||
import { PROMPTS } from './prompts/system.js';
|
||||
import * as readline from 'node:readline/promises';
|
||||
import { hooks } from './hooks/index.js';
|
||||
import { registerHooks } from './hooks/registry.js';
|
||||
import { knowledgeBase } from './knowledge/knowledge-base.js';
|
||||
|
||||
await registerHooks();
|
||||
await knowledgeBase.load();
|
||||
|
||||
const promptName = process.argv[2] || 'orchestrator';
|
||||
const systemPrompt = PROMPTS[promptName as keyof typeof PROMPTS];
|
||||
|
||||
if (!systemPrompt) {
|
||||
console.error(`未知提示词: ${promptName},可选: ${Object.keys(PROMPTS).join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 主 orchestrator Agent,拥有全部工具(包括 spawn_agent / run_conversation)
|
||||
const mainAgent = new Agent({
|
||||
name: 'Orchestrator',
|
||||
systemPrompt,
|
||||
tools: getOrchestratorTools(),
|
||||
});
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
console.log(`提示词模式: ${promptName},输入 "exit" 退出。\n`);
|
||||
|
||||
while (true) {
|
||||
const userInput = await rl.question('我: ');
|
||||
if (userInput.toLowerCase() === 'exit') break;
|
||||
|
||||
hooks.emit('agent:start', { userInput });
|
||||
|
||||
const reply = await mainAgent.run(userInput);
|
||||
|
||||
hooks.emit('agent:end', { userInput, reply });
|
||||
// agent:end hook 会打印回复,这里不需要重复打印
|
||||
}
|
||||
|
||||
rl.close();
|
||||
@ -1,128 +0,0 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
/** 知识库中的一个文档块 */
|
||||
interface Chunk {
|
||||
/** 来源文件名 */
|
||||
source: string;
|
||||
/** 块内文本 */
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 轻量知识库:从 knowledge/ 目录加载 .md 文件,按 ## 标题分块,
|
||||
* 提供基于关键词匹配的检索能力。
|
||||
*
|
||||
* 用法:
|
||||
* const kb = new KnowledgeBase('./knowledge');
|
||||
* await kb.load();
|
||||
* const results = kb.search('Agent 工具调用');
|
||||
*/
|
||||
export class KnowledgeBase {
|
||||
private chunks: Chunk[] = [];
|
||||
private knowledgeDir: string;
|
||||
|
||||
constructor(knowledgeDir: string) {
|
||||
this.knowledgeDir = knowledgeDir;
|
||||
}
|
||||
|
||||
/** 加载 knowledgeDir 下所有 .md 文件并分块 */
|
||||
async load(): Promise<void> {
|
||||
this.chunks = [];
|
||||
|
||||
if (!fs.existsSync(this.knowledgeDir)) {
|
||||
console.warn(`知识库目录不存在: ${this.knowledgeDir}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const files = fs
|
||||
.readdirSync(this.knowledgeDir)
|
||||
.filter((f) => f.endsWith('.md'));
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(this.knowledgeDir, file);
|
||||
const raw = fs.readFileSync(filePath, 'utf-8');
|
||||
const fileChunks = this.splitChunks(raw, file);
|
||||
this.chunks.push(...fileChunks);
|
||||
}
|
||||
|
||||
console.log(`知识库已加载: ${this.chunks.length} 个块,来自 ${files.length} 个文件`);
|
||||
}
|
||||
|
||||
/** 按 ## 标题将文档拆分为块 */
|
||||
private splitChunks(raw: string, source: string): Chunk[] {
|
||||
const blocks = raw.split(/(?=^## )/m);
|
||||
return blocks
|
||||
.map((b) => b.trim())
|
||||
.filter(Boolean)
|
||||
.map((content) => ({ source, content }));
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于关键词匹配搜索,返回相关块(按相关性降序)
|
||||
*
|
||||
* 算法:将查询分词,统计每个块命中关键词的次数,
|
||||
* 同时给标题匹配额外加权。
|
||||
*/
|
||||
search(query: string, topK: number = 3): Chunk[] {
|
||||
const keywords = this.tokenize(query);
|
||||
if (keywords.length === 0) return [];
|
||||
|
||||
const scored = this.chunks.map((chunk) => {
|
||||
const lower = chunk.content.toLowerCase();
|
||||
let score = 0;
|
||||
for (const kw of keywords) {
|
||||
// 标题行命中加权 ×3
|
||||
const headlineRegex = /^## .+$/gm;
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = headlineRegex.exec(chunk.content)) !== null) {
|
||||
if (match[0].toLowerCase().includes(kw)) {
|
||||
score += 3;
|
||||
}
|
||||
}
|
||||
// 正文命中
|
||||
const count = (lower.match(new RegExp(this.escapeRegex(kw), 'gi')) || []).length;
|
||||
score += count;
|
||||
}
|
||||
// 标题匹配额外加分
|
||||
return { chunk, score };
|
||||
});
|
||||
|
||||
return scored
|
||||
.filter((s) => s.score > 0)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, topK)
|
||||
.map((s) => s.chunk);
|
||||
}
|
||||
|
||||
/** 中文 + 英文简单分词 */
|
||||
private tokenize(text: string): string[] {
|
||||
// 按空白/标点拆分,过滤长度 ≤1 的词
|
||||
return text
|
||||
.split(/[\s,,。.!!??::;;、]+/)
|
||||
.map((t) => t.toLowerCase().trim())
|
||||
.filter((t) => t.length > 1);
|
||||
}
|
||||
|
||||
private escapeRegex(s: string): string {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
/** 获取知识库摘要(供 Agent 概览) */
|
||||
summary(): string {
|
||||
if (this.chunks.length === 0) return '知识库为空';
|
||||
const sources = [...new Set(this.chunks.map((c) => c.source))];
|
||||
const titles = this.chunks
|
||||
.map((c) => {
|
||||
const m = c.content.match(/^## (.+)$/m);
|
||||
return m ? ` - ${m[1]} (${c.source})` : null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
return `知识库包含 ${sources.length} 个文档:\n${titles.join('\n')}`;
|
||||
}
|
||||
}
|
||||
|
||||
/** 全局单例 */
|
||||
export const knowledgeBase = new KnowledgeBase(
|
||||
path.join(process.cwd(), 'knowledge'),
|
||||
);
|
||||
@ -1,53 +0,0 @@
|
||||
import { Tool } from '../types/index.js';
|
||||
import { knowledgeBase } from './knowledge-base.js';
|
||||
|
||||
/**
|
||||
* search_knowledge — 在知识库中检索相关信息
|
||||
*/
|
||||
export const searchKnowledgeTool: Tool = {
|
||||
name: 'search_knowledge',
|
||||
description:
|
||||
'在本地知识库中搜索与查询相关的文档片段。当你需要查找项目文档、技术说明、业务规则等存储在知识库中的信息时使用此工具。',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: {
|
||||
type: 'string',
|
||||
description: '搜索关键词或问题,如"Agent 工具调用流程"、"如何创建子Agent"',
|
||||
},
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
execute: async (args) => {
|
||||
const { query } = args;
|
||||
const results = knowledgeBase.search(query as string, 3);
|
||||
if (results.length === 0) {
|
||||
return `未找到与 "${query}" 相关的知识。当前知识库摘要:\n${knowledgeBase.summary()}`;
|
||||
}
|
||||
return results
|
||||
.map(
|
||||
(r, i) =>
|
||||
`--- 结果 ${i + 1} (来源: ${r.source}) ---\n${r.content}`,
|
||||
)
|
||||
.join('\n\n');
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* list_knowledge — 列出知识库中所有文档和章节
|
||||
*/
|
||||
export const listKnowledgeTool: Tool = {
|
||||
name: 'list_knowledge',
|
||||
description: '列出知识库中所有文档及其章节标题,用于了解知识库包含哪些内容。',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
required: [],
|
||||
},
|
||||
execute: async () => {
|
||||
return knowledgeBase.summary();
|
||||
},
|
||||
};
|
||||
|
||||
/** 知识库相关工具集 */
|
||||
export const knowledgeTools: Tool[] = [searchKnowledgeTool, listKnowledgeTool];
|
||||
@ -1,20 +0,0 @@
|
||||
import OpenAI from 'openai';
|
||||
import 'dotenv/config';
|
||||
|
||||
const client = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
baseURL: process.env.OPENAI_BASE_URL,
|
||||
});
|
||||
|
||||
export async function chat(
|
||||
messages: { role: string; content: string }[],
|
||||
tools?: any[] // OpenAI 格式的工具定义数组
|
||||
) {
|
||||
const response = await client.chat.completions.create({
|
||||
model: 'deepseek-v4-pro',
|
||||
messages: messages as any,
|
||||
temperature: 0.7,
|
||||
...(tools && { tools }),
|
||||
});
|
||||
return response.choices[0]!.message!;
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
export const ORCHESTRATOR_PROMPT = `你是一个智能编排助手,能够创建子Agent并编排它们之间的多轮对话。
|
||||
|
||||
## 你拥有的工具
|
||||
1. **spawn_agent** — 创建一个子Agent,需要指定名称和角色描述。用于根据用户需求创建具有特定人设的角色(如销售、顾客、谈判者等)。
|
||||
2. **run_conversation** — 在两个已创建的子Agent之间运行多轮对话。需要指定发起方、回应方、对话轮次和主题。
|
||||
|
||||
## 工作流程
|
||||
当用户要求创建Agent并进行对话时:
|
||||
1. 分析用户需求,确定需要哪些角色
|
||||
2. 使用 spawn_agent 分别创建每个子Agent,为它们编写合适的角色描述/系统提示词
|
||||
3. 使用 run_conversation 运行对话,设定合理的轮次和主题
|
||||
4. 对话结束后,根据对话记录进行简要总结
|
||||
|
||||
## 重要规则
|
||||
- 子Agent的角色提示词要具体、生动,包含角色背景、性格特点和目标
|
||||
- 对话轮次根据用户要求设定,默认5-10轮
|
||||
- 总结时聚焦关键转折点、各方策略和最终结果
|
||||
- 使用中文回复`;
|
||||
@ -1,16 +0,0 @@
|
||||
export const REACT_SYSTEM_PROMPT = `你是一个自主智能体,能够使用工具来完成目标。
|
||||
|
||||
## 工作方式
|
||||
你需要反复执行以下步骤,直到目标完成:
|
||||
|
||||
1. **思考**:分析当前状态,决定下一步行动。
|
||||
2. **行动**:调用一个工具,或者给出最终答案。
|
||||
|
||||
## 行动格式
|
||||
- 如果需要调用工具,只返回工具调用的 JSON,不要其他内容。
|
||||
|
||||
## 重要规则
|
||||
- 如果工具返回了错误,分析错误并尝试修复,不要重复相同的错误调用。
|
||||
- 如果连续三次调用没有进展,给出最终答案并说明遇到困难。
|
||||
- 诚实:如果无法完成,直接说明,不要编造。
|
||||
- 使用中文回复。`;
|
||||
@ -1,17 +0,0 @@
|
||||
import { REACT_SYSTEM_PROMPT } from './reAct.js';
|
||||
import { ORCHESTRATOR_PROMPT } from './orchestrator.js';
|
||||
|
||||
export const PROMPTS = {
|
||||
default: '你是一个直爽的代码审查员,回答尽量简洁。',
|
||||
toxic: '你是一个毒舌代码审查员,用讽刺的语气表达。',
|
||||
json: `你是一个 API 格式化助手。你的回答必须是纯 JSON,不要加任何解释。
|
||||
{
|
||||
"answer": "你的回答",
|
||||
"confidence": 0.0-1.0 之间的数字
|
||||
}`,
|
||||
agent: `你是一个有工具调用能力的助手。当需要查询信息/执行计算或者玩猜谜游戏时,请使用提供的工具。
|
||||
如果调用了工具,根据工具执行结果给出答案。
|
||||
如果不需要工具,直接回答即可。回答简洁。`,
|
||||
reAct: REACT_SYSTEM_PROMPT,
|
||||
orchestrator: ORCHESTRATOR_PROMPT,
|
||||
} as const;
|
||||
@ -1,22 +0,0 @@
|
||||
import { Tool } from '../types/index.js';
|
||||
|
||||
export const calculatorTool: Tool = {
|
||||
name: 'calculate',
|
||||
description: '执行数学计算,支持加减乘除和括号',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
expression: { type: 'string', description: '数学表达式,如"2+3*4"' },
|
||||
},
|
||||
required: ['expression'],
|
||||
},
|
||||
execute: async (args) => {
|
||||
try {
|
||||
// 安全警告:生产环境绝不可以用 eval
|
||||
const result = eval(args.expression);
|
||||
return `${args.expression} = ${result}`;
|
||||
} catch (e: any) {
|
||||
return `计算错误: ${e.message}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -1,20 +0,0 @@
|
||||
import { Tool } from '../types/index.js';
|
||||
|
||||
export const guessTool: Tool = {
|
||||
name: 'guess_number',
|
||||
description: '猜一个1到100之间的整数。返回“大了”、“小了”或“猜对了”。',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
number: { type: 'number', description: '你猜的数字' },
|
||||
},
|
||||
required: ['number'],
|
||||
},
|
||||
execute: async (args) => {
|
||||
// 答案写死在工具内部,模型绝对不知道
|
||||
const answer = 67;
|
||||
const guess = args.number;
|
||||
if (guess === answer) return '猜对了!';
|
||||
return guess > answer ? '大了' : '小了';
|
||||
},
|
||||
};
|
||||
@ -1,36 +0,0 @@
|
||||
import { weatherTool } from './weather.js';
|
||||
import { calculatorTool } from './calculator.js';
|
||||
import { guessTool } from './guess.js';
|
||||
import { Tool } from '../types/index.js';
|
||||
import { subAgentTools } from '../agents/tools.js';
|
||||
import { knowledgeTools } from '../knowledge/search-tool.js';
|
||||
|
||||
const baseTools: Tool[] = [weatherTool, calculatorTool, guessTool, ...knowledgeTools];
|
||||
|
||||
/** 所有工具(基础工具 + sub-agent 管理工具),供主 orchestrator Agent 使用 */
|
||||
const allTools: Tool[] = [...baseTools, ...subAgentTools];
|
||||
|
||||
/** 返回基础工具列表(供子 Agent 使用,不含 sub-agent 管理工具以防递归) */
|
||||
export function getTools(): Tool[] {
|
||||
return baseTools;
|
||||
}
|
||||
|
||||
/** 返回全部工具列表(供主 orchestrator Agent 使用) */
|
||||
export function getOrchestratorTools(): Tool[] {
|
||||
return allTools;
|
||||
}
|
||||
|
||||
export function getOpenAITools() {
|
||||
return baseTools.map((t) => ({
|
||||
type: 'function' as const,
|
||||
function: {
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.parameters,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export function findTool(name: string): Tool | undefined {
|
||||
return allTools.find((t) => t.name === name);
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import { Tool } from '../types/index.js';
|
||||
|
||||
export const weatherTool: Tool = {
|
||||
name: 'get_weather',
|
||||
description: '获取指定城市的当前天气信息',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
city: { type: 'string', description: '城市名称,如"北京"、"上海"' },
|
||||
},
|
||||
required: ['city'],
|
||||
},
|
||||
execute: async (args) => {
|
||||
// 模拟异步 API 调用
|
||||
const weathers = ['晴', '多云', '小雨', '阴天'];
|
||||
const picked = weathers[Math.floor(Math.random() * weathers.length)];
|
||||
return `城市:${args.city},天气:${picked},温度:${Math.floor(Math.random() * 15 + 15)}°C`;
|
||||
},
|
||||
};
|
||||
@ -1,14 +0,0 @@
|
||||
export interface Tool {
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: {
|
||||
type: 'object';
|
||||
properties: Record<string, {
|
||||
type: string;
|
||||
description: string;
|
||||
enum?: string[];
|
||||
}>;
|
||||
required: string[];
|
||||
};
|
||||
execute: (args: Record<string, any>) => Promise<string> | string;
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user