03.1 项目需求
去年帮朋友搭完客服机器人后,我想做个能查热搜的小助手。光会讲笑话不够,得让它真的去调接口。
需求很具体:用户说"微博热搜 Top3",Agent 调接口、拿数据、整理成列表。
这就是 Agent 和"聊天机器人"的分水岭——它会动手调外部服务。
03.2 思路:先想清楚三件事
动手前,先定三件事:
- 用什么模型?(我用 deepseek,便宜又快)
- 给它什么工具?(一个查热搜的 HTTP 工具)
- 在哪运行?(
local()沙箱,能直接发请求)
类比一下:Agent 是员工,工具是他的办公软件。你不写他每一步点哪,只把软件装好。
03.3 写工具和 Agent
// src/agents/social-search.ts
import { createAgent, defineTool, Type, local } from '@flue/runtime';
import type { AgentRouteHandler, AgentWebSocketHandler } from '@flue/runtime';
// 定义一个工具:调外部 API
const apiCall = defineTool({
name: 'api_call',
description: 'Call an external API',
parameters: Type.Object({
method: Type.Union([Type.Literal('GET'), Type.Literal('POST')]),
path: Type.String({ description: 'API path' }),
}),
execute: async ({ method, path }) => {
// 这行是关键:直接返回字符串给模型
return JSON.stringify({ method, path });
},
});
const agent = createAgent(() => ({
model: 'deepseek/deepseek-v4-flash',
instructions: 'You are a social media search assistant.',
sandbox: local(), // 让它能碰主机网络
tools: [apiCall],
}));
// 这两个导出是 HTTP/WS 访问必须的
export const route: AgentRouteHandler = agent.route;
export const websocket: AgentWebSocketHandler = agent.websocket;
export default agent;
预期结果:保存后,flue dev 启动,访问 POST /agents/social-search/我的会话 就能对话。
注意:工具参数用
Type(TypeBox),不是valibot。这是 Flue 一个反直觉点。工具用 Type,工作流的
result才用 valibot。混用会报错。
03.4 让它真正联网
上面 apiCall 我简化了。真实场景里,execute 直接用 Node 的 https 发请求:
import https from 'node:https';
execute: async ({ path }) => {
// 直接调 Node API,不用沙箱里的 shell
return new Promise((resolve) => {
const req = https.request(
{ hostname: 'api.example.com', path },
(res) => {
let data = '';
res.on('data', (c) => (data += c));
res.on('end', () => resolve(data));
},
);
req.end();
});
}
标叔的经验:工具的
execute就是普通 Node 函数。我一开始以为要在沙箱里跑,绕了弯路。其实它跑在 Node 运行时,啥 Node API 都能用:
fs、https、child_process。这点自由度很大。
03.5 回顾
我们给 Agent 装了"手"(工具)和"工作间"(沙箱)。它能调外部 API 了。
核心建议:第一个项目,工具越简单越好。
别一上来搞 MCP、多工具联动。先跑通"一个工具 + 一次调用",建立手感最重要。
会动手了。下一 Part,我拆开 Harness 的五个零件,让你真正看懂 Flue。