Hook 是在执行流水线特定节点运行的 TypeScript 函数。本页通过最极端的形式引入 Hook:完全用代码处理请求,不涉及任何 LLM。
## Hook 的工作方式
在 Assistant 目录下创建 `src/index.ts`,导出 `Create` 和/或 `Next` 函数:
```typescript
// assistants/my-assistant/src/index.ts
import { agent } from "@yao/runtime";
export function Create(
ctx: agent.Context,
messages: agent.Message[]
): agent.Create {
// 在 LLM 之前运行
return { messages };
}
export function Next(
ctx: agent.Context,
payload: agent.Payload
): agent.Next | null {
// 在 LLM 之后运行
return null;
}
```
两个函数都是可选的,返回 `null` 表示使用默认行为。
## 跳过 LLM
只有当 `prompts.yml` 存在或配置了 MCP Server 时,LLM 才会被调用。两者都没有时,`Create` Hook 全权负责响应。用 `ctx.Send()` 直接输出内容:
```typescript
export function Create(
ctx: agent.Context,
messages: agent.Message[]
): agent.Create {
ctx.Send("I received your message.");
return { messages }; // LLM 被跳过是因为没有 prompts.yml 且没有 MCP —— 不是因为这个 return
}
```
Pure Hook Agent 的 `package.yao` —— 无提示词,无 MCP:
```json
{
"name": "My Pure Hook Agent",
"connector": "$ENV.DEFAULT_CONNECTOR"
}
```
即使配置了 `connector`,只要 `prompts.yml` 不存在且没有 MCP,LLM 就不会被调用。
## 示例:菜单路由
来自 `yaobots/assistants/tests/messages/src/index.ts` —— 不借助 LLM,根据用户输入路由到不同处理器:
```typescript
export function Create(
ctx: agent.Context,
messages: agent.Message[]
): agent.Create {
const input = messages[messages.length - 1]?.content?.toLowerCase() || "";
if (input.includes("help")) {
const msgId = ctx.SendStream({ type: "text", props: { content: "" } });
ctx.Append(msgId, "# 可用命令\n\n");
ctx.Append(msgId, "- **hello** — 打招呼\n");
ctx.Append(msgId, "- **time** — 当前时间\n");
ctx.Append(msgId, "- **help** — 本菜单\n");
ctx.End(msgId);
return { messages };
}
if (input.includes("time")) {
ctx.Send(`当前时间:${new Date().toISOString()}`);
return { messages };
}
if (input.includes("hello")) {
ctx.Send("你好!有什么可以帮你的?");
return { messages };
}
// 其他情况 —— 如果配置了 prompts.yml 或 MCP,则由 LLM 处理
return { messages };
}
```
## 示例:混合模式
特定情况用代码处理,其余交给 LLM:
```typescript
export function Create(
ctx: agent.Context,
messages: agent.Message[]
): agent.Create {
const input = messages[messages.length - 1]?.content || "";
// 状态查询不经过 LLM
if (input.toLowerCase() === "status") {
const status = getSystemStatus(); // 你的函数
ctx.Send(`系统状态:${status}`);
return { messages };
}
// 其他情况交给 LLM
return { messages };
}
```
## 流式输出
对于较长的响应,分块流式输出而不是一次性发送:
```typescript
const msgId = ctx.SendStream({ type: "text", props: { content: "" } });
for (const chunk of generateChunks()) {
ctx.Append(msgId, chunk);
}
ctx.End(msgId);
```
## 下一步
你已经看到 Hook 如何拦截执行流。接下来学习在 LLM 运行时配合使用 `Create` Hook。
→ **[Create Hook](./hook-create)**