本文指导你如何安装并使用扣子罗盘 SDK 进行数据上报。
准备工作
环境准备
扣子罗盘 Node SDK @cozeloop/ai 适用于 Node.js 18 及以上版本。安装之前,可执行以下命令确认你的 Node.js 版本。
推荐使用 fnm 管理 Node.js 版本。
# 查看 Node 版本
node -v
SDK 授权
在使用扣子罗盘 Node SDK 前,确保你已经完成了授权。详情请参考SDK 鉴权。
安装扣子罗盘 SDK
执行以下命令安装扣子罗盘 SDK。
npm install @cozeloop/ai
# or
pnpm install @cozeloop/ai
默认直接安装最新版本的 SDK。如果你想使用历史版本,单击此处查看历史版本记录和 README。
初始化扣子罗盘 ApiClient
初始化扣子罗盘 ApiClient 之后,才可以访问 SDK 提供的 API。
初始化时推荐通过环境变量动态获取访问密钥,以免硬编码引发数据安全风险。 设置完成环境变量后,你可以直接访问 SDK API 实现相应功能。
以下示例代码展示了如何使用扣子罗盘 ApiClient,并请求某个 API。
const apiClient = new ApiClient({
/**
* Api baseURL, default value is:
* * process.env.COZELOOP_API_BASE_URL
* * https://api.coze.cn
*/
baseURL: 'https://api.coze.cn',
/**
* Personal Access Token (PAT) or OAuth2.0 token, or a function to get token.
* use process.env.COZELOOP_API_TOKEN when unprovided
*/
token: '',
/** Custom axios instance */
axiosInstance: axios,
/**
* Partial [axios request-config](https://github.com/axios/axios?tab=readme-ov-file#request-config), excludes url, method, baeURL, data and responseType.
*/
axiosOptions: {},
/** Custom headers */
headers: {},
});
const resp = await apiClient.post('/v1/api_url', {/** data */});
扣子罗盘 SDK 的主要能力组件如 Tracer、PromptHub 等都依赖 ApiClient实例或 ApiClient 配置参数。
示例代码
Trace 上报
Tracer 初始化
参考以下代码初始化全局 Tracer 实例。(请尽早,比如在应用启动时。)
// initialize tracer globally
cozeLoopTracer.initialize({
/** workspace id, use process.env.COZELOOP_WORKSPACE_ID when unprovided */
workspaceId: 'your_workspace_id',
apiClient: {
baseURL: 'https://api.coze.cn',
token: 'your_api_token',
},
/** Allow ultra long text report */
ultraLargeReport: true,
});
Root Span上报
建议将一次完整的用户请求串联成一个 Trace。在服务入口处,上报一个 Root Span,并在此注入相关的业务 ID 便于 Trace 定位和统计分析。
import { cozeLoopTracer } from '@cozeloop/ai';
export async function runRoot() {
// We recommend concatenating a complete user request into a trace,
// so the recommended approach is to report a root span at the entrance of the entire execution
await cozeLoopTracer.traceable(
async () => {
// execute your method
const result = await doSomething();
return result;
},
{
name: 'TestRootSpan',
type: 'RootSpanType',
// you can set your own baggage fields (eg. userId),
// these fields will be automatically passed through and set in all sub-spans
baggages: {
user_id: 'uid-123',
message_id: 'msg-123',
thread_id: 'thread-123',
custom_id: 'custom-123',
},
},
);
}
自定义 Span上报
扣子罗盘支持在任何关键节点进行自定义 span 上报,并自由设置关键信息和自定义 tag。每个traceable所包装的span实例将透传给内部业务方法,使用者可以在方法中消费。
import { cozeLoopTracer } from '@cozeloop/ai';
export async function runCustom() {
// Wrap any function to make it traceable
await cozeLoopTracer.traceable(
async parentSpan => {
// Manually set input
cozeLoopTracer.setInput(parentSpan, 'xxx');
// Invoke any function, if it throws error, error will be caught and automatically set span as error
const result = await doSomething();
// Or, you can manually set error
cozeLoopTracer.setError(parentSpan, 'custom error message');
// You can also trace nested span, the parent-child relationship of span will be automatically concatenated
await cozeLoopTracer.traceable(
async childSpan => {
// Set custom tags
childSpan.setAttribute('custom-tag', 'xxx');
await doSomething();
},
{
name: 'TestCustomChildSpan',
type: 'MyCustomType',
},
);
// Automatically set return value as output
return result;
},
{
name: 'TestCustomParentSpan',
type: 'MyCustomType',
},
);
}
Model Span上报
模型调用往往是最关键的节点,可将 span type 设置为 SpanKind.Model,并上报 Model的相关信息,在上报 input 和 output 时建议使用和遵循 SDK 提供的 LoopTraceLLMCallInput 和 LoopTraceLLMCallOutput 类型,以便在平台获得更好的观测体验。
import { SpanKind } from '@cozeloop/ai';
import {
COZELOOP_TRACE_BUSINESS_TAGS,
cozeLoopTracer,
type LoopTraceLLMCallInput,
} from '@cozeloop/ai';
export async function runModel() {
// Wrap model invoke function to make it traceable
await cozeLoopTracer.traceable(
async span => {
const input: LoopTraceLLMCallInput = {
messages: [
{
role: 'user',
content: 'hi',
},
],
tools: [
{
type: 'function',
function: {
name: 'test-tool',
description: 'tool-description',
parameters: {
type: 'object',
properties: {
name: { type: 'string' },
},
},
},
},
],
tool_choice: {
type: 'auto',
function: {
name: 'test-tool',
},
},
};
// Manually set input, if the input satisfies the LoopTraceLLMCallInput structure,
// the results will be better displayed in the CozeLoop platform
cozeLoopTracer.setInput(span, input);
// Invoke/stream model
const result = await fakeLLMCall();
// Set model related tags
cozeLoopTracer.setTags(span, {
[COZELOOP_TRACE_BUSINESS_TAGS.MODEL_NAME]: 'custom-model',
[COZELOOP_TRACE_BUSINESS_TAGS.MODEL_PROVIDER]: 'cozeloop',
[COZELOOP_TRACE_BUSINESS_TAGS.CALL_OPTIONS]: {
temperature: 0.5,
max_tokens: 1000,
},
[COZELOOP_TRACE_BUSINESS_TAGS.INPUT_TOKENS]: 100,
[COZELOOP_TRACE_BUSINESS_TAGS.OUTPUT_TOKENS]: 200,
[COZELOOP_TRACE_BUSINESS_TAGS.TOKENS]: 300,
// If you use streaming return, record the microsecond timestamp returned by the first Token,
// and the SDK will automatically calculate the time spent on the first Token
[COZELOOP_TRACE_BUSINESS_TAGS.START_TIME_FIRST_RESP]: 1741305600123456,
});
// Manually set output, if the output satisfies the LoopTraceLLMCallOutput structure,
// the results will be better displayed in the CozeLoop platform
cozeLoopTracer.setOutput(span, result);
return result.choices;
},
{
name: 'TestModelSpan',
type: SpanKind.Model,
},
);
}
超大文本上报
在 Tracer 初始化时,如果将 ultraLargeReport 设置为 true,则启用超大文本上报。当 Input / Output 内容超过 1 MB 时,将通过文件形式进行上报,并可在平台上查看完整内容;如果未启用该选项,超出大小限制的内容将会被丢弃。
import {
cozeLoopTracer,
SpanKind,
type LoopTraceLLMCallInput,
} from '@cozeloop/ai';
function generateLargeString(sizeInMB: number) {
const repeats = sizeInMB * 1024 * 1024;
return 'x'.repeat(repeats);
}
// You can enable the reporting of ultra-long texts by setting
// ultraLargeReport to true when you initialize tracer
export async function runLargeText() {
await cozeLoopTracer.traceable(
async span => {
// Reporting of ultra-long texts will only take effect when the
// input / output satisfies the LoopTraceLLMCallInput / LoopTraceLLMCallOutput structure
const input: LoopTraceLLMCallInput = {
messages: [
{
role: 'user',
content: generateLargeString(2),
},
],
};
// Manually set input
cozeLoopTracer.setInput(span, input);
// execute your method
const result = await doSomething();
return result;
},
{
name: 'TestLargeTextSpan',
type: SpanKind.Model,
},
);
}
多模态上报
Model Span 的 Input / Output 可能包含图片或文件。当 Input / Output 遵循 SDK 提供的 LoopTraceLLMCallInput 和 LoopTraceLLMCallOutput 类型时,多模态资源将被自动处理和上报。图片和文件应使用标准的 MDN 格式或公网可访问的 URL
import {
cozeLoopTracer,
SpanKind,
type LoopTraceLLMCallInput,
} from '@cozeloop/ai';
export async function runMultiModality() {
await cozeLoopTracer.traceable(
async span => {
// Reporting of multi modality will only take effect when the
// input / output satisfies the LoopTraceLLMCallInput / LoopTraceLLMCallOutput structure
const input: LoopTraceLLMCallInput = {
messages: [
{
role: 'user',
content: '',
parts: [
// current support base64 encoded image and file
{
type: 'image_url',
image_url: {
url: 'data:image/png;base64,XXX',
},
},
{
type: 'file_url',
file_url: {
name: 'test.txt',
url: 'data:text/plain;base64,XXX',
},
},
],
},
],
};
// Manually set input
cozeLoopTracer.setInput(span, input);
// execute your method
const result = await doSomething();
return result;
},
{
name: 'TestMultiModalitySpan',
type: SpanKind.Model,
},
);
}
跨服务上报
- 当需要得到当前上下文,并提供给下游服务时:
- 【CozeLoop SDK 协议上下文】你可以通过injectWithCozeLoopHeaders来将上下文信息注入headers:
import { context, injectWithCozeLoopHeaders } from '@cozeloop/ai';
const headers: Record<string, string> = {};
injectWithCozeLoopHeaders(context.active(), headers);
- 【标准W3C协议上下文】你可以通过propagation.inject来将上下文信息注入headers:
import { propagation } from '@opentelemetry/api';
import { context } from '@cozeloop/ai';
const headers: Record<string, string> = {};
propagation.inject(context.active(), headers);
- 当需要接收来自上游服务的上下文时:
- 【CozeLoop SDK 协议上下文】你可以使用extractWithCozeLoopHeaders并传入带有 CozeLoop SDK 协议的 headers 来得到 Context:
import { context, extractWithCozeLoopHeaders, cozeLoopTracer } from '@cozeloop/ai';
const extractedContext = extractWithCozeLoopHeaders(
context.active(),
headers,
);
context.with(extractedContext, () =>
cozeLoopTracer.traceable(
() => {
doSomething();
},
{
name: 'XXX',
type: 'XXX',
},
),
);
- 【标准W3C协议上下文】你可以使用propagation.extract并传入带有标准W3C协议的 headers 来得到 Context:
import { propagation } from '@opentelemetry/api';
import { context, extractWithCozeLoopHeaders, cozeLoopTracer } from '@cozeloop/ai';
const extractedContext = propagation.extract(context.active(), headers);
context.with(extractedContext, () =>
cozeLoopTracer.traceable(
() => {
doSomething();
},
{
name: 'XXX',
type: 'XXX',
},
),
);
完整示例如下:
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
import axios, { type AxiosRequestConfig } from 'axios';
import { ROOT_CONTEXT } from '@opentelemetry/api';
import {
cozeLoopTracer,
injectWithCozeLoopHeaders,
context,
extractWithCozeLoopHeaders,
} from '@cozeloop/ai';
import { doSomething } from './utils';
function setupMockServer() {
const mockServer = setupServer(
http.post(/\/mock\/service\/api\/endpoint/, async req => {
// Simulate collecting headers in the request
const headers: Record<string, string> = {};
req.request.headers.forEach((value, key) => {
headers[key] = value;
});
// Simulate a new context environment (for example, in a cross-service scenario) by
// setting context to ROOT_CONTEXT, you don't need to take this step in real cross-service calls
return await context.with(
ROOT_CONTEXT,
// Simulate service execution
async () => await mockService({ headers }),
);
}),
);
return {
start: () => mockServer.listen({ onUnhandledRequest: 'bypass' }),
close: () => mockServer.close(),
};
}
async function mockService(req: { headers: Record<string, string> }) {
// Read the information of the current context from the headers
const extractedContext = extractWithCozeLoopHeaders(
context.active(),
req.headers,
);
// Use the extracted context
return await context.with(
extractedContext,
async () =>
await cozeLoopTracer.traceable(
async () => {
const result = await doSomething();
return HttpResponse.json({
code: 0,
msg: '',
data: {
result,
},
});
},
{
name: 'ChildSpan',
type: 'TransferBetweenServicesType',
},
),
);
}
export async function runTransferBetweenServices() {
const mockServer = setupMockServer();
mockServer.start();
const result = await cozeLoopTracer.traceable(
async span => {
span.setAttribute('custom-tag', 'xxx');
const headers: AxiosRequestConfig['headers'] = {};
// Generate the information in the current context as headers
injectWithCozeLoopHeaders(context.active(), headers);
const resp = await axios.post(
'http://mock/service/api/endpoint',
{},
{
headers,
},
);
return resp.data;
},
{
name: 'ParentSpan',
type: 'TransferBetweenServicesType',
// The baggage fields will be automatically passed through
baggages: {
user_id: 'uid-123',
message_id: 'msg-123',
thread_id: 'thread-123',
},
},
);
mockServer.close();
return result;
}
Prompt 拉取与格式化
在使用 SDK 拉取 Prompt 前,需要在扣子罗盘的 Prompt 开发页面获取 Prompt Key 和版本号。
扣子罗盘 SDK 提供接口来拉取在扣子罗盘中创建并提交的 Prompt。通过 hub.get() 方法可以获取指定 Key 的 Prompt(支持指定版本或使用最新版本),然后使用 hub.formatPrompt() 方法完成变量替换。设置 traceable 为 true 后,SDK 会自动记录 Prompt 的获取和渲染过程,便于后续分析和优化(需完成 Tracer 初始化)。
以下是完整的示例代码。
import { type Message, type PromptVariables, PromptHub } from '@cozeloop/ai';
// 1. 设置 ApiClient
const apiClient = new ApiClient({
baseURL: 'https://api.coze.cn',
token: 'your_access_token',
});
const hub = new PromptHub({
workspaceId: 'your_workspace_id',
apiClient,
// 是否开启自动trace上报
traceable: true,
});
// get prompt with version 0.0.1
const prompt1 = await hub.get('your_prompt_key', '0.0.1')
// get prompt with latest version
const prompt2 = await hub.get('your_prompt_key')
// format prompt to messages with variables
const messages = hub.formatPrompt(prompt1, { key: 'value' });
《CDA数据分析师技能树系列图书》系统整合数据分析核心知识,从基础工具(如Python、SQL、Excel、Tableau、SPSS等)到机器学习、深度学习算法,再到行业实战(金融、零售等场景)形成完整体系。书中结合案例讲解数据清洗、建模、可视化等技能,兼顾理论深度与实操性,帮助读者构建系统化知识框架。同时,内容紧跟行业趋势,涵盖大数据分析、商业智能、ChatGPT与DeepSeek等前沿领域,还配套练习与项目实战,助力读者将知识转化为职场竞争力,是数据分析师从入门到进阶的实用参考资料。