feat: 添加钩子
This commit is contained in:
parent
a0023c49f4
commit
cc79596b1e
23
src/hooks/index.ts
Normal file
23
src/hooks/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
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();
|
||||
26
src/hooks/log.hooks.ts
Normal file
26
src/hooks/log.hooks.ts
Normal file
@ -0,0 +1,26 @@
|
||||
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, result } = data;
|
||||
console.log(` [工具调用] ${tc.function.name}(${tc.function.arguments})`);
|
||||
});
|
||||
|
||||
hooks.on('tool:after', async (data) => {
|
||||
const { toolCall: tc, result } = data;
|
||||
console.log(` [工具] ${tc.function.name}(${tc.function.arguments}) -> ${result}`);
|
||||
});
|
||||
|
||||
hooks.on('agent:end', async (data) => {
|
||||
const { reply } = data;
|
||||
console.log('助手:', reply.content);
|
||||
});
|
||||
}
|
||||
28
src/hooks/registry.ts
Normal file
28
src/hooks/registry.ts
Normal file
@ -0,0 +1,28 @@
|
||||
// 从内置hooks目录中获取所有hooks并注册到HookBus
|
||||
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { hooks } from './index.js';
|
||||
|
||||
// 这里可以自动扫描hooks目录下的所有文件并导入它们,假设每个文件都默认导出一个函数来注册hook
|
||||
|
||||
const getAllHooks = async () => {
|
||||
// 动态读取hooks目录下的所有 .hooks.ts 结尾的文件
|
||||
const hooksDir = path.resolve('src/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);
|
||||
}
|
||||
}
|
||||
13
src/index.ts
13
src/index.ts
@ -3,6 +3,10 @@ import { chat } from './llm.js';
|
||||
import { PROMPTS } from './prompts/system.js';
|
||||
import { getOpenAITools, findTool } from './tools/registry.js';
|
||||
import * as readline from 'node:readline/promises';
|
||||
import { hooks } from './hooks/index.js';
|
||||
import { registerHooks } from './hooks/registry.js';
|
||||
|
||||
await registerHooks();
|
||||
|
||||
const promptName = process.argv[2] || 'reAct';
|
||||
const systemPrompt = PROMPTS[promptName as keyof typeof PROMPTS];
|
||||
@ -28,21 +32,24 @@ while (true) {
|
||||
|
||||
context.add({ role: 'user', content: userInput });
|
||||
|
||||
hooks.emit('agent:start', { userInput });
|
||||
|
||||
// 第一轮:带工具定义调用
|
||||
const tools = getOpenAITools();
|
||||
let reply = await chat(context.getMessages(), tools);
|
||||
|
||||
// 如果模型要求调用工具,执行并在上下文中构造 tool 消息
|
||||
while (reply.tool_calls && reply.tool_calls.length > 0) {
|
||||
hooks.emit('step:before', { userInput, reply });
|
||||
// 先把 assistant 消息(含 tool_calls)加入上下文
|
||||
context.add({ role: 'assistant', content: '', tool_calls: reply.tool_calls } as any);
|
||||
|
||||
for (const tc of reply.tool_calls) {
|
||||
hooks.emit('tool:before', { toolCall: tc });
|
||||
const tool = findTool(tc.function.name);
|
||||
const args = JSON.parse(tc.function.arguments);
|
||||
const result = await tool!.execute(args);
|
||||
console.log(` [工具] ${tc.function.name}(${tc.function.arguments}) -> ${result}`);
|
||||
|
||||
hooks.emit('tool:after', { toolCall: tc, result });
|
||||
// tool 消息:必须回传 tool_call_id
|
||||
context.add({
|
||||
role: 'tool',
|
||||
@ -55,7 +62,7 @@ while (true) {
|
||||
reply = await chat(context.getMessages(), tools);
|
||||
}
|
||||
|
||||
console.log('助手:', reply.content);
|
||||
hooks.emit('agent:end', { userInput, reply });
|
||||
context.add({ role: 'assistant', content: reply.content! });
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user