自定义 Provider
文档模型接入
LIGHTER / DOCUMENTATION

自定义 Provider

接入其他模型服务时,实现 Provider 接口,再传给 new Model(provider)。Agent 不需要了解服务的具体 API。

最小实现

下面的 EchoProvider 直接复述最后一条用户消息,不访问网络。可以保存为 echo.ts,按快速开始中的方式运行,也不需要 API key。

import {
  Agent, AssistantMessage, Model, ModelError, UserMessage,
  type Provider, type ProviderRequest, type ProviderStreamEvent,
} from "@yong_xie/lighter";

export class EchoProvider implements Provider {
  readonly name = "echo";

  async invoke(request: ProviderRequest): Promise<AssistantMessage> {
    // 此示例只支持文本;不支持的能力应明确报错。
    if (request.toolSpecs?.length) {
      throw new ModelError("EchoProvider 不支持工具。", "invalid_request");
    }

    const lastUser = [...request.messages].reverse()
      .find((message) => message.role === "user");
    return new AssistantMessage(`收到:${lastUser?.content ?? ""}`);
  }

  async *stream(request: ProviderRequest): AsyncGenerator<ProviderStreamEvent> {
    const message = await this.invoke(request);
    // 模拟增量输出;真实接入时应逐步转发服务返回的文本。
    yield { type: "text_delta", text: message.content };
    // 最后必须提供完整助手消息,供 Agent 决定是否继续循环。
    yield { type: "message", message };
  }
}

const agent = new Agent({ model: new Model(new EchoProvider()) });
const result = await agent.run({ messages: [new UserMessage("月球书店开业了!")] });
console.log(result.messages);

接入真实服务

部分 需要完成的工作
name 提供稳定的服务标识
invoke() 转换输入消息和工具定义,返回完整 AssistantMessage
stream() 转发文本与可选推理增量,最后返回一次完整 message 事件
工具调用 将服务返回的调用转换为 callId、toolName、JSON 字符串 arguments
工具结果 将 ToolMessage 转换为对应调用的结果,保留调用标识与错误信息
错误 识别失败或不完整响应,抛出合适的 ModelError

Provider 只负责通信和格式转换,工具执行由 Agent 完成。不要把单个对话的可变历史保存在共享 Provider 中。

流式结束规则

text_delta 和 reasoning_delta 只是增量,不能替代最终的 message。流结束前没有完整消息时,Agent 会返回 bad_response 失败结果。

直接调用 Model 的流不会额外校验这个规则,因此自定义 Provider 自身也应检查服务是否正常结束,不要把断流当成成功。

验证接入

至少检查普通回复、工具调用与结果往返、流式增量与结束事件,以及服务失败和中途断流。支持工具时,还应检查一次回复中多个工具调用的标识与参数是否完整。

接口签名见 Model 与 Provider API。