在 Corsair 中集成 TextRazor:NLP 文本分析插件完整使用指南
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
@corsair-dev/textrazor是 Corsair 官方生态中的一个插件包,它将 TextRazor 的 REST API(实体抽取、文本分类、自定义词典与分类器等能力)封装为类型安全、可统一鉴权的 Corsair 端点。本文以 packages/textrazor/README.md 为核心骨架,结合 插件源码、端点实现 与 测试与配置,带你完成从安装、鉴权到 17 个 API 端点的完整接入,掌握如何在多租户环境中安全、可靠地调用 TextRazor 文本分析能力。
一、插件是什么
TextRazor 提供基于 NLP 的文本理解服务,包括命名实体识别(NER)、主题提取、文本分类、依存句法分析、拼写纠错等。在 Corsair 中,它被封装为id: 'textrazor'的插件,具备以下特性:
- 统一鉴权:使用 API Key(
api_key)认证,Corsair 会在租户首次使用时提示录入凭证(见 index.ts 的textrazorAuthConfig); - 类型安全:每个端点都有 Zod 输入/输出 Schema(见 endpoints/types.ts),配合 TypeScript 可获得完整的参数与返回值推导;
- 结果落库缓存:分析出的实体、账户信息、词典与分类器类别会自动写入 Corsair 数据库(见 schema/database.ts);
- 无 Webhook:TextRazor 为同步 API,本插件
webhooks: {}且pluginWebhookMatcher始终返回false。
二、安装与基础使用
在 Corsair 项目中安装:
pnpm add @corsair-dev/textrazor插件的 peer 依赖为corsair >= 0.1.0与zod ^4.1.13(见 package.json)。注册并调用:
import { textrazor } from '@corsair-dev/textrazor'; const plugin = textrazor({ // 可选:直接注入 API Key;不注入时 Corsair 会在租户首次使用时提示录入 key: process.env.TEXTRAZOR_API_KEY, }); // 在 handler 中调用分析端点 const result = await plugin.endpoints.analysis.analyzeContent(ctx, { text: 'Apple announced a new iPhone on September 9.', extractors: ['entities', 'topics'], });插件默认authType为'api_key'(见 index.ts)。密钥解析逻辑位于keyBuilder:当source === 'endpoint'时优先使用构造选项中的key,否则从ctx.keys.get_api_key()读取租户密钥;两者都缺失时抛出AuthMissingError。
三、端点全览
README 中给出了插件暴露的全部 17 个端点。每个端点带 Operation ID、风险等级与说明,风险等级分为read(读)、write(写)与destructive(删除),是 Corsair 权限系统的重要依据:
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
account.get | textrazor.api.account.get | read | Get the current TextRazor plan, concurrency limits, and daily usage |
analysis.analyzeContent | textrazor.api.analysis.analyzeContent | read | Analyze text or a URL with one or more TextRazor extractors in a single call |
analysis.classifyText | textrazor.api.analysis.classifyText | read | Classify text or a URL against built-in or custom TextRazor classifiers |
analysis.extractEntities | textrazor.api.analysis.extractEntities | read | Extract named entities from text or a URL, optionally filtering by relevance and confidence |
classifiers.delete | textrazor.api.classifiers.delete | destructive | Delete a custom classifier and all of its categories |
classifiers.deleteCategory | textrazor.api.classifiers.deleteCategory | destructive | Delete a category from a custom classifier |
classifiers.getCategory | textrazor.api.classifiers.getCategory | read | Get a category from a custom classifier by id |
classifiers.listCategories | textrazor.api.classifiers.listCategories | read | List categories for a custom classifier with limit and offset pagination |
classifiers.put | textrazor.api.classifiers.put | write | Create or update a custom classifier from JSON categories |
dictionaries.addEntries | textrazor.api.dictionaries.addEntries | write | Add or overwrite entries in a custom entity dictionary |
dictionaries.create | textrazor.api.dictionaries.create | write | Create a custom entity dictionary |
dictionaries.delete | textrazor.api.dictionaries.delete | destructive | Delete a custom entity dictionary and all of its entries |
dictionaries.deleteEntry | textrazor.api.dictionaries.deleteEntry | destructive | Delete a dictionary entry by id |
dictionaries.get | textrazor.api.dictionaries.get | read | Get a custom entity dictionary by id |
dictionaries.getEntry | textrazor.api.dictionaries.getEntry | read | Get a dictionary entry by id |
dictionaries.list | textrazor.api.dictionaries.list | read | List custom entity dictionaries on the account |
dictionaries.listEntries | textrazor.api.dictionaries.listEntries | read | List dictionary entries with limit and offset pagination |
在源码中,端点被组织为analysis、account、dictionaries、classifiers四组(见 index.ts 的textrazorEndpointsNested),端点元数据(风险等级与描述)定义在textrazorEndpointMeta中。
四、分析类端点:analyzeContent / classifyText / extractEntities
三个分析端点共用同一套参数体系(endpoints/types.ts):
| 参数 | 类型 | 说明 |
|---|---|---|
text/url | string | 二选一必填,Schema 通过refine强制要求恰好提供一个(Provide exactly one of text or url) |
extractors | ExtractorSchema[] | 抽取器列表。支持entities、topics、words、phrases、dependency-trees、relations、entailments、senses、spelling(analyzeContent必填且至少 1 个) |
classifiers | string[] | 分类器 id 列表(classifyText必填且至少 1 个) |
classifierMaxCategories | number | 每个分类器最多返回的类别数(正整数) |
cleanupMode | 'raw' \| 'stripTags' \| 'cleanHTML' | HTML 清洗模式 |
cleanupReturnCleaned/cleanupReturnRaw | boolean | 是否返回清洗后/原始文本 |
cleanupUseMetadata | boolean | 是否使用页面元数据 |
cleanupCleanHtmlPrecision | 1 \| 2 \| 3 | cleanHTML 精度 |
cleanupCleanHtmlUseTitle | boolean | 是否保留标题 |
downloadRunJavascript | boolean | 抓取 URL 时是否执行 JS |
downloadUserAgent | string | 自定义 User-Agent |
entitiesAllowOverlap | boolean | 是否允许实体重叠 |
entitiesDictionaries | string[] | 使用的自定义实体词典 id |
entitiesFilterDbpediaTypes/entitiesFilterFreebaseTypes | string[] | 按 DBPedia/Freebase 类型过滤实体 |
entitiesIncludeAddressPlaces | boolean | 是否包含地址类地点 |
languageOverride | string | 语言覆盖(≥2 字符) |
rules | string | 自定义规则 |
minRelevanceScore | number | 实体相关度下限[0,1](仅extractEntities) |
minConfidenceScore | number | 实体置信度下限(仅extractEntities) |
三个端点最终都会向 TextRazor 根路径POST /发送application/x-www-form-urlencoded表单(见 endpoints/analysis.ts)。表单字段名(如classifier.maxCategories、cleanup.mode、entities.dictionaries)与 TextRazor 官方参数命名一一对应,由 endpoints/call.ts 的analysisForm负责映射:
function analysisForm(input) { return { text: input.text, url: input.url, extractors: input.extractors, classifiers: input.classifiers, 'classifier.maxCategories': input.classifierMaxCategories, 'cleanup.mode': input.cleanupMode, 'entities.dictionaries': input.entitiesDictionaries, // ... }; }需要注意一个实现细节:classifyText与extractEntities即使未显式传入extractors,也会默认注入['entities'](见 endpoints/analysis.ts)。extractEntities的minRelevanceScore/minConfidenceScore过滤是在响应返回后由插件在本地完成二次过滤,而非通过 API 请求参数实现。
实体的本地缓存
分析完成后,插件会遍历response.entities,按entityId ?? matchedText去重,将matchedText、confidenceScore、relevanceScore、wikiLink、wikidataId等字段通过ctx.db.entities.upsertByEntityId写入数据库(见 endpoints/analysis.ts)。落库失败仅打印告警日志,不会中断请求。
五、账户端点:account.get
account.get对应 GETaccount/,返回当前 TextRazor 套餐信息:plan、concurrentRequestLimit、concurrentRequestsUsed、planDailyRequestsIncluded、requestsUsedToday(见 endpoints/account.ts)。调用后结果会以id: 'current'缓存到ctx.db.accounts,方便后续请求读取套餐与用量而无需重复调用远程 API。
六、自定义词典:dictionaries 端点组
TextRazor 支持自定义实体词典(entity dictionary),用于在分析中识别业务专属名词。本插件将其映射到 TextRazor 的entities/REST 路径(见 endpoints/dictionaries.ts 的dictionaryPath,id 会经过encodeURIComponent处理):
create:PUTentities/{id},请求体 JSON 为{ matchType, caseInsensitive, language }。matchType取值'token' | 'stem';caseInsensitive控制大小写敏感;language指定语言。创建成功后同步缓存到ctx.db.dictionaries;list:GETentities/,列出账户下全部词典;get:GETentities/{id},按 id 获取词典;delete:DELETEentities/{id},删除词典及其全部条目;listEntries:GETentities/{id}/_all,支持limit(正整数)与offset(≥0)分页查询参数;addEntries:POSTentities/{id}/,请求体为条目数组,每条含id(可选)、text(必填,≥1 字符)、data(Record<string, string[]>,可挂任意自定义元数据),用于添加或覆盖词典条目;getEntry:GETentities/{id}/{entryId};deleteEntry:DELETEentities/{id}/{entryId}。
词典条目示例
// 创建词典 await plugin.endpoints.dictionaries.create(ctx, { id: 'products', matchType: 'token', caseInsensitive: true, language: 'eng', }); // 添加条目(text 为匹配文本,data 为自定义元数据) await plugin.endpoints.dictionaries.addEntries(ctx, { id: 'products', entries: [ { id: 'e1', text: 'Corsair', data: { type: ['hardware'] } }, { id: 'e2', text: 'TextRazor', data: { type: ['nlp'] } }, ], });七、自定义分类器:classifiers 端点组
分类器端点映射到 TextRazor 的categories/REST 路径(见 endpoints/classifiers.ts):
put:PUTcategories/{id},请求体为类别数组,每项含categoryId(必填)、label、query(查询表达式,必填)。这是创建或更新分类器(含其全部类别)的原子操作。成功后每个类别会以${classifierId}:${categoryId}为键缓存到ctx.db.categories;delete:DELETEcategories/{id},删除分类器及所有类别;listCategories:GETcategories/{id}/_all,支持limit/offset分页;getCategory:GETcategories/{id}/{categoryId};deleteCategory:DELETEcategories/{id}/{categoryId}。
分类器示例
await plugin.endpoints.classifiers.put(ctx, { id: 'sentiment', categories: [ { categoryId: 'pos', label: 'Positive', query: 'good OR great OR excellent' }, { categoryId: 'neg', label: 'Negative', query: 'bad OR terrible OR awful' }, ], }); // 配合 classifyText 使用 const res = await plugin.endpoints.analysis.classifyText(ctx, { text: 'This is a great product!', classifiers: ['sentiment'], classifierMaxCategories: 1, });八、鉴权与错误处理
鉴权
- 认证方式:API Key。请求头为
X-TextRazor-Key(见 client.ts 的buildConfig),API 基地址为https://api.textrazor.com(TEXTRAZOR_API_BASE,版本1.0.0); - 租户密钥在首次使用端点时由 Corsair 提示录入并持久化,密钥解析由
keyBuilder完成; - 凭证缺失时抛出
AuthMissingError('textrazor', 'api_key')。
错误模型
所有请求失败都会统一包装为TextrazorAPIError(client.ts),它继承自Error并携带status、statusText、body、retryAfter、rateLimitReset、rateLimitRemaining、rateLimitLimit等字段,其中限流信息来自底层 HTTP 层的ApiError。
插件内置了针对 HTTP 状态码的响应文案映射(client.ts):
| 状态码 | 含义 |
|---|---|
| 400 | Bad Request |
| 401 | Unauthorized |
| 413 | Request too large |
| 429 | Too Many Requests |
| 500 | Internal Server Error |
assertTextrazorOk还会检查响应体中的ok === false字段,一旦出现即抛出带error/message的TextrazorAPIError,用于兜底 TextRazor 部分接口在业务失败时返回 200 +ok: false的协议约定。
重试策略
插件的errorHandlers(error-handlers.ts)将错误分类为六类,并给出默认重试策略:
| 错误类别 | 匹配条件 | 默认策略 |
|---|---|---|
VALIDATION_ERROR | ZodError | 不重试 |
RATE_LIMIT_ERROR | 429 或消息含rate limit | 最多重试 3 次,指数退避,并利用Retry-After头 |
AUTH_ERROR | 401 或消息含unauthorized/invalid api key/used up its quota | 不重试 |
NOT_FOUND_ERROR | 404 或消息含not found | 不重试 |
BAD_REQUEST_ERROR | 400 / 413 或消息含bad request/request too large | 不重试 |
SERVER_ERROR | 5xx | 最多重试 2 次,指数退避 |
你可以在创建插件时通过errorHandlers选项覆盖默认策略(index.ts 的mergeErrorHandlers会按类别合并,DEFAULT兜底)。
九、数据库 Schema 与缓存对象
插件定义了五类数据库对象(schema/database.ts),均带fetchedAt时间戳:
| 表对象 | 关键字段 | 写入时机 |
|---|---|---|
TextrazorAccount | plan、concurrentRequestLimit、concurrentRequestsUsed、planDailyRequestsIncluded、requestsUsedToday | account.get |
TextrazorDictionary | matchType、caseInsensitive、language | dictionaries.create |
TextrazorDictionaryEntry | text、data、dictionaryId | — |
TextrazorCategory | categoryId、label、query、classifierId | classifiers.put |
TextrazorEntity | entityId、matchedText、confidenceScore、relevanceScore、wikiLink、wikidataId | 三个分析端点 |
实体、账户、词典、类别的落库均通过upsertByEntityId实现幂等写入,保证重复调用不产生重复记录。
十、端点的校验与调用链路
每个端点都遵循"解析 → 请求 → 校验 → 落库 → 事件日志"的调用链(以analyzeContent为例,见 endpoints/analysis.ts):
AnalyzeContentInputSchema.parse(input):Zod 校验输入,失败抛出ZodError(触发VALIDATION_ERROR处理器);makeTextrazorRequest发送 POST 请求,携带X-TextRazor-Key与 URL 编码表单;assertTextrazorOk检查ok === false;AnalyzeContentOutputSchema.parse(raw)校验并归一化输出;- 实体结果写入
ctx.db.entities; logEventFromContext记录事件textrazor.analysis.analyzeContent为completed状态。
HTTP 层由corsair/http的request提供,表单序列化逻辑(数组转逗号分隔、布尔转true/false、空值跳过)见 client.ts。
十一、验证与测试
插件仓库内置了三套测试,可作为接入正确性的参考:
- api.test.ts:针对 Schema 与请求构造的单元测试;
- plugin.test.ts:验证插件注册、端点元数据与风险等级;
- live.test.ts:真实调用 TextRazor 的在线测试(需有效 API Key)。
运行测试:
pnpm test # 在 packages/textrazor 目录下 pnpm typecheck # TypeScript 类型检查插件的端点清单与说明文档还以plugin-docs.yaml的形式维护,供 Corsair 生态自动生成文档使用。
十二、版本与许可
- 当前版本:
0.1.1,包名@corsair-dev/textrazor,ESM 模块,产物输出至dist/(见 package.json); - 许可协议:Apache-2.0;
- 完整类型定义、端点文档与更多示例可查阅仓库内 插件文档 及 Corsair 官方文档中心(docs.corsair.dev 下的 plugins/textrazor 页面)。
小结
@corsair-dev/textrazor用约 17 个类型安全端点覆盖了 TextRazor 的核心能力:文本分析(实体、主题、分类)、账户用量查询、自定义词典与自定义分类器的全生命周期管理。通过 Corsair 的租户密钥体系、Zod 输入校验、自动结果缓存与内置重试策略,你可以省去手写 HTTP 客户端、鉴权存储与错误处理的成本,快速为你的 Agent 应用接入可靠的 NLP 文本理解能力。接入时只需牢记三点:分析请求必须且仅能提供text或url之一;extractors与classifiers按端点要求提供;凭证通过api_key方式由 Corsair 统一托管。
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考