| # Code Explanation: simple-agent.js | |
| This file demonstrates **function calling** - the core feature that transforms an LLM from a text generator into an agent that can take actions using tools. | |
| ## Step-by-Step Code Breakdown | |
| ### 1. Import and Setup (Lines 1-7) | |
| ```javascript | |
| import {defineChatSessionFunction, getLlama, LlamaChatSession} from "node-llama-cpp"; | |
| import {fileURLToPath} from "url"; | |
| import path from "path"; | |
| import {PromptDebugger} from "../helper/prompt-debugger.js"; | |
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); | |
| const debug = false; | |
| ``` | |
| - **defineChatSessionFunction**: Key import for creating callable functions | |
| - **PromptDebugger**: Helper for debugging prompts (covered at the end) | |
| - **debug**: Controls verbose logging | |
| ### 2. Initialize and Load Model (Lines 9-17) | |
| ```javascript | |
| const llama = await getLlama({debug}); | |
| const model = await llama.loadModel({ | |
| modelPath: path.join( | |
| __dirname, | |
| "../", | |
| "models", | |
| "Qwen3-1.7B-Q8_0.gguf" | |
| ) | |
| }); | |
| const context = await model.createContext({contextSize: 2000}); | |
| ``` | |
| - Uses Qwen3-1.7B model (good for function calling) | |
| - Sets context size to 2000 tokens explicitly | |
| ### 3. System Prompt for Time Conversion (Lines 20-23) | |
| ```javascript | |
| const systemPrompt = `You are a professional chronologist who standardizes time representations across different systems. | |
| Always convert times from 12-hour format (e.g., "1:46:36 PM") to 24-hour format (e.g., "13:46") without seconds | |
| before returning them.`; | |
| ``` | |
| **Purpose:** | |
| - Defines agent's role and behavior | |
| - Instructs on output format (24-hour, no seconds) | |
| - Ensures consistency in time representation | |
| ### 4. Create Session (Lines 25-28) | |
| ```javascript | |
| const session = new LlamaChatSession({ | |
| contextSequence: context.getSequence(), | |
| systemPrompt, | |
| }); | |
| ``` | |
| Standard session with system prompt. | |
| ### 5. Define a Tool Function (Lines 30-39) | |
| ```javascript | |
| const getCurrentTime = defineChatSessionFunction({ | |
| description: "Get the current time", | |
| params: { | |
| type: "object", | |
| properties: {} | |
| }, | |
| async handler() { | |
| return new Date().toLocaleTimeString(); | |
| } | |
| }); | |
| ``` | |
| **Breaking it down:** | |
| **description:** | |
| - Tells the LLM what this function does | |
| - LLM reads this to decide when to call it | |
| **params:** | |
| - Defines function parameters (JSON Schema format) | |
| - Empty `properties: {}` means no parameters needed | |
| - Type must be "object" even if no properties | |
| **handler:** | |
| - The actual JavaScript function that executes | |
| - Returns current time as string (e.g., "1:46:36 PM") | |
| - Can be async (use await inside) | |
| ### How Function Calling Works | |
| ``` | |
| 1. User asks: "What time is it?" | |
| 2. LLM reads: | |
| - System prompt | |
| - Available functions (getCurrentTime) | |
| - Function description | |
| 3. LLM decides: "I should call getCurrentTime()" | |
| 4. Library executes: handler() | |
| 5. Handler returns: "1:46:36 PM" | |
| 6. LLM receives result as "tool output" | |
| 7. LLM processes: Converts to 24-hour format per system prompt | |
| 8. LLM responds: "13:46" | |
| ``` | |
| ### 6. Register Functions (Line 41) | |
| ```javascript | |
| const functions = {getCurrentTime}; | |
| ``` | |
| - Creates object with all available functions | |
| - Multiple functions: `{getCurrentTime, getWeather, calculate, ...}` | |
| - LLM can choose which function(s) to call | |
| ### 7. Define User Prompt (Line 42) | |
| ```javascript | |
| const prompt = `What time is it right now?`; | |
| ``` | |
| A question that requires using the tool. | |
| ### 8. Execute with Functions (Line 45) | |
| ```javascript | |
| const a1 = await session.prompt(prompt, {functions}); | |
| console.log("AI: " + a1); | |
| ``` | |
| - **{functions}** makes tools available to the LLM | |
| - LLM will automatically call getCurrentTime if needed | |
| - Response includes tool result processed by LLM | |
| ### 9. Debug Prompt Context (Lines 49-55) | |
| ```javascript | |
| const promptDebugger = new PromptDebugger({ | |
| outputDir: './logs', | |
| filename: 'qwen_prompts.txt', | |
| includeTimestamp: true, | |
| appendMode: false | |
| }); | |
| await promptDebugger.debugContextState({session, model}); | |
| ``` | |
| **What this does:** | |
| - Saves the entire prompt sent to the model | |
| - Shows exactly what the LLM sees (including function definitions) | |
| - Useful for debugging why model does/doesn't call functions | |
| - Writes to `./logs/qwen_prompts_[timestamp].txt` | |
| ### 10. Cleanup (Lines 58-61) | |
| ```javascript | |
| session.dispose() | |
| context.dispose() | |
| model.dispose() | |
| llama.dispose() | |
| ``` | |
| Standard cleanup. | |
| ## Key Concepts Demonstrated | |
| ### 1. Function Calling (Tool Use) | |
| This is what makes it an "agent": | |
| ``` | |
| Without tools: With tools: | |
| LLM β Text only LLM β Can take actions | |
| β | |
| Call functions | |
| Access data | |
| Execute code | |
| ``` | |
| ### 2. Function Definition Pattern | |
| ```javascript | |
| defineChatSessionFunction({ | |
| description: "What the function does", // LLM reads this | |
| params: { // Expected parameters | |
| type: "object", | |
| properties: { | |
| paramName: { | |
| type: "string", | |
| description: "What this param is for" | |
| } | |
| }, | |
| required: ["paramName"] | |
| }, | |
| handler: async (params) => { // Your code | |
| // Do something with params | |
| return result; | |
| } | |
| }); | |
| ``` | |
| ### 3. JSON Schema for Parameters | |
| Uses standard JSON Schema: | |
| ```javascript | |
| // No parameters | |
| properties: {} | |
| // One string parameter | |
| properties: { | |
| city: { | |
| type: "string", | |
| description: "City name" | |
| } | |
| } | |
| // Multiple parameters | |
| properties: { | |
| a: { type: "number" }, | |
| b: { type: "number" } | |
| }, | |
| required: ["a", "b"] | |
| ``` | |
| ### 4. Agent Decision Making | |
| ``` | |
| User: "What time is it?" | |
| β | |
| LLM thinks: | |
| "I need current time" | |
| "I see function: getCurrentTime" | |
| "Description matches what I need" | |
| β | |
| LLM outputs special format: | |
| {function_call: "getCurrentTime"} | |
| β | |
| Library intercepts and runs handler() | |
| β | |
| Handler returns: "1:46:36 PM" | |
| β | |
| LLM receives: Tool result | |
| β | |
| LLM applies system prompt: | |
| Convert to 24-hour format | |
| β | |
| Final answer: "13:46" | |
| ``` | |
| ## Use Cases | |
| ### 1. Information Retrieval | |
| ```javascript | |
| const getWeather = defineChatSessionFunction({ | |
| description: "Get weather for a city", | |
| params: { | |
| type: "object", | |
| properties: { | |
| city: { type: "string" } | |
| } | |
| }, | |
| handler: async ({city}) => { | |
| return await fetchWeather(city); | |
| } | |
| }); | |
| ``` | |
| ### 2. Calculations | |
| ```javascript | |
| const calculate = defineChatSessionFunction({ | |
| description: "Perform arithmetic calculation", | |
| params: { | |
| type: "object", | |
| properties: { | |
| expression: { type: "string" } | |
| } | |
| }, | |
| handler: async ({expression}) => { | |
| return eval(expression); // (Be careful with eval!) | |
| } | |
| }); | |
| ``` | |
| ### 3. Data Access | |
| ```javascript | |
| const queryDatabase = defineChatSessionFunction({ | |
| description: "Query user database", | |
| params: { | |
| type: "object", | |
| properties: { | |
| userId: { type: "string" } | |
| } | |
| }, | |
| handler: async ({userId}) => { | |
| return await db.users.findById(userId); | |
| } | |
| }); | |
| ``` | |
| ### 4. External APIs | |
| ```javascript | |
| const searchWeb = defineChatSessionFunction({ | |
| description: "Search the web", | |
| params: { | |
| type: "object", | |
| properties: { | |
| query: { type: "string" } | |
| } | |
| }, | |
| handler: async ({query}) => { | |
| return await googleSearch(query); | |
| } | |
| }); | |
| ``` | |
| ## Expected Output | |
| When run: | |
| ``` | |
| AI: 13:46 | |
| ``` | |
| The LLM: | |
| 1. Called getCurrentTime() internally | |
| 2. Got "1:46:36 PM" | |
| 3. Converted to 24-hour format | |
| 4. Removed seconds | |
| 5. Returned "13:46" | |
| ## Debugging with PromptDebugger | |
| The debug output shows the full prompt including function schemas: | |
| ``` | |
| System: You are a professional chronologist... | |
| Functions available: | |
| - getCurrentTime: Get the current time | |
| Parameters: (none) | |
| User: What time is it right now? | |
| ``` | |
| This helps debug: | |
| - Did the model see the function? | |
| - Was the description clear? | |
| - Did parameters match expectations? | |
| ## Why This Matters for AI Agents | |
| ### Agents = LLMs + Tools | |
| ``` | |
| LLM alone: LLM + Tools: | |
| ββ Generate text ββ Generate text | |
| ββ That's it ββ Access real data | |
| ββ Perform calculations | |
| ββ Call APIs | |
| ββ Execute actions | |
| ββ Interact with world | |
| ``` | |
| ### Foundation for Complex Agents | |
| This simple example is the foundation for: | |
| - **Research agents**: Search web, read documents | |
| - **Coding agents**: Run code, check errors | |
| - **Personal assistants**: Calendar, email, reminders | |
| - **Analysis agents**: Query databases, compute statistics | |
| All start with basic function calling! | |
| ## Best Practices | |
| 1. **Clear descriptions**: LLM uses these to decide when to call | |
| 2. **Type safety**: Use JSON Schema properly | |
| 3. **Error handling**: Handler should catch errors | |
| 4. **Return strings**: LLM processes text best | |
| 5. **Keep functions focused**: One clear purpose per function | |
| This is the minimum viable agent: one LLM + one tool + proper configuration. | |