src/index.ts
, src/lib/client/cli.ts
chalk
tô màu in trên terminal.data/history.json
để duy trì ngữ cảnh qua các lần tương tác.src/lib/openai.ts
src/lib/system-prompt.md
.gpt-4o
hỗ trợ gọi tool nhanh và ổn định.src/lib/tools/
chứa từng tool riêng biệt.src/lib/tool-bridge.ts
runCli()
xử lý yêu cầu, trả kết quả.getUserInput()
dùng Node.js readline
để lấy nhập liệu.cli-html
chuyển sang ANSI escape sequence cho màu sắc hiển thị đẹp mắt trong terminal.Thành phần | Mô tả |
---|---|
system prompt | Hướng dẫn chi tiết cho GPT: phong cách trả lời, khi nào gọi tool, cách format HTML output |
user messages | Lịch sử hội thoại và câu hỏi mới |
tools | Danh sách function schemas công cụ có thể sử dụng |
model | GPT-4o (tối ưu gọi function) |
tool_choice | 'auto' để GPT tự quyết định khi nào gọi tool |
<div style="color: #00bfae; margin-bottom: 4px;">Section Title</div><ul> <li style="margin-bottom:6px;"> <a href="https://example.com" style="color:#1e90ff; text-decoration: underline;">Link Title</a> <div style="color: #888;">Short description</div> </li></ul>
get
lấy nội dung một trang web:export interface GetPageParams { url: string; }export interface GetPageReturn { url: string; html: string; error?: string; }
export const getTool = createToolType<GetPageParams, GetPageReturn>( 'get', 'Fetch the HTML content of a web page using Playwright. Input a URL and receive the full HTML.', createOpenAIToolSchema( 'get', 'Fetch the HTML content of a web page using Playwright.', { url: { type: 'string', description: 'The URL of the web page to fetch.' } }, ['url'] ));
export const getHandler: ToolHandler<GetPageParams, GetPageReturn> = async ({ url }) => { // Sử dụng Playwright để fetch HTML, chặn tải hình ảnh/stylesheet. // Trả về text nội dung hoặc lỗi nếu có.};
class ToolRegistryManager { private registry = new Map<string, ToolRegistryEntry>(); register(tool: ToolType, handler: ToolHandler) { this.registry.set(tool.name, { tool, handler }); } async execute(name: string, params: ToolParameters): Promise<ToolExecutionResult> { const entry = this.registry.get(name); if (!entry) return { success: false, error: `Tool '${name}' not found` }; try { const data = await entry.handler(params); return { success: true, data }; } catch(error) { return { success: false, error: String(error) }; } } getAllOpenAITools(): ChatCompletionTool[] { return Array.from(this.registry.values()).map(e => e.tool.openaiTool); }}
async runAssistantWithTools(messages) { this.initialize(); // đăng ký các tool let currentMessages = messages;
while (true) { const assistantResponse = await runAssistant(currentMessages); if (assistantResponse.tool_calls?.length) { const results = []; for (const call of assistantResponse.tool_calls) { const result = await this.executeToolCall(call); results.push(result); } currentMessages = [...currentMessages, assistantResponse, ...results]; } else { return assistantResponse; // câu trả lời cuối } }}
git clone https://github.com/designly1/gptagent.gitcd gptagent
pnpm installpnpm exec playwright install
.env
với khóa API:OPENAI_API_KEY="sk-..."GEOCODE_API_KEY="..."SEARXNG_HOSTNAME=localhostSEARXNG_BASE_URL=http://localhost:8080/SEARXNG_SECRET=<32_char_hex_key>DEBUG=0
pnpm dev
Enter your request (or "." to exit): How’s the weather in Tokyo today?
pnpm run clear:history
DEBUG=1
thì ghi log thao tác tool chi tiếtcaption_image
nhận đường dẫn file ảnh ( imagePath
).Enter your request: Describe what’s in the image at "./cat_photo.jpg".
caption_image
với tham số file ảnh, nhận mô tả và trả lời người dùng.// def.tsexport interface ImageCaptionParams { imagePath: string; }export interface ImageCaptionResult { caption: string; }export const imageCaptionTool = createToolType<ImageCaptionParams, ImageCaptionResult>( 'caption_image', 'Analyze an image file and return a descriptive caption.', createOpenAIToolSchema( 'caption_image', 'Analyze an image file and return a descriptive caption.', { imagePath: { type: 'string', description: 'Path to an image file.' } }, ['imagePath'] ));
// handler.tsimport fs from 'fs';export const imageCaptionHandler: ToolHandler<ImageCaptionParams, ImageCaptionResult> = async ({ imagePath }) => { const fileData = fs.readFileSync(imagePath); if (!fileData) throw new Error(`Image file not found: ${imagePath}`); // TODO: gọi API hoặc ML để tạo caption const caption = "<description of the image>"; return { caption };};
transcribe_audio
nhận audioPath
.function_call='auto'
của GPT-4 để lựa chọn gọi tool thông minh.