feat: 调用工具
This commit is contained in:
parent
aa6ab0d379
commit
c2a906773f
32
src/index.ts
32
src/index.ts
@ -1,9 +1,10 @@
|
||||
import { ContextManager } from './context.js';
|
||||
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';
|
||||
|
||||
const promptName = process.argv[2] || 'default';
|
||||
const promptName = process.argv[2] || 'agent';
|
||||
const systemPrompt = PROMPTS[promptName as keyof typeof PROMPTS];
|
||||
|
||||
if (!systemPrompt) {
|
||||
@ -26,7 +27,34 @@ while (true) {
|
||||
if (userInput.toLowerCase() === 'exit') break;
|
||||
|
||||
context.add({ role: 'user', content: userInput });
|
||||
const reply = await chat(context.getMessages());
|
||||
|
||||
// 第一轮:带工具定义调用
|
||||
const tools = getOpenAITools();
|
||||
let reply = await chat(context.getMessages(), tools);
|
||||
|
||||
// 如果模型要求调用工具,执行并在上下文中构造 tool 消息
|
||||
if (reply.tool_calls && reply.tool_calls.length > 0) {
|
||||
// 先把 assistant 消息(含 tool_calls)加入上下文
|
||||
context.add({ role: 'assistant', content: '', tool_calls: reply.tool_calls } as any);
|
||||
|
||||
for (const tc of reply.tool_calls) {
|
||||
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}`);
|
||||
|
||||
// tool 消息:必须回传 tool_call_id
|
||||
context.add({
|
||||
role: 'tool',
|
||||
tool_call_id: tc.id,
|
||||
content: result,
|
||||
} as any);
|
||||
}
|
||||
|
||||
// 继续调用,看模型还需要不需要更多工具
|
||||
reply = await chat(context.getMessages(), tools);
|
||||
}
|
||||
|
||||
console.log('助手:', reply.content);
|
||||
context.add({ role: 'assistant', content: reply.content! });
|
||||
}
|
||||
|
||||
@ -6,11 +6,15 @@ const client = new OpenAI({
|
||||
baseURL: process.env.OPENAI_BASE_URL,
|
||||
});
|
||||
|
||||
export async function chat(messages: { role: string; content: string }[]) {
|
||||
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!;
|
||||
}
|
||||
@ -6,4 +6,6 @@ export const PROMPTS = {
|
||||
"answer": "你的回答",
|
||||
"confidence": 0.0-1.0 之间的数字
|
||||
}`,
|
||||
agent: `你是一个有工具调用能力的助手。当需要查询信息或执行计算时,请使用提供的工具。
|
||||
如果不需要工具,直接回答即可。回答简洁。`,
|
||||
} as const;
|
||||
22
src/tools/calculator.ts
Normal file
22
src/tools/calculator.ts
Normal file
@ -0,0 +1,22 @@
|
||||
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}`;
|
||||
}
|
||||
},
|
||||
};
|
||||
24
src/tools/registry.ts
Normal file
24
src/tools/registry.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { weatherTool } from './weather.js';
|
||||
import { calculatorTool } from './calculator.js';
|
||||
import { Tool } from '../types/index.js';
|
||||
|
||||
const tools: Tool[] = [weatherTool, calculatorTool];
|
||||
|
||||
export function getTools(): Tool[] {
|
||||
return tools;
|
||||
}
|
||||
|
||||
export function getOpenAITools() {
|
||||
return tools.map((t) => ({
|
||||
type: 'function' as const,
|
||||
function: {
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.parameters,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export function findTool(name: string): Tool | undefined {
|
||||
return tools.find((t) => t.name === name);
|
||||
}
|
||||
19
src/tools/weather.ts
Normal file
19
src/tools/weather.ts
Normal file
@ -0,0 +1,19 @@
|
||||
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`;
|
||||
},
|
||||
};
|
||||
14
src/types/index.ts
Normal file
14
src/types/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user