broad-painter-9222
12/15/2025, 3:57 PMtall-airline-16756
12/15/2025, 3:57 PMtall-airline-16756
12/15/2025, 3:58 PMfunction_tool arguments — I'll explain how to define and receive them and show short Python and Node.js examples.
Key points
• Arguments are defined by the function signature (Python) or a parameter schema (Node.js) and are passed from the LLM when it calls the tool.
• Include RunContext (Python) or { ctx } (Node.js) in the parameters if your tool needs access to session/agent state.
• Describe each argument in the docstring (Python) or via Zod .describe() (Node.js) so the LLM knows what to pass.
• For advanced usage, you can supply a raw JSON schema and receive a raw_arguments dict.
Python example
@function_tool()
async def lookup_weather(context: RunContext, location: str) -> dict[str, Any]:
"""Look up weather information.
Args:
location: The location to look up weather information for.
"""
return {"weather": "sunny", "temperature_f": 70}
• The LLM will call lookup_weather and pass location as a string. Use context to access session or agent state.
Raw-schema Python example
@function_tool(raw_schema=raw_schema)
async def get_weather(raw_arguments: dict[str, object], context: RunContext):
location = raw_arguments["location"]
return f"The weather in {location} is ..."
Node.js (Zod) example
const lookupWeather = llm.tool({
description: "Look up weather information for a given location.",
parameters: z.object({
location: z.string().describe("The location to look up weather information for."),
}),
execute: async ({ location }, { ctx }) => {
return { weather: "sunny", temperatureF: 70 };
},
});
Error handling
• Validate arguments inside the tool and raise a ToolError (Python) or throw an error (Node) to return meaningful feedback if input is invalid.
Best practices
• Keep parameter names clear and use types/type hints.
• Mark required fields in schemas.
• Put helpful descriptions in docstrings or Zod .describe() to improve the LLM's argument generation.
• Use `RunContext`/`ctx` when you need agent/session data.
If you want, tell me which language (Python or Node.js) and share the function signature you plan to expose — I can give a tailored example or a JSON schema for your tool.
Sources: Agents v0.x migration guide - Python | LiveKit docs | Agents v0.x migration guide - Node.js | LiveKit Docs | Tool definition and use | LiveKit docsbroad-painter-9222
12/15/2025, 4:08 PMtall-airline-16756
12/15/2025, 4:09 PMfrom livekit.agents import Agent, function_tool, RunContext
from livekit.agents.llm import ChatContext, ChatMessage
class MyAgent(Agent):
@function_tool()
async def transcript_contains_topic(self, context: RunContext, topic: str) -> bool:
"""Return True if `topic` appears in the current transcript."""
chat_ctx: ChatContext = context.session.chat_ctx
# Combine text from all ChatMessage entries (change filter to restrict to user messages)
transcript = " ".join(
(msg.text_content() or "") for msg in chat_ctx.messages if isinstance(msg, ChatMessage)
).lower()
return topic.lower() in transcript
Node.js (Zod schema for the tool)
import { z } from "zod";
const transcriptContainsTopic = llm.tool({
description: "Return true if topic appears in the current transcript.",
parameters: z.object({
topic: z.string().describe("Topic to search for in the transcript"),
}),
execute: async ({ topic }, { ctx }) => {
// ctx.session.chat_ctx.messages expected; adjust based on your runtime
const messages = ctx.session.chat_ctx.messages || [];
const transcript = messages.map(m => m.text || "").join(" ").toLowerCase();
return transcript.includes(topic.toLowerCase());
},
});
Notes and quick options
• Access: Use context.session.chat_ctx (Python) or ctx.session.chat_ctx (Node) to read messages.
• Filtering: Restrict to user messages by checking message role/type before joining.
• Robustness: Replace simple substring check with regex, embeddings, or an LLM classifier for fuzzier matches.
• Return: Return a boolean; the agent/LLM will receive it as true/false.
Do you want the Node.js example adapted to only user messages, or a version that uses an LLM check instead of keyword matching?
Sources: Agents and handoffs | LiveKit docs | Tool definition and use | LiveKit docs | Agent speech and audio | LiveKit docsbroad-painter-9222
12/15/2025, 4:10 PMtall-airline-16756
12/15/2025, 4:10 PMfrom livekit.agents import Agent, function_tool, RunContext
class EvalAgent(Agent):
@function_tool()
async def evaluate_candidate(self, context: RunContext, rubric: dict | None = None) -> dict:
"""Run an LLM-based evaluation over the current transcript and return structured results."""
chat_ctx = context.session.chat_ctx
transcript = " ".join((m.text_content() or "") for m in chat_ctx.messages).strip()
# Build prompt (include rubric or default criteria)
criteria = rubric or {
"communication": "Clarity and conciseness",
"technical": "Problem solving and technical correctness",
"confidence": "Confidence and composure"
}
prompt = (
"You are an objective interviewer evaluator. Given the transcript below, score each criterion 1-5 "
f"and provide a short justification. Return a JSON object with keys: scores (map), comments (map), overall (string).\n\n"
f"Criteria: {criteria}\n\nTranscript:\n{transcript}\n\nRespond only with valid JSON."
)
# Call the agent's LLM (pseudo-call; adapt to your LLM API)
llm_response = await context.agent.llm.chat_prompt(prompt) # adapt to your LLM call
content = llm_response.message.content
# Parse JSON safely
import json, re
try:
json_text = re.search(r"\{.*\}", content, re.S).group(0)
result = json.loads(json_text)
except Exception:
# fallback: wrap as minimal structure
result = {"scores": {}, "comments": {}, "overall": content}
return result
3) Node.js example (Zod tool + LLM call)
import { z } from "zod";
const evaluateCandidate = llm.tool({
description: "LLM-based evaluation of candidate performance from transcript.",
parameters: z.object({
rubric: z.record(z.string()).optional(),
}),
execute: async ({ rubric }, { ctx }) => {
const messages = (ctx.session.chat_ctx?.messages ?? []).map(m => m.text || "").join(" ").trim();
const criteria = rubric ?? {
communication: "Clarity and conciseness",
technical: "Problem solving and technical correctness",
confidence: "Confidence and composure",
};
const prompt = `You are an objective interviewer evaluator. Given the transcript below, score each criterion 1-5 and provide a short justification. Return valid JSON with keys: scores, comments, overall.\n\nCriteria: ${JSON.stringify(criteria)}\n\nTranscript:\n${messages}\n\nRespond only with JSON.`;
const resp = await ctx.agent.llm.chat({ input: prompt }); // adapt to your LLM method
const content = resp.message.content;
try {
const jsonText = content.match(/\{[\s\S]*\}/)[0];
return JSON.parse(jsonText);
} catch {
return { scores: {}, comments: {}, overall: content };
}
},
});
4) Practical tips
• Provide a rubric and examples in the prompt to reduce variance.
• Ask for numeric scores plus brief justifications to enable automation.
• Validate LLM output (JSON schema) and fallback gracefully.
• If you need higher reliability, run per-criterion prompts or ensemble multiple LLM prompts.
• Consider privacy/consent for analyzing transcripts.
Would you like a ready-to-use prompt template and a strict JSON schema for validation (I can provide both)?