前四个 Demo 都使用确定性假模型。Demo 5 是可选项:当你有 OpenAI-compatible 接口时,用真实模型跑一次“工具调用 -> 本地执行 -> 工具结果回写 -> 最终回答”的链路。
对应文件:
examples/demos/05-openai-compatible.ts
tool_calls。AssistantMessage。OPENAI_COMPATIBLE_BASE_URL="https://example.com/v1" \
OPENAI_COMPATIBLE_API_KEY="你的 key" \
OPENAI_COMPATIBLE_MODEL="模型名" \
OPENAI_COMPATIBLE_AUTH_HEADER="authorization" \
npm run demo:05
如果服务商文档要求 api-key header:
OPENAI_COMPATIBLE_AUTH_HEADER="api-key"
不要把真实 key 写进 package.json、README 或教程正文。
如果不传环境变量,脚本会打印用法并正常跳过,方便你批量运行所有 Demo。
如果你使用 Xiaomi MiMo 的 OpenAI-compatible endpoint,通常可以设置:
OPENAI_COMPATIBLE_MODEL="mimo-v2.5-pro"
OPENAI_COMPATIBLE_AUTH_HEADER="api-key"
Demo 先把本地工具描述发给模型:
const tools = [
{
type: "function",
function: {
name: "list_files",
description: "List files inside the safe teaching workspace.",
parameters: {
type: "object",
properties: {
path: { type: "string" },
},
},
},
},
];
如果模型返回 tool call,就在本地执行:
const entries = await listFiles(resolve(workspaceRoot, path));
messages.push({
role: "tool",
tool_call_id: call.id,
content: entries.join("\n"),
});
然后再次请求模型,让模型基于工具结果回答。
Demo 里还包含一个 adapter 函数,把 OpenAI-compatible assistant message 转成教学版协议:
function toTeachingAssistantMessage(message, finishReason): AssistantMessage {
const toolCalls = (message.tool_calls ?? []).map((call) => ({
type: "toolCall",
id: call.id,
name: call.function.name,
arguments: safeJsonParse(call.function.arguments),
}));
return {
role: "assistant",
content: [
...(message.content ? [{ type: "text", text: message.content }] : []),
...toolCalls,
],
stopReason: finishReason === "tool_calls" || toolCalls.length > 0 ? "toolUse" : "stop",
usage: { input: 0, output: 0, totalTokens: 0 },
timestamp: Date.now(),
};
}
读这段时记住一个原则:provider 字段只在 adapter 里出现,后面的 Agent Loop 继续只认教学版 AssistantMessage。
不同模型输出会有差异,但大致应包含:
[demo:05] requesting model: your-model
[demo:05] local tool list_files output:
README.md
agent-notes.md
[demo:05] final answer:
教学工作区里有 README.md 和 agent-notes.md...
如果模型没有返回 tool call,Demo 会打印它的直接回答。这不是脚本错误,而是模型行为差异;可以调整 prompt、模型名或工具描述再试。
如果模型连续请求工具超过 4 轮,Demo 会停止。这是为了演示真实 Agent 必须有无限 tool call 保护。
Demo 5 不替换教学版 Agent 的默认 MockModel。它只展示一个替换方向:
flowchart LR A["MockModel.complete()"] --> B["AssistantMessage"] C["OpenAI-compatible adapter"] --> B B --> D["runAgentLoop"]
只要 adapter 返回同样的 AssistantMessage,后面的 loop、工具和会话系统就可以继续复用。
更完整的 adapter 设计见 可选:接入 OpenAI-compatible 模型。
把 list_files 改成 read_file,让模型读取 agent-notes.md 并总结。你需要同时修改工具 schema、本地工具执行函数和用户 prompt。