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 { ContextManager } from './context.js';
|
||||||
import { chat } from './llm.js';
|
import { chat } from './llm.js';
|
||||||
import { PROMPTS } from './prompts/system.js';
|
import { PROMPTS } from './prompts/system.js';
|
||||||
|
import { getOpenAITools, findTool } from './tools/registry.js';
|
||||||
import * as readline from 'node:readline/promises';
|
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];
|
const systemPrompt = PROMPTS[promptName as keyof typeof PROMPTS];
|
||||||
|
|
||||||
if (!systemPrompt) {
|
if (!systemPrompt) {
|
||||||
@ -26,7 +27,34 @@ while (true) {
|
|||||||
if (userInput.toLowerCase() === 'exit') break;
|
if (userInput.toLowerCase() === 'exit') break;
|
||||||
|
|
||||||
context.add({ role: 'user', content: userInput });
|
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);
|
console.log('助手:', reply.content);
|
||||||
context.add({ role: 'assistant', content: reply.content! });
|
context.add({ role: 'assistant', content: reply.content! });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,15 @@ const client = new OpenAI({
|
|||||||
baseURL: process.env.OPENAI_BASE_URL,
|
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({
|
const response = await client.chat.completions.create({
|
||||||
model: 'deepseek-v4-pro',
|
model: 'deepseek-v4-pro',
|
||||||
messages: messages as any,
|
messages: messages as any,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
|
...(tools && { tools }),
|
||||||
});
|
});
|
||||||
return response.choices[0]!.message!;
|
return response.choices[0]!.message!;
|
||||||
}
|
}
|
||||||
@ -6,4 +6,6 @@ export const PROMPTS = {
|
|||||||
"answer": "你的回答",
|
"answer": "你的回答",
|
||||||
"confidence": 0.0-1.0 之间的数字
|
"confidence": 0.0-1.0 之间的数字
|
||||||
}`,
|
}`,
|
||||||
|
agent: `你是一个有工具调用能力的助手。当需要查询信息或执行计算时,请使用提供的工具。
|
||||||
|
如果不需要工具,直接回答即可。回答简洁。`,
|
||||||
} as const;
|
} 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