- Updated the Agent API documentation to reflect a new structure, emphasizing quick start instructions and reorganizing content for better readability. - Renamed the main documentation title to "Yao Agent" and streamlined sections, including API endpoints and file management. - Adjusted the SUI documentation to align with the Yao App Engine license, ensuring consistency across project documentation.
6.9 KiB
6.9 KiB
Hooks
Hooks allow you to customize agent behavior at key points in the execution lifecycle.
Lifecycle
User Input → Create Hook → LLM Call → Tool Execution → Next Hook → Response
Create Hook
Called before LLM call. Use to preprocess messages, configure request, or delegate.
function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
// Return null for default behavior
return null;
// Or return configuration
return {
messages, // Modified messages
temperature: 0.7, // Override temperature
max_tokens: 2000, // Override max tokens
connector: "gpt-4o-mini", // Override connector
prompt_preset: "task", // Select prompt preset
disable_global_prompts: true,// Skip global prompts
mcp_servers: [ // Add MCP servers
{ server_id: "tools", tools: ["search"] }
],
uses: { // Override wrapper tools
vision: "vision-agent",
search: "disabled"
},
force_uses: true, // Force use wrapper tools
locale: "zh-cn", // Override locale
metadata: { key: "value" }, // Pass data to context
};
}
Delegation (Skip LLM)
Route to another agent immediately:
function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
if (shouldDelegate(messages)) {
return {
delegate: {
agent_id: "specialist.agent",
messages: messages,
options: { metadata: { source: "main" } }
}
};
}
return { messages };
}
Next Hook
Called after LLM response and tool execution. Use to post-process or delegate.
function Next(ctx: agent.Context, payload: agent.Payload): agent.Next {
const { messages, completion, tools, error } = payload;
// Handle errors
if (error) {
return { data: { status: "error", message: error } };
}
// Process tool results
if (tools?.length > 0) {
const results = tools.map(t => t.result);
return { data: { status: "success", results } };
}
// Delegate based on response
if (completion?.content?.includes("transfer")) {
return {
delegate: {
agent_id: "transfer.agent",
messages: payload.messages
}
};
}
// Return null for standard response
return null;
}
Payload Structure
interface Payload {
messages: Message[]; // Messages sent to LLM
completion?: {
content: string; // LLM text response
tool_calls?: ToolCall[]; // Tool calls from LLM
usage?: UsageInfo; // Token usage
};
tools?: ToolCallResponse[]; // Tool execution results
error?: string; // Error message
}
interface ToolCallResponse {
toolcall_id: string;
server: string; // MCP server ID
tool: string; // Tool name
arguments?: any; // Tool arguments
result?: any; // Tool result
error?: string; // Tool error
}
Return Values
interface NextResponse {
delegate?: { // Route to another agent
agent_id: string;
messages: Message[];
options?: Record<string, any>;
};
data?: any; // Custom response data
metadata?: Record<string, any>;// Debug metadata
}
Sending Messages
Use ctx to send messages to the client:
function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
// Send complete message
ctx.Send({ type: "text", props: { content: "Processing..." } });
// Streaming message
const msgId = ctx.SendStream("Starting...");
ctx.Append(msgId, " step 1...");
ctx.Append(msgId, " step 2...");
ctx.End(msgId);
return { messages };
}
Memory
Share data between hooks:
function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
// Store in request-scoped memory
ctx.memory.context.Set("start_time", Date.now());
ctx.memory.context.Set("query", messages[0]?.content);
return { messages };
}
function Next(ctx: agent.Context, payload: agent.Payload): agent.Next {
// Retrieve data
const startTime = ctx.memory.context.Get("start_time");
const duration = Date.now() - startTime;
return { data: { duration_ms: duration } };
}
Tracing
Add trace nodes for debugging and UI:
function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
const node = ctx.trace.Add(
{ query: messages[0]?.content },
{ label: "Preprocessing", type: "process", icon: "play" }
);
node.Info("Starting analysis");
// ... processing ...
node.Complete({ status: "done" });
return { messages };
}
Error Handling
function Next(ctx: agent.Context, payload: agent.Payload): agent.Next {
try {
if (payload.error) {
ctx.trace.Error(payload.error);
return {
data: { status: "error", message: "Something went wrong" }
};
}
// ... normal processing
} catch (e) {
ctx.trace.Error(e.message);
return { data: { status: "error", message: e.message } };
}
}
Multi-Agent Orchestration
// Main agent delegates based on intent
function Next(ctx: agent.Context, payload: agent.Payload): agent.Next {
const { tools } = payload;
// Route based on tool result
const intent = tools?.[0]?.result?.intent;
const agentMap = {
"search": "search.agent",
"calculate": "calc.agent",
"translate": "translate.agent"
};
if (intent && agentMap[intent]) {
return {
delegate: {
agent_id: agentMap[intent],
messages: payload.messages
}
};
}
return null;
}
Complete Example
import { agent } from "@yao/runtime";
function Create(ctx: agent.Context, messages: agent.Message[]): agent.Create {
const query = messages[messages.length - 1]?.content || "";
// Store for Next hook
ctx.memory.context.Set("query", query);
ctx.memory.context.Set("start", Date.now());
// Add trace
ctx.trace.Add({ query }, { label: "Create", type: "hook" });
// Check if needs special handling
if (query.toLowerCase().includes("urgent")) {
return {
messages,
temperature: 0,
prompt_preset: "task"
};
}
return { messages };
}
function Next(ctx: agent.Context, payload: agent.Payload): agent.Next {
const { completion, tools, error } = payload;
const start = ctx.memory.context.Get("start");
const duration = Date.now() - start;
ctx.trace.Add(
{ duration },
{ label: "Next", type: "hook" }
).Complete();
if (error) {
return { data: { status: "error", error } };
}
if (tools?.length > 0) {
return {
data: {
status: "success",
response: completion?.content,
tools: tools.map(t => ({ name: t.tool, result: t.result })),
duration_ms: duration
}
};
}
return null;
}