This commit is contained in:
熊潇 2025-05-25 14:01:37 +08:00
commit 8f52a10ae0
42 changed files with 1946 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
node_modules
dist
app.config.json5
apps.config.json
deploy.tar.gz
cache-file
/apps
logs
.env*
!.env.example
config.json
pack-dist
videos/output*

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

25
bun.config.mjs Normal file
View File

@ -0,0 +1,25 @@
// @ts-check
// https://bun.sh/docs/bundler
// @ts-ignore
import { resolvePath } from '@kevisual/use-config/env';
import pkg from './package.json';
import { execSync } from 'node:child_process';
// bun run src/index.ts --
await Bun.build({
target: 'node',
format: 'esm',
entrypoints: [resolvePath('./src/provider/index.ts')],
outdir: resolvePath('./dist'),
naming: {
entry: 'ai-provider.js',
},
define: {
ENVISION_VERSION: JSON.stringify(pkg.version),
},
env: 'ENVISION_*',
});
const cmd = 'dts -i src/provider/index.ts -o ai-provider.d.ts';
execSync(cmd, { stdio: 'inherit' });

77
package.json Normal file
View File

@ -0,0 +1,77 @@
{
"name": "@kevisual/ai",
"version": "0.0.4",
"description": "后面需要把ai-center的provider模块提取出去",
"main": "index.js",
"basename": "/root/ai-center-services",
"app": {
"entry": "dist/app.mjs",
"key": "ai-center-services",
"type": "system-app"
},
"files": [
"dist",
"types"
],
"scripts": {
"build": "npm run clean && bun bun.config.mjs",
"dev": "bun run --watch bun.config.mjs",
"test": "tsx test/**/*.ts",
"clean": "rm -rf dist",
"pub": "envision pack -p -u"
},
"keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT",
"packageManager": "pnpm@10.11.0",
"type": "module",
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "public"
},
"exports": {
".": {
"import": "./dist/ai-provider.js",
"types": "./dist/ai-provider.d.ts"
},
"./ai-provider": {
"import": "./dist/ai-provider.js",
"types": "./dist/ai-provider.d.ts"
}
},
"devDependencies": {
"@kevisual/code-center-module": "0.0.19",
"@kevisual/mark": "0.0.7",
"@kevisual/router": "0.0.21",
"@kevisual/types": "^0.0.10",
"@kevisual/use-config": "^1.0.17",
"@types/bun": "^1.2.14",
"@types/crypto-js": "^4.2.2",
"@types/formidable": "^3.4.5",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.15.21",
"@vitejs/plugin-basic-ssl": "^2.0.0",
"cookie": "^1.0.2",
"cross-env": "^7.0.3",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"dotenv": "^16.5.0",
"formidable": "^3.5.4",
"ioredis": "^5.6.1",
"json5": "^2.2.3",
"lodash-es": "^4.17.21",
"openai": "4.103.0",
"pm2": "^6.0.6",
"rimraf": "^6.0.1",
"rollup": "^4.41.0",
"rollup-plugin-dts": "^6.2.1",
"sequelize": "^6.37.7",
"tape": "^5.9.0",
"tiktoken": "^1.0.21",
"typescript": "^5.8.3",
"vite": "^6.3.5"
},
"dependencies": {
"@kevisual/logger": "^0.0.4"
}
}

2
readme.md Normal file
View File

@ -0,0 +1,2 @@
# AI Center

6
src/modules/logger.ts Normal file
View File

@ -0,0 +1,6 @@
import { Logger } from '@kevisual/logger';
export const logger = new Logger({
level: process?.env?.LOG_LEVEL || 'info',
showTime: true,
});

View File

@ -0,0 +1,14 @@
import { BaseChat, BaseChatOptions } from '../core/chat.ts';
export type OllamaOptions = BaseChatOptions;
/**
*
*/
export class Custom extends BaseChat {
static BASE_URL = 'https://api.deepseek.com/v1/';
constructor(options: OllamaOptions) {
const baseURL = options.baseURL || Custom.BASE_URL;
super({ ...(options as BaseChatOptions), baseURL: baseURL });
}
}

View File

@ -0,0 +1,10 @@
import { BaseChat, BaseChatOptions } from '../core/chat.ts';
export type DeepSeekOptions = Partial<BaseChatOptions>;
export class DeepSeek extends BaseChat {
static BASE_URL = 'https://api.deepseek.com/v1/';
constructor(options: DeepSeekOptions) {
const baseURL = options.baseURL || DeepSeek.BASE_URL;
super({ ...(options as BaseChatOptions), baseURL: baseURL });
}
}

View File

@ -0,0 +1,11 @@
// https://api-inference.modelscope.cn/v1/
import { BaseChat, BaseChatOptions } from '../core/chat.ts';
export type ModelScopeOptions = Partial<BaseChatOptions>;
export class ModelScope extends BaseChat {
static BASE_URL = 'https://api-inference.modelscope.cn/v1/';
constructor(options: ModelScopeOptions) {
const baseURL = options.baseURL || ModelScope.BASE_URL;
super({ ...options, baseURL: baseURL } as any);
}
}

View File

@ -0,0 +1,47 @@
import { BaseChat, BaseChatOptions } from '../core/index.ts';
import type { ChatMessage, ChatMessageOptions } from '../core/index.ts';
export type OllamaOptions = Partial<BaseChatOptions>;
type OllamaModel = {
name: string;
model: string;
modified_at: string;
size: number;
digest: string;
details: {
parent_model: string;
format: string; // example: gguf
family: string; // example qwen
families: string[];
parameter_size: string;
quantization_level: string; // example: Q4_K_M Q4_0
};
};
export class Ollama extends BaseChat {
static BASE_URL = 'http://localhost:11434/v1';
constructor(options: OllamaOptions) {
const baseURL = options.baseURL || Ollama.BASE_URL;
super({ ...(options as BaseChatOptions), baseURL: baseURL });
}
async chat(messages: ChatMessage[], options?: ChatMessageOptions) {
const res = await super.chat(messages, options);
console.log('thunk', this.getChatUsage());
return res;
}
/**
*
* @returns
*/
async listModels(): Promise<{ models: OllamaModel[] }> {
const _url = new URL(this.baseURL);
const tagsURL = new URL('/api/tags', _url);
return this.openai.get(tagsURL.toString());
}
async listRunModels(): Promise<{ models: OllamaModel[] }> {
const _url = new URL(this.baseURL);
const tagsURL = new URL('/api/ps', _url);
return this.openai.get(tagsURL.toString());
}
}

View File

@ -0,0 +1,39 @@
import { BaseChat, BaseChatOptions } from '../core/chat.ts';
import { OpenAI } from 'openai';
export type SiliconFlowOptions = Partial<BaseChatOptions>;
type SiliconFlowUsageData = {
id: string;
name: string;
image: string;
email: string;
isAdmin: boolean;
balance: string;
status: 'normal' | 'suspended' | 'expired' | string; // 状态
introduce: string;
role: string;
chargeBalance: string;
totalBalance: string;
category: string;
};
type SiliconFlowUsageResponse = {
code: number;
message: string;
status: boolean;
data: SiliconFlowUsageData;
};
export class SiliconFlow extends BaseChat {
static BASE_URL = 'https://api.siliconflow.cn/v1';
constructor(options: SiliconFlowOptions) {
const baseURL = options.baseURL || SiliconFlow.BASE_URL;
super({ ...(options as BaseChatOptions), baseURL: baseURL });
}
async getUsageInfo(): Promise<SiliconFlowUsageResponse> {
return this.openai.get('/user/info');
}
async chat(messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[], options?: Partial<OpenAI.Chat.Completions.ChatCompletionCreateParams>) {
const res = await super.chat(messages, options);
return res;
}
}

View File

@ -0,0 +1,10 @@
import { BaseChat, BaseChatOptions } from '../core/chat.ts';
export type VolcesOptions = Partial<BaseChatOptions>;
export class Volces extends BaseChat {
static BASE_URL = 'https://ark.cn-beijing.volces.com/api/v3/';
constructor(options: VolcesOptions) {
const baseURL = options.baseURL || Volces.BASE_URL;
super({ ...(options as BaseChatOptions), baseURL: baseURL });
}
}

143
src/provider/core/chat.ts Normal file
View File

@ -0,0 +1,143 @@
import { OpenAI } from 'openai';
import type {
BaseChatInterface,
ChatMessageComplete,
ChatMessage,
ChatMessageOptions,
BaseChatUsageInterface,
ChatStream,
EmbeddingMessage,
EmbeddingMessageComplete,
} from './type.ts';
export type BaseChatOptions<T = Record<string, any>> = {
/**
* baseURL
*/
baseURL: string;
/**
*
*/
model?: string;
/**
* apiKey
*/
apiKey: string;
/**
* 使
*/
isBrowser?: boolean;
/**
* false
*/
stream?: boolean;
} & T;
export class BaseChat implements BaseChatInterface, BaseChatUsageInterface {
/**
* baseURL
*/
baseURL: string;
/**
*
*/
model: string;
/**
* apiKey
*/
apiKey: string;
/**
* 使
*/
isBrowser: boolean;
/**
* openai实例
*/
openai: OpenAI;
prompt_tokens: number;
total_tokens: number;
completion_tokens: number;
constructor(options: BaseChatOptions) {
this.baseURL = options.baseURL;
this.model = options.model;
this.apiKey = options.apiKey;
this.isBrowser = options.isBrowser ?? false;
this.openai = new OpenAI({
apiKey: this.apiKey,
baseURL: this.baseURL,
dangerouslyAllowBrowser: this.isBrowser,
});
}
/**
*
*/
async chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete> {
const createParams: OpenAI.Chat.Completions.ChatCompletionCreateParams = {
model: this.model,
messages,
...options,
stream: false,
};
const res = (await this.openai.chat.completions.create(createParams)) as ChatMessageComplete;
this.prompt_tokens = res.usage?.prompt_tokens ?? 0;
this.total_tokens = res.usage?.total_tokens ?? 0;
this.completion_tokens = res.usage?.completion_tokens ?? 0;
return res;
}
async chatStream(messages: ChatMessage[], options?: ChatMessageOptions) {
const createParams: OpenAI.Chat.Completions.ChatCompletionCreateParams = {
model: this.model,
messages,
...options,
stream: true,
};
if (createParams.response_format) {
throw new Error('response_format is not supported in stream mode');
}
return this.openai.chat.completions.create(createParams) as unknown as ChatStream;
}
/**
*
*/
test() {
return this.chat([{ role: 'user', content: 'Hello, world!' }]);
}
/**
* 使
* @returns
*/
getChatUsage() {
return {
prompt_tokens: this.prompt_tokens,
total_tokens: this.total_tokens,
completion_tokens: this.completion_tokens,
};
}
getHeaders(headers?: Record<string, string>) {
return {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`,
...headers,
};
}
/**
* embedding
* @param text
* @returns
*/
async generateEmbeddingCore(text: string | string[], options?: EmbeddingMessage): Promise<EmbeddingMessageComplete> {
const embeddingModel = options?.model || this.model;
const res = await this.openai.embeddings.create({
model: embeddingModel,
input: text,
encoding_format: 'float',
...options,
});
this.prompt_tokens += res.usage.prompt_tokens;
this.total_tokens += res.usage.total_tokens;
return res;
}
}

View File

@ -0,0 +1,27 @@
import { ChatStream } from './type.ts';
// export type { BaseChat, BaseChatOptions } from './chat.ts';
export * from './chat.ts'
// export {
// ChatMessage,
// ChatMessageOptions, //
// ChatMessageComplete,
// ChatMessageStream,
// BaseChatInterface,
// BaseChatUsageInterface,
// ChatStream,
// EmbeddingMessage,
// EmbeddingMessageComplete,
// } from './type.ts';
export * from './type.ts'
/**
* for await (const chunk of chatStream) {
* console.log(chunk);
* }
* @param chatStream
*/
export const readStream = async (chatStream: ChatStream) => {
for await (const chunk of chatStream) {
console.log(chunk);
}
};

View File

@ -0,0 +1,105 @@
// Updated: Aug. 20, 2024
// Live demo: https://jina.ai/tokenizer
// LICENSE: Apache-2.0 (https://www.apache.org/licenses/LICENSE-2.0)
// COPYRIGHT: Jina AI
// Define variables for magic numbers
const MAX_HEADING_LENGTH = 7;
const MAX_HEADING_CONTENT_LENGTH = 200;
const MAX_HEADING_UNDERLINE_LENGTH = 200;
const MAX_HTML_HEADING_ATTRIBUTES_LENGTH = 100;
const MAX_LIST_ITEM_LENGTH = 200;
const MAX_NESTED_LIST_ITEMS = 6;
const MAX_LIST_INDENT_SPACES = 7;
const MAX_BLOCKQUOTE_LINE_LENGTH = 200;
const MAX_BLOCKQUOTE_LINES = 15;
const MAX_CODE_BLOCK_LENGTH = 1500;
const MAX_CODE_LANGUAGE_LENGTH = 20;
const MAX_INDENTED_CODE_LINES = 20;
const MAX_TABLE_CELL_LENGTH = 200;
const MAX_TABLE_ROWS = 20;
const MAX_HTML_TABLE_LENGTH = 2000;
const MIN_HORIZONTAL_RULE_LENGTH = 3;
const MAX_SENTENCE_LENGTH = 400;
const MAX_QUOTED_TEXT_LENGTH = 300;
const MAX_PARENTHETICAL_CONTENT_LENGTH = 200;
const MAX_NESTED_PARENTHESES = 5;
const MAX_MATH_INLINE_LENGTH = 100;
const MAX_MATH_BLOCK_LENGTH = 500;
const MAX_PARAGRAPH_LENGTH = 1000;
const MAX_STANDALONE_LINE_LENGTH = 800;
const MAX_HTML_TAG_ATTRIBUTES_LENGTH = 100;
const MAX_HTML_TAG_CONTENT_LENGTH = 1000;
const LOOKAHEAD_RANGE = 100; // Number of characters to look ahead for a sentence boundary
const AVOID_AT_START = `[\\s\\]})>,']`;
const PUNCTUATION = `[.!?…]|\\.{3}|[\\u2026\\u2047-\\u2049]|[\\p{Emoji_Presentation}\\p{Extended_Pictographic}]`;
const QUOTE_END = `(?:'(?=\`)|''(?=\`\`))`;
const SENTENCE_END = `(?:${PUNCTUATION}(?<!${AVOID_AT_START}(?=${PUNCTUATION}))|${QUOTE_END})(?=\\S|$)`;
const SENTENCE_BOUNDARY = `(?:${SENTENCE_END}|(?=[\\r\\n]|$))`;
const LOOKAHEAD_PATTERN = `(?:(?!${SENTENCE_END}).){1,${LOOKAHEAD_RANGE}}${SENTENCE_END}`;
const NOT_PUNCTUATION_SPACE = `(?!${PUNCTUATION}\\s)`;
const SENTENCE_PATTERN = `${NOT_PUNCTUATION_SPACE}(?:[^\\r\\n]{1,{MAX_LENGTH}}${SENTENCE_BOUNDARY}|[^\\r\\n]{1,{MAX_LENGTH}}(?=${PUNCTUATION}|${QUOTE_END})(?:${LOOKAHEAD_PATTERN})?)${AVOID_AT_START}*`;
export const textSplitter = new RegExp(
"(" +
// 1. Headings (Setext-style, Markdown, and HTML-style, with length constraints)
`(?:^(?:[#*=-]{1,${MAX_HEADING_LENGTH}}|\\w[^\\r\\n]{0,${MAX_HEADING_CONTENT_LENGTH}}\\r?\\n[-=]{2,${MAX_HEADING_UNDERLINE_LENGTH}}|<h[1-6][^>]{0,${MAX_HTML_HEADING_ATTRIBUTES_LENGTH}}>)[^\\r\\n]{1,${MAX_HEADING_CONTENT_LENGTH}}(?:</h[1-6]>)?(?:\\r?\\n|$))` +
"|" +
// New pattern for citations
`(?:\\[[0-9]+\\][^\\r\\n]{1,${MAX_STANDALONE_LINE_LENGTH}})` +
"|" +
// 2. List items (bulleted, numbered, lettered, or task lists, including nested, up to three levels, with length constraints)
`(?:(?:^|\\r?\\n)[ \\t]{0,3}(?:[-*+•]|\\d{1,3}\\.\\w\\.|\\[[ xX]\\])[ \\t]+${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_LIST_ITEM_LENGTH))}` +
`(?:(?:\\r?\\n[ \\t]{2,5}(?:[-*+•]|\\d{1,3}\\.\\w\\.|\\[[ xX]\\])[ \\t]+${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_LIST_ITEM_LENGTH))}){0,${MAX_NESTED_LIST_ITEMS}}` +
`(?:\\r?\\n[ \\t]{4,${MAX_LIST_INDENT_SPACES}}(?:[-*+•]|\\d{1,3}\\.\\w\\.|\\[[ xX]\\])[ \\t]+${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_LIST_ITEM_LENGTH))}){0,${MAX_NESTED_LIST_ITEMS}})?)` +
"|" +
// 3. Block quotes (including nested quotes and citations, up to three levels, with length constraints)
`(?:(?:^>(?:>|\\s{2,}){0,2}${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_BLOCKQUOTE_LINE_LENGTH))}\\r?\\n?){1,${MAX_BLOCKQUOTE_LINES}})` +
"|" +
// 4. Code blocks (fenced, indented, or HTML pre/code tags, with length constraints)
`(?:(?:^|\\r?\\n)(?:\`\`\`|~~~)(?:\\w{0,${MAX_CODE_LANGUAGE_LENGTH}})?\\r?\\n[\\s\\S]{0,${MAX_CODE_BLOCK_LENGTH}}?(?:\`\`\`|~~~)\\r?\\n?` +
`|(?:(?:^|\\r?\\n)(?: {4}|\\t)[^\\r\\n]{0,${MAX_LIST_ITEM_LENGTH}}(?:\\r?\\n(?: {4}|\\t)[^\\r\\n]{0,${MAX_LIST_ITEM_LENGTH}}){0,${MAX_INDENTED_CODE_LINES}}\\r?\\n?)` +
`|(?:<pre>(?:<code>)?[\\s\\S]{0,${MAX_CODE_BLOCK_LENGTH}}?(?:</code>)?</pre>))` +
"|" +
// 5. Tables (Markdown, grid tables, and HTML tables, with length constraints)
`(?:(?:^|\\r?\\n)(?:\\|[^\\r\\n]{0,${MAX_TABLE_CELL_LENGTH}}\\|(?:\\r?\\n\\|[-:]{1,${MAX_TABLE_CELL_LENGTH}}\\|){0,1}(?:\\r?\\n\\|[^\\r\\n]{0,${MAX_TABLE_CELL_LENGTH}}\\|){0,${MAX_TABLE_ROWS}}` +
`|<table>[\\s\\S]{0,${MAX_HTML_TABLE_LENGTH}}?</table>))` +
"|" +
// 6. Horizontal rules (Markdown and HTML hr tag)
`(?:^(?:[-*_]){${MIN_HORIZONTAL_RULE_LENGTH},}\\s*$|<hr\\s*/?>)` +
"|" +
// 10. Standalone lines or phrases (including single-line blocks and HTML elements, with length constraints)
`(?!${AVOID_AT_START})(?:^(?:<[a-zA-Z][^>]{0,${MAX_HTML_TAG_ATTRIBUTES_LENGTH}}>)?${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_STANDALONE_LINE_LENGTH))}(?:</[a-zA-Z]+>)?(?:\\r?\\n|$))` +
"|" +
// 7. Sentences or phrases ending with punctuation (including ellipsis and Unicode punctuation)
`(?!${AVOID_AT_START})${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_SENTENCE_LENGTH))}` +
"|" +
// 8. Quoted text, parenthetical phrases, or bracketed content (with length constraints)
"(?:" +
`(?<!\\w)\"\"\"[^\"]{0,${MAX_QUOTED_TEXT_LENGTH}}\"\"\"(?!\\w)` +
`|(?<!\\w)(?:['\"\`'"])[^\\r\\n]{0,${MAX_QUOTED_TEXT_LENGTH}}\\1(?!\\w)` +
`|(?<!\\w)\`[^\\r\\n]{0,${MAX_QUOTED_TEXT_LENGTH}}'(?!\\w)` +
`|(?<!\\w)\`\`[^\\r\\n]{0,${MAX_QUOTED_TEXT_LENGTH}}''(?!\\w)` +
`|\\([^\\r\\n()]{0,${MAX_PARENTHETICAL_CONTENT_LENGTH}}(?:\\([^\\r\\n()]{0,${MAX_PARENTHETICAL_CONTENT_LENGTH}}\\)[^\\r\\n()]{0,${MAX_PARENTHETICAL_CONTENT_LENGTH}}){0,${MAX_NESTED_PARENTHESES}}\\)` +
`|\\[[^\\r\\n\\[\\]]{0,${MAX_PARENTHETICAL_CONTENT_LENGTH}}(?:\\[[^\\r\\n\\[\\]]{0,${MAX_PARENTHETICAL_CONTENT_LENGTH}}\\][^\\r\\n\\[\\]]{0,${MAX_PARENTHETICAL_CONTENT_LENGTH}}){0,${MAX_NESTED_PARENTHESES}}\\]` +
`|\\$[^\\r\\n$]{0,${MAX_MATH_INLINE_LENGTH}}\\$` +
`|\`[^\`\\r\\n]{0,${MAX_MATH_INLINE_LENGTH}}\`` +
")" +
"|" +
// 9. Paragraphs (with length constraints)
`(?!${AVOID_AT_START})(?:(?:^|\\r?\\n\\r?\\n)(?:<p>)?${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_PARAGRAPH_LENGTH))}(?:</p>)?(?=\\r?\\n\\r?\\n|$))` +
"|" +
// 11. HTML-like tags and their content (including self-closing tags and attributes, with length constraints)
`(?:<[a-zA-Z][^>]{0,${MAX_HTML_TAG_ATTRIBUTES_LENGTH}}(?:>[\\s\\S]{0,${MAX_HTML_TAG_CONTENT_LENGTH}}?</[a-zA-Z]+>|\\s*/>))` +
"|" +
// 12. LaTeX-style math expressions (inline and block, with length constraints)
`(?:(?:\\$\\$[\\s\\S]{0,${MAX_MATH_BLOCK_LENGTH}}?\\$\\$)|(?:\\$[^\\$\\r\\n]{0,${MAX_MATH_INLINE_LENGTH}}\\$))` +
"|" +
// 14. Fallback for any remaining content (with length constraints)
`(?!${AVOID_AT_START})${SENTENCE_PATTERN.replace(/{MAX_LENGTH}/g, String(MAX_STANDALONE_LINE_LENGTH))}` +
")",
"gmu"
);

29
src/provider/core/type.ts Normal file
View File

@ -0,0 +1,29 @@
import OpenAI from 'openai';
export type ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;
export type ChatMessageOptions = Partial<OpenAI.Chat.Completions.ChatCompletionCreateParams>;
export type ChatMessageComplete = OpenAI.Chat.Completions.ChatCompletion;
export type ChatMessageStream = OpenAI.Chat.Completions.ChatCompletion;
export type EmbeddingMessage = Partial<OpenAI.Embeddings.EmbeddingCreateParams>;
export type EmbeddingMessageComplete = OpenAI.Embeddings.CreateEmbeddingResponse;
export interface BaseChatInterface {
chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete>;
}
export interface BaseChatUsageInterface {
/**
*
*/
prompt_tokens: number;
/**
*
*/
total_tokens: number;
/**
*
*/
completion_tokens: number;
}
export type ChatStream = AsyncGenerator<ChatMessageComplete, void, unknown>;

63
src/provider/index.ts Normal file
View File

@ -0,0 +1,63 @@
export * from './core/index.ts';
import { BaseChat } from './core/chat.ts';
import { Ollama } from './chat-adapter/ollama.ts';
import { SiliconFlow } from './chat-adapter/siliconflow.ts';
import { Custom } from './chat-adapter/custom.ts';
import { Volces } from './chat-adapter/volces.ts';
import { DeepSeek } from './chat-adapter/deepseek.ts';
import { ModelScope } from './chat-adapter/model-scope.ts';
import { ChatMessage } from './core/type.ts';
export const OllamaProvider = Ollama;
export const SiliconFlowProvider = SiliconFlow;
export const CustomProvider = Custom;
export const VolcesProvider = Volces;
export const DeepSeekProvider = DeepSeek;
export const ModelScopeProvider = ModelScope;
export const ProviderMap = {
Ollama: OllamaProvider,
SiliconFlow: SiliconFlowProvider,
Custom: CustomProvider,
Volces: VolcesProvider,
DeepSeek: DeepSeekProvider,
ModelScope: ModelScopeProvider,
BaseChat: BaseChat,
};
type ProviderManagerConfig = {
provider: string;
model: string;
apiKey: string;
baseURL?: string;
};
export class ProviderManager {
provider: BaseChat;
constructor(config: ProviderManagerConfig) {
const { provider, model, apiKey, baseURL } = config;
const Provider = ProviderMap[provider] as typeof BaseChat;
if (!Provider) {
throw new Error(`Provider ${provider} not found`);
}
const providerConfig = {
model,
apiKey,
baseURL,
};
if (!providerConfig.baseURL) {
delete providerConfig.baseURL;
}
this.provider = new Provider(providerConfig);
}
static async createProvider(config: ProviderManagerConfig) {
if (!config.baseURL) {
delete config.baseURL;
}
const pm = new ProviderManager(config);
return pm.provider;
}
async chat(messages: ChatMessage[]) {
return this.provider.chat(messages);
}
}

View File

@ -0,0 +1,107 @@
import { BaseChat, BaseChatOptions } from '../core/chat.ts';
import { EmbeddingMessage } from '../core/type.ts';
export type KnowledgeOptions<T = Record<string, string>> = BaseChatOptions<
{
embeddingModel: string;
splitSize?: number; // 分块大小 默认 2000
splitOverlap?: number; // 分块重叠 默认 200
batchSize?: number; // 批量大小 默认 4, 4*2000=8000
} & T
>;
/**
*
* 1. Embedding generate
* 2. retriever
* 3. reranker
*/
export class KnowledgeBase extends BaseChat {
embeddingModel: string;
splitSize: number;
splitOverlap: number;
batchSize: number;
constructor(options: KnowledgeOptions) {
super(options);
this.embeddingModel = options.embeddingModel;
this.splitSize = options.splitSize || 2000;
this.splitOverlap = options.splitOverlap || 200;
this.prompt_tokens = 0;
this.total_tokens = 0;
this.batchSize = options.batchSize || 4;
}
/**
* embedding
* @param text
* @returns
*/
async generateEmbedding(text: string | string[]) {
try {
const res = await this.generateEmbeddingCore(text, { model: this.embeddingModel });
return { code: 200, data: res.data };
} catch (error) {
const has413 = error?.message?.includes('413');
if (has413) {
return {
code: 413,
message: '请求过大,请分割文本',
};
}
return {
code: error?.code || 500,
message: '生成embedding失败',
};
}
}
/**
* embedding
* @param text
* @returns
*/
async generateEmbeddingBatch(textArray: string[]) {
const batchSize = this.batchSize || 4;
const embeddings: number[][] = [];
for (let i = 0; i < textArray.length; i += batchSize) {
const batch = textArray.slice(i, i + batchSize);
const res = await this.generateEmbedding(batch);
if (res.code === 200) {
embeddings.push(...res.data.map((item) => item.embedding));
}
}
return embeddings;
}
/**
* , embedding
* @param text
* @returns
*/
async splitLongText(text: string) {
// 分割文本
const chunks: string[] = [];
let startIndex = 0;
while (startIndex < text.length) {
// 计算当前chunk的结束位置
const endIndex = Math.min(startIndex + this.splitSize, text.length);
// 提取当前chunk
const chunk = text.substring(startIndex, endIndex);
chunks.push(chunk);
// 移动到下一个起始位置,考虑重叠
startIndex = endIndex - this.splitOverlap;
// 如果下一个起始位置已经超出或者太接近文本结尾,就结束循环
if (startIndex >= text.length - this.splitOverlap) {
break;
}
}
// 为每个chunk生成embedding
const embeddings = await this.generateEmbeddingBatch(chunks);
// 返回文本片段和对应的embedding
return chunks.map((chunk, index) => ({
text: chunk,
embedding: embeddings[index],
}));
}
}

View File

@ -0,0 +1,7 @@
import { KnowledgeBase, KnowledgeOptions } from './knowledge-base.ts';
export class Knowledge extends KnowledgeBase {
constructor(options: KnowledgeOptions) {
super(options);
}
}

View File

@ -0,0 +1 @@
export * from './video/siliconflow.ts';

View File

@ -0,0 +1,37 @@
import { SiliconFlow } from '../../chat-adapter/siliconflow.ts';
export class VideoSiliconFlow extends SiliconFlow {
constructor(opts: any) {
super(opts);
}
async uploadAudioVoice(audioBase64: string | Blob | File) {
const pathname = 'uploads/audio/voice';
const url = `${this.baseURL}/${pathname}`;
const headers = {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${this.apiKey}`,
};
const formData = new FormData();
// formData.append('audio', 'data:audio/mpeg;base64,aGVsbG93b3JsZA==');
// formData.append('audio', audioBase64);
formData.append('file', audioBase64);
formData.append('model', 'FunAudioLLM/CosyVoice2-0.5B');
formData.append('customName', 'test_name');
formData.append('text', '在一无所知中, 梦里的一天结束了,一个新的轮回便会开始');
const res = await fetch(url, {
method: 'POST',
headers,
body: formData,
}).then((res) => res.json());
console.log('uploadAudioVoice', res);
}
async audioSpeech() {
this.openai.audio.speech.create({
model: 'FunAudioLLM/CosyVoice2-0.5B',
voice: 'alloy',
input: '在一无所知中, 梦里的一天结束了,一个新的轮回便会开始',
response_format: 'mp3',
});
}
}

View File

@ -0,0 +1,52 @@
import type { Permission } from '@kevisual/permission';
export type AIModel = {
/**
*
*/
provider: string;
/**
*
*/
model: string;
/**
*
*/
group: string;
/**
*
*/
dayLimit?: number;
/**
* token限制
*/
tokenLimit?: number;
};
export type SecretKey = {
/**
*
*/
group: string;
/**
* API密钥
*/
apiKey: string;
/**
*
*/
decryptKey?: string;
};
export type AIConfig = {
title?: string;
description?: string;
models: AIModel[];
secretKeys: SecretKey[];
permission?: Permission;
filter?: {
objectKey: string;
type: 'array' | 'object';
operate: 'removeAttribute' | 'remove';
attribute: string[];
}[];
};

View File

@ -0,0 +1,86 @@
import { numTokensFromString } from './token.ts';
// 常量定义
const CHUNK_SIZE = 512; // 每个chunk的最大token数
const MAGIC_SEPARATOR = '🦛';
const DELIMITER = [',', '.', '!', '?', '\n', '', '。', '', ''];
const PARAGRAPH_DELIMITER = '\n\n';
export interface Chunk {
chunkId: number;
text: string;
tokens: number;
}
/**
* chunk的大小不超过最大token数
* @param chunk
* @returns token数的数组
*/
function ensureChunkSize(chunk: string): Array<[string, number]> {
const tokens = numTokensFromString(chunk);
if (tokens <= CHUNK_SIZE) {
return [[chunk, tokens]];
}
// 在分隔符后添加魔法分隔符
let processedChunk = chunk;
for (const delimiter of DELIMITER) {
// 转义特殊字符
const escapedDelimiter = delimiter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
processedChunk = processedChunk.replace(new RegExp(escapedDelimiter, 'g'), delimiter + MAGIC_SEPARATOR);
}
const chunks: Array<[string, number]> = [];
let tail = '';
// 按CHUNK_SIZE分割文本
for (let i = 0; i < processedChunk.length; i += CHUNK_SIZE) {
const sentences = (processedChunk.slice(i, i + CHUNK_SIZE) + ' ').split(MAGIC_SEPARATOR);
const currentChunk = tail + sentences.slice(0, -1).join('');
if (currentChunk.trim()) {
const tokenCount = numTokensFromString(currentChunk);
chunks.push([currentChunk, tokenCount]);
}
tail = sentences[sentences.length - 1].trim();
}
// 处理最后剩余的tail
if (tail) {
const tokenCount = numTokensFromString(tail);
chunks.push([tail, tokenCount]);
}
return chunks;
}
/**
* chunks
* @param text
* @returns chunks数组
*/
export async function getChunks(text: string): Promise<Chunk[]> {
// 按段落分割文本
const paragraphs = text
.split(PARAGRAPH_DELIMITER)
.map((p) => p.trim())
.filter((p) => p);
const chunks: Chunk[] = [];
let currentIndex = 0;
// 处理每个段落
for (const paragraph of paragraphs) {
const splittedParagraph = ensureChunkSize(paragraph);
for (const [text, tokens] of splittedParagraph) {
chunks.push({
chunkId: currentIndex,
text,
tokens,
});
currentIndex++;
}
}
return chunks;
}

View File

@ -0,0 +1,206 @@
import { Permission } from '@kevisual/permission';
import AES from 'crypto-js/aes.js';
import Utf8 from 'crypto-js/enc-utf8.js';
const CryptoJS = { AES, enc: { Utf8 } };
// 加密函数
export function encryptAES(plainText: string, secretKey: string) {
return CryptoJS.AES.encrypt(plainText, secretKey).toString();
}
// 解密函数
export function decryptAES(cipherText: string, secretKey: string) {
const bytes = CryptoJS.AES.decrypt(cipherText, secretKey);
return bytes.toString(CryptoJS.enc.Utf8);
}
type AIModel = {
/**
*
*/
provider: string;
/**
*
*/
model: string;
/**
*
*/
group: string;
/**
*
*/
dayLimit?: number;
/**
* token限制
*/
tokenLimit?: number;
};
type SecretKey = {
/**
*
*/
group: string;
/**
* API密钥
*/
apiKey: string;
/**
*
*/
decryptKey?: string;
};
export type GetProviderOpts = {
model: string;
group: string;
decryptKey?: string;
};
export type ProviderResult = {
provider: string;
model: string;
group: string;
apiKey: string;
dayLimit?: number;
tokenLimit?: number;
baseURL?: string;
/**
*
*/
decryptKey?: string;
};
export type AIConfig = {
title?: string;
description?: string;
models: AIModel[];
secretKeys: SecretKey[];
permission?: Permission;
filter?: {
objectKey: string;
type: 'array' | 'object';
operate: 'removeAttribute' | 'remove';
attribute: string[];
}[];
};
export class AIConfigParser {
private config: AIConfig;
result: ProviderResult;
constructor(config: AIConfig) {
this.config = config;
}
/**
*
* @param opts
* @returns
*/
getProvider(opts: GetProviderOpts): ProviderResult {
const { model, group, decryptKey } = opts;
const modelConfig = this.config.models.find((m) => m.model === model && m.group === group);
const groupConfig = this.config.secretKeys.find((m) => m.group === group);
if (!modelConfig) {
throw new Error(`在模型组 ${group} 中未找到模型 ${model}`);
}
const mergeConfig = {
...modelConfig,
...groupConfig,
decryptKey: decryptKey || groupConfig?.decryptKey,
};
// 验证模型配置
if (!mergeConfig.provider) {
throw new Error(`模型 ${model} 未配置提供商`);
}
if (!mergeConfig.model) {
throw new Error(`模型 ${model} 未配置模型名称`);
}
if (!mergeConfig.apiKey) {
throw new Error(`${group} 未配置 API 密钥`);
}
if (!mergeConfig.group) {
throw new Error(`${group} 未配置`);
}
this.result = mergeConfig;
return mergeConfig;
}
/**
*
* @param opts
* @returns
*/
async getSecretKey(opts?: {
getCache?: (key: string) => Promise<string>;
setCache?: (key: string, value: string) => Promise<void>;
providerResult?: ProviderResult;
}) {
const { getCache, setCache, providerResult } = opts || {};
const { apiKey, decryptKey, group = '', model } = providerResult || this.result;
const cacheKey = `${group}--${model}`;
if (!decryptKey) {
return apiKey;
}
if (getCache) {
const cache = await getCache(cacheKey);
if (cache) {
return cache;
}
}
const secretKey = decryptAES(apiKey, decryptKey);
if (setCache) {
await setCache(cacheKey, secretKey);
}
return secretKey;
}
/**
*
* @param plainText
* @param secretKey
* @returns
*/
encrypt(plainText: string, secretKey: string) {
return encryptAES(plainText, secretKey);
}
/**
*
* @param cipherText
* @param secretKey
* @returns
*/
decrypt(cipherText: string, secretKey: string) {
return decryptAES(cipherText, secretKey);
}
/**
*
* @returns
*/
getSelectOpts() {
const { models, secretKeys = [] } = this.config;
return models.map((model) => {
const selectOpts = secretKeys.find((m) => m.group === model.group);
return {
...model,
...selectOpts,
};
});
}
getConfig(keepSecret?: boolean, config?: AIConfig) {
const chatConfig = config ?? this.config;
if (keepSecret) {
return chatConfig;
}
// 过滤掉secret中的所有apiKey移除掉并返回chatConfig
const { secretKeys = [], ...rest } = chatConfig || {};
return {
...rest,
secretKeys: secretKeys.map((item) => {
return {
...item,
apiKey: undefined,
decryptKey: undefined,
};
}),
};
}
}

View File

@ -0,0 +1,34 @@
import { encoding_for_model, get_encoding } from 'tiktoken';
const MODEL_TO_ENCODING = {
'gpt-4': 'cl100k_base',
'gpt-4-turbo': 'cl100k_base',
'gpt-3.5-turbo': 'cl100k_base',
'text-embedding-ada-002': 'cl100k_base',
'text-davinci-002': 'p50k_base',
'text-davinci-003': 'p50k_base',
} as const;
export function numTokensFromString(text: string, model: keyof typeof MODEL_TO_ENCODING = 'gpt-3.5-turbo'): number {
try {
// 对于特定模型使用专门的编码器
const encoder = encoding_for_model(model);
const tokens = encoder.encode(text);
const tokenCount = tokens.length;
encoder.free(); // 释放编码器
return tokenCount;
} catch (error) {
try {
// 如果模型特定的编码器失败,尝试使用基础编码器
const encoder = get_encoding(MODEL_TO_ENCODING[model]);
const tokens = encoder.encode(text);
const tokenCount = tokens.length;
encoder.free(); // 释放编码器
return tokenCount;
} catch (error) {
// 如果编码失败使用一个粗略的估计平均每个字符0.25个token
return Math.ceil(text.length * 0.25);
}
}
}

65
src/test/chunks/01-get.ts Normal file
View File

@ -0,0 +1,65 @@
import { getChunks } from '../../../../../src/provider/utils/chunk.ts';
const str = 'Hello world this is a test 你好沙盒 very big';
const str2 = `不能直接使用 tiktokenOpenAI的分词器来计算 Qwen 模型的 Token 数量因为两者的分词规则Tokenization和词表Vocabulary完全不同。
tiktoken OpenAI GPT gpt-3.5-turbo, gpt-4
Qwen 使 BPE
tiktoken Qwen
OpenAI (tiktoken): "你好" ['你', '好']2 Tokens
Qwen: "你好" ['你好']1 Token
使使 tiktoken Qwen Token
API
Qwen
Hugging Face transformers Qwen
python
from transformers import AutoTokenizer
# Qwen Qwen-7B
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B", trust_remote_code=True)
text = "你好Qwen模型"
tokens = tokenizer.tokenize(text) #
token_count = len(tokenizer.encode(text, add_special_tokens=False))
print("分词结果:", tokens)
print("Token数量:", token_count)
trust_remote_code=True
Qwen Hugging Face
Token
Qwen
tiktoken
tiktoken Python Qwen Hugging Face
tiktoken Qwen
使Qwen transformers
Qwen 使
tokenizer.vocab `
const chunks = getChunks(str2);
console.log(chunks);

View File

@ -0,0 +1,9 @@
import { encryptAES, decryptAES } from '../../../../../src/provider/utils/parse-config.ts';
const plainx = process.env.API_KEY;
const decryptKey = process.env.DECRYPT_KEY;
const encrypt = encryptAES(plainx, decryptKey);
console.log('encrypt', encrypt);
const decrypt = decryptAES(encrypt, decryptKey);
console.log(decrypt);

View File

@ -0,0 +1,35 @@
curl --request POST \
--url https://api.siliconflow.cn/v1/chat/completions \
--header 'Authorization: Bearer sk-qbiigkzoaamuqxtwlgkugodncebkfbosemadfubjrseobpvx' \
--header 'Content-Type: application/json' \
--data '{
"model": "Qwen/Qwen3-14B",
"messages": [
{
"role": "user",
"content": "计算a+b的值"
}
],
"stream": false,
"max_tokens": 512,
"stop": null,
"temperature": 0.7,
"top_p": 0.7,
"top_k": 50,
"frequency_penalty": 0.5,
"n": 1,
"response_format": {
"type": "text"
},
"tools": [
{
"type": "function",
"function": {
"description": "计算abc算法的值a=1b=2c=3",
"name": "compouted",
"parameters": {},
"strict": false
}
}
]
}'

116
src/test/func-call/demo.ts Normal file
View File

@ -0,0 +1,116 @@
import { SiliconFlow } from '../../../../../src/provider/chat-adapter/siliconflow.ts';
import { Ollama } from '../../../../../src/provider/chat-adapter/ollama.ts';
import dotenv from 'dotenv';
dotenv.config();
const siliconflow = new SiliconFlow({
apiKey: process.env.SILICONFLOW_API_KEY,
model: 'Qwen/Qwen3-14B',
});
const ollama = new Ollama({
model: 'qwen3:32b',
apiKey: process.env.OLLAMA_API_KEY,
baseURL: process.env.OLLAMA_BASE_URL,
});
const main = async () => {
const usage = await siliconflow.getUsageInfo();
console.log(usage);
};
// 1. 定义工具函数
const availableFunctions: Record<string, (args: any) => Promise<any>> = {
get_time: async (args: { location: string }) => {
// 模拟API调用
console.log('time', args);
return {
time: '2022-03-22 12:00:00',
};
},
get_location: async (args: { symbol: string }) => {
// 模拟API调用
console.log('location', args);
return {
city: 'Beijing',
};
},
};
// main();
const funcCall = async (model = siliconflow) => {
const tools = [
{
type: 'function',
function: {
name: 'get_time',
description: '获取当前时间',
parameters: {
type: 'object',
properties: {
place: {
type: 'string',
description: '位置',
},
},
required: ['place'],
},
},
},
{
type: 'function',
function: {
name: 'get_location',
description: '获取当前位置',
// parameters: {},
parameters: {},
strict: false,
},
},
];
const messages: any[] = [{ role: 'user', content: '获取当前位置的当前时间' }];
const res = await model.chat(messages, {
tools: tools as any,
});
console.log(res.choices[0]);
const assistantMessage = res.choices[0].message;
const finish_reason = res.choices[0].finish_reason;
messages.push(assistantMessage);
let toolCalls = assistantMessage.tool_calls;
console.log("toolCalls", JSON.stringify(toolCalls));
let maxRetries = 3;
while (toolCalls && toolCalls.length > 0) {
// 处理每个函数调用
for (const toolCall of toolCalls) {
const functionName = toolCall.function.name;
const functionArgs = JSON.parse(toolCall.function.arguments);
// 调用本地函数
const functionResponse = await availableFunctions[functionName](functionArgs);
// 将结果添加到消息历史
messages.push({
role: 'tool',
name: functionName,
content: JSON.stringify(functionResponse),
tool_call_id: toolCall.id,
});
}
// 第二次调用 - 将函数结果发送给模型获取最终回复
const secondResponse = await model.chat(messages, {
tools: tools as any,
});
const finalMessage = secondResponse.choices[0].message;
messages.push(finalMessage);
const _toolCalls = finalMessage.tool_calls;
console.log("toolCalls", JSON.stringify(toolCalls) ,finalMessage.role);
toolCalls = _toolCalls ? _toolCalls : [];
maxRetries--;
if (maxRetries <= 0) {
break;
}
console.log('tool calls', toolCalls);
}
console.log(messages);
};
funcCall(ollama as any);

View File

@ -0,0 +1,26 @@
import { ModelScope } from '../../../../../src/provider/chat-adapter/model-scope.ts';
import { log } from '../../../../../src/logger/index.ts';
import util from 'util';
import { config } from 'dotenv';
config();
const chat = new ModelScope({
apiKey: process.env.MODEL_SCOPE_API_KEY,
model: 'Qwen/Qwen2.5-Coder-32B-Instruct',
});
// chat.chat([{ role: 'user', content: 'Hello, world! 1 + 1 equals ?' }]);
const chatMessage = [{ role: 'user', content: 'Hello, world! 1 + 1 equals ?' }];
const main = async () => {
const res = await chat.test();
log.info('test', res);
};
main();
const mainChat = async () => {
const res = await chat.chat(chatMessage as any);
log.info('chat', res);
};
// mainChat();

View File

@ -0,0 +1,37 @@
import { Knowledge } from '../../../../src/provider/knowledge/knowledge.ts';
import fs from 'fs';
import dotenv from 'dotenv';
dotenv.config();
const knowledge = new Knowledge({
embeddingModel: 'bge-m3:latest',
baseURL: 'https://ollama.xiongxiao.me/v1',
model: 'qwq:latest',
apiKey: process.env.OLLAMA_API_KEY,
});
const main = async () => {
const res = await knowledge.generateEmbeddingCore('Hello world this is a test 你好沙盒 very big');
fs.writeFileSync('docs/embedding.json', JSON.stringify(res, null, 2));
console.log(res);
};
main();
const main2 = async () => {
const text1 = 'Hello, world! this is a test';
const text2 = 'Hello, world! this is a test 2';
const text3 = 'Hello, world! this is a test 3';
const text4 = 'Hello, world! this is a test 4';
const text5 = 'Hello, world! this is a test 5';
const text6 = 'Hello, world! this is a test 6';
const text7 = 'Hello, world! this is a test 7';
const text8 = 'Hello, world! this is a test 8';
const text9 = 'Hello, world! this is a test 9';
const text10 = 'Hello, world! this is a test 10';
const res = await knowledge.generateEmbeddingCore([text1, text2, text3, text4, text5, text6, text7, text8, text9, text10]);
fs.writeFileSync('docs/embedding2.json', JSON.stringify(res, null, 2));
console.log(res);
};
// main2();

86
src/test/ollama.ts Normal file
View File

@ -0,0 +1,86 @@
import { Ollama } from '../../../../src/provider/chat-adapter/ollama.ts';
import util from 'util';
const chat = new Ollama({
baseURL: 'https://ollama.xiongxiao.me/v1',
apiKey: 'xiongxiao2233',
model: 'qwq:latest',
});
// chat.chat([{ role: 'user', content: 'Hello, world!' }]);
const main = async () => {
const res = await chat.test();
console.log(util.inspect(res, { depth: null, colors: true }));
};
// main();
const getJson = async () => {
const res = await chat.chat(
[
{ role: 'system', content: '把发送的数据返回给我对应的json只处理完发送的数据。如果发送了多个给我一个数组' },
// { role: 'user', content: '{"name":"John","age":30}' },
{ role: 'user', content: 'name: 张三' },
{ role: 'user', content: 'name: 李四, age: 18' },
],
{
response_format: {
type: 'json_schema',
json_schema: {
name: 'user',
description: '用户信息',
schema: {
type: 'object',
// properties: {
// name: { type: 'string' },
// // age: { type: 'number' },
// },
// // required: ['name', 'age'],
// required: ['name'],
properties: {
name: { type: 'string' },
age: { type: 'number' },
},
required: ['name', 'age'],
},
},
},
n: 10,
},
);
console.log(util.inspect(res, { depth: null, colors: true }));
};
// getJson();
const createChat1 = async () => {
const res = await chat.chat(
[
{ role: 'user', content: 'a=1, b=2, c=3' },
{ role: 'user', content: 'a+b+c=?' },
{ role: 'assistant', content: '给定的值为 \\( a = 1 \\), \\( b = 2 \\), \\( c = 3 \\)。\n' + '\n' + '因此,\\( a + b + c = 1 + 2 + 3 = 6 \\)。' },
{ role: 'user', content: 'a+b+c+4=?' },
],
{
model: 'qwen2.5:7b',
},
);
console.log(util.inspect(res, { depth: null, colors: true }));
};
// createChat1();
const getTags = async () => {
const res = await chat.listModels();
console.log(util.inspect(res, { depth: null, colors: true }));
};
// getTags();
const getRunModels = async () => {
const res = await chat.listRunModels();
console.log('current', new Date().toISOString());
console.log(util.inspect(res, { depth: null, colors: true }));
};
// getRunModels();

View File

@ -0,0 +1,7 @@
import { ProviderManager } from '../../../../../src/provider/index.ts';
import { config } from 'dotenv';
config();
const providerConfig = { provider: 'ModelScope', model: 'Qwen/Qwen2.5-Coder-32B-Instruct', apiKey: process.env.MODEL_SCOPE_API_KEY };
const provider = await ProviderManager.createProvider(providerConfig);
const result = await provider.chat([{ role: 'user', content: '你好' }]);
console.log(result);

View File

@ -0,0 +1,13 @@
import { SiliconFlow } from '../../../../../src/provider/chat-adapter/siliconflow.ts';
import { KnowledgeBase } from '../../../../../src/provider/knowledge/knowledge-base.ts';
export const siliconflow = new SiliconFlow({
apiKey: process.env.SILICONFLOW_API_KEY,
model: 'Qwen/Qwen2-7B-Instruct',
});
export const knowledge = new KnowledgeBase({
apiKey: process.env.SILICONFLOW_API_KEY,
baseURL: SiliconFlow.BASE_URL,
model: 'Qwen/Qwen2-7B-Instruct',
embeddingModel: 'Pro/BAAI/bge-m3',
});

View File

@ -0,0 +1,22 @@
import { SiliconFlow } from '../../../../../src/provider/chat-adapter/siliconflow.ts';
import dotenv from 'dotenv';
dotenv.config();
const siliconflow = new SiliconFlow({
apiKey: process.env.SILICONFLOW_API_KEY,
model: 'Qwen/Qwen2-7B-Instruct',
});
const main = async () => {
const usage = await siliconflow.getUsageInfo();
console.log(usage);
};
main();
const mainChat = async () => {
const res = await siliconflow.chat([{ role: 'user', content: 'Hello, world! 1 + 1 equals ?' }]);
console.log(res);
};
// mainChat();

View File

@ -0,0 +1,18 @@
import { knowledge } from '../common.ts';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// 包含: 9184 个汉字 953 个标点(全角) 2493 个字母 52 个数字
const content = fs.readFileSync(path.join(__dirname, 'qwen.md'), 'utf-8');
const text = 'Hello, world';
const main = async () => {
const res = await knowledge.generateEmbeddingCore([content, content]);
console.log(res);
// 8000 tokens 大概1w个字 2万个字符
console.log('speak', knowledge.getChatUsage());
};
main();

View File

@ -0,0 +1,232 @@
# Qwen的基本背景与概述
Qwen是由通义实验室研发的超大规模语言模型具备强大的语言理解和生成能力能够胜任多种自然语言处理任务。作为一款先进的AI语言模型Qwen不仅能够回答问题、撰写文本、进行逻辑推理还支持多语言交流、代码编写、创意写作等多种应用场景。其核心目标是为用户提供高效、智能的语言交互体验同时满足企业级应用的需求。
Qwen的技术基础建立在深度学习和大规模预训练模型之上借助海量文本数据进行训练使其能够精准理解语义并生成高质量的文本。该模型采用了先进的Transformer架构结合自注意力机制和位置编码使其在处理长文本、复杂语境和多轮对话时表现出色。此外Qwen经过多轮优化支持高效的推理和生成能够在不同计算环境下稳定运行包括云端、本地服务器乃至边缘设备。
在功能方面Qwen的核心能力涵盖自然语言理解NLU、自然语言生成NLG和对话交互等多个领域。它能够准确解析用户的意图并根据上下文生成符合逻辑、流畅自然的回应。同时Qwen支持多语言处理能够理解和生成包括中文、英文、法语、西班牙语等多种语言的文本满足全球化应用场景的需求。此外Qwen还具备代码生成能力能够理解和编写多种编程语言的代码为开发者提供智能辅助。
Qwen的应用场景广泛涵盖智能客服、内容创作、教育辅助、数据分析、科学研究等多个领域。例如在智能客服领域Qwen可以充当虚拟助手自动回答用户咨询提高服务效率在内容创作方面它可以协助撰写新闻报道、营销文案、剧本等提高创作效率在教育领域Qwen可以作为个性化学习助手帮助学生解答问题、提供学习建议。此外Qwen还可用于自动化报告生成、法律文书分析、金融数据分析等专业领域为企业提供智能化解决方案。
总体而言Qwen是一款集强大语言处理能力、多语言支持和广泛适用性于一体的先进语言模型。随着人工智能技术的不断发展Qwen将继续优化其性能拓展更多应用场景为用户提供更智能、更高效的交互体验。
## Qwen的技术架构与核心组件
Qwen的技术架构基于深度学习模型尤其是Transformer架构这是当前最先进的自然语言处理NLP模型之一。Qwen的设计目标是实现高效的文本理解和生成能力使其能够胜任各种复杂的语言任务包括问答、文本摘要、对话交互、代码生成等。为了达到这一目标Qwen依赖于大规模的训练数据、复杂的参数体系以及高效的训练和推理机制从而确保其在不同应用场景下的卓越表现。
### 1. **Transformer架构Qwen的基础模型**
Qwen的核心模型基于Transformer架构这是一种采用自注意力机制Self-Attention和位置编码Positional Encoding的深度学习模型。传统的循环神经网络RNN和长短时记忆网络LSTM在处理长文本时存在一定的局限性而Transformer通过自注意力机制能够同时关注整个输入序列的不同部分从而更有效地捕捉上下文信息。
在Qwen中Transformer架构被优化以支持大规模参数和高效计算。模型的编码器Encoder和解码器Decoder由多个堆叠的Transformer层组成每一层都包含多头注意力Multi-Head Attention和前馈神经网络Feed-Forward Network。这种结构使Qwen能够并行处理大量信息提高训练和推理的效率同时增强对复杂语义关系的理解能力。
### 2. **训练数据构建Qwen的语言知识库**
Qwen的训练数据来源于互联网上的大量文本包括网页、书籍、百科、新闻、论文、代码库等。这些数据涵盖了广泛的领域和语言风格使Qwen能够掌握丰富的知识和语言模式。为了确保训练数据的质量Qwen的训练过程采用了严格的清洗和过滤机制以去除低质量、重复或含有噪声的数据。
此外Qwen的训练数据还包括多语言文本使其能够支持多种语言的处理和生成。这种多语言能力不仅有助于提升模型的泛化能力也使其在国际化应用场景中具备更强的适应性。通过大规模的训练数据Qwen能够学习到丰富的语言结构、词汇关系和语义模式从而在实际应用中提供更加准确和自然的文本生成能力。
### 3. **参数规模Qwen的模型复杂度**
Qwen的参数规模是其强大语言能力的关键因素之一。参数数量决定了模型的表达能力和学习能力Qwen的参数量级达到了超大规模使其能够处理复杂的语言任务。大规模参数使得Qwen在面对不同的输入时能够更准确地理解上下文并生成符合语义逻辑的输出。
为了优化计算效率Qwen采用了模型压缩和分布式训练技术。这些技术使得Qwen能够在不同的计算环境下高效运行包括云端服务器、本地计算机甚至边缘设备。此外Qwen还支持动态调整参数规模以适应不同的计算资源和应用场景从而在性能和效率之间取得平衡。
### 4. **训练与推理机制Qwen的优化策略**
Qwen的训练过程采用了大规模分布式训练策略利用多个GPU或TPU并行计算以加快训练速度并提高模型收敛效率。此外Qwen还结合了混合精度训练Mixed Precision Training和梯度累积Gradient Accumulation等技术以减少内存占用并提升训练稳定性。
在推理阶段Qwen支持多种解码策略包括贪婪解码Greedy Decoding、束搜索Beam Search和采样解码Sampling Decoding以确保生成文本的多样性和准确性。此外Qwen还引入了缓存机制以加速多轮对话中的推理过程使其在实时交互应用中表现更加流畅。
总体而言Qwen的技术架构融合了先进的Transformer模型、大规模训练数据、超大规模参数体系以及高效的训练和推理策略。这一架构使其在自然语言处理任务中具备强大的表现力并能够适应不同应用场景的需求。
## Qwen的核心模块及其功能
Qwen的核心功能由多个关键模块共同支撑包括自然语言理解NLU、自然语言生成NLG和对话交互模块。这些模块各自承担不同的任务同时相互协作使Qwen能够高效地处理复杂的语言任务并提供流畅、准确的交互体验。
### 1. **自然语言理解NLU解析用户输入的语义**
自然语言理解NLU模块是Qwen的基础组成部分负责解析用户的输入文本理解其语义和意图。该模块利用深度学习技术特别是基于Transformer的模型对输入文本进行编码提取关键信息如实体识别、情感分析、意图分类等。例如当用户输入“明天北京天气如何”时NLU模块能够识别出“天气”作为核心主题并提取“明天”和“北京”作为时间与地点信息。
NLU模块的核心任务是将用户的自然语言输入转换为结构化数据以便后续模块能够基于这些信息进行处理。例如在智能客服应用中NLU模块能够识别用户的问题类型如订单查询、产品咨询等并将其分类以指导后续的回复生成。此外NLU模块还支持多语言理解使其能够在不同的语言环境中准确解析用户意图。
### 2. **自然语言生成NLG生成高质量的文本输出**
自然语言生成NLG模块负责根据NLU模块解析的语义信息生成自然、流畅的文本输出。该模块基于深度学习模型结合上下文信息和语法规则生成符合用户需求的文本。例如在回答“明天北京天气如何”这一问题时NLG模块会结合天气预报数据生成诸如“明天北京天气晴朗最高气温25摄氏度”的回答。
NLG模块的核心任务是确保生成的文本既准确又自然符合人类语言的表达习惯。为此该模块采用了多种技术如注意力机制Attention Mechanism和语言模型微调Fine-tuning以提升生成文本的质量。此外NLG模块还支持个性化生成能够根据不同用户的偏好调整表达方式。例如在内容创作场景中Qwen可以根据用户的需求生成不同风格的文本如正式报告、轻松的对话式文本或富有创意的文学作品。
### 3. **对话交互模块:实现多轮对话与上下文理解**
对话交互模块是Qwen的重要组成部分负责管理多轮对话并确保对话的连贯性和一致性。该模块利用上下文记忆机制记录用户的历史对话信息从而在多轮对话中提供更精准的响应。例如如果用户在第一轮对话中询问“北京有哪些旅游景点并在第二轮对话中继续提问“那故宫的门票价格是多少对话交互模块能够识别“故宫”指的是北京的景点并提供相应的信息。
对话交互模块的核心功能包括上下文理解、对话状态追踪Dialogue State Tracking和对话策略优化Dialogue Policy Optimization。这些功能使Qwen能够在复杂的对话环境中保持连贯性并提供更加智能的交互体验。此外该模块还支持强化学习Reinforcement Learning通过不断优化对话策略提高Qwen在不同场景下的对话能力。
### 4. **模块之间的协同作用**
Qwen的NLU、NLG和对话交互模块紧密协作共同完成复杂的语言任务。NLU模块负责解析用户输入提取关键信息NLG模块基于这些信息生成自然流畅的文本对话交互模块则确保多轮对话的连贯性和逻辑性。这种模块化设计使Qwen能够在不同的应用场景中提供高效、智能的语言交互体验无论是在智能客服、内容创作还是个性化推荐等领域都能展现出卓越的能力。
## Qwen的典型应用场景
Qwen作为一款功能强大的超大规模语言模型已经在多个领域展现出广泛的应用潜力。从内容创作到智能客服再到教育辅助和科学研究Qwen的多功能性使其成为各类行业的理想工具。以下将详细介绍Qwen在这些典型场景中的具体应用及其优势。
### 1. **内容创作:提升文本生成效率**
Qwen在内容创作领域具有极高的实用性能够帮助用户高效生成高质量的文本内容。无论是新闻报道、市场营销文案、社交媒体内容还是创意写作Qwen都能提供智能化的辅助。
在新闻写作方面Qwen可以基于给定的关键词或事件快速生成结构清晰、逻辑严谨的新闻稿件。例如在体育赛事报道中Qwen可以自动整理比赛数据、分析比赛过程并生成符合新闻格式的报道大幅减少人工撰写的时间成本。
在广告与营销文案创作方面Qwen能够根据品牌调性和目标受众生成富有吸引力的营销内容。例如针对特定产品Qwen可以自动撰写产品介绍、促销文案或社交媒体推文提高营销活动的效率。此外Qwen还能根据用户反馈不断优化文案风格使其更贴合市场需求。
在创意写作方面Qwen可以作为作者的智能助手提供灵感支持和内容优化。例如在小说创作过程中Qwen可以根据故事情节生成合理的对白、描述场景或补充细节帮助作家克服写作瓶颈。此外Qwen还能协助剧本创作提供角色设定、情节发展建议甚至生成完整的剧本草稿。
### 2. **智能客服:优化客户交互体验**
Qwen在智能客服领域的应用尤为突出能够显著提升客户服务的效率和质量。传统的人工客服往往面临响应时间长、服务覆盖范围有限等问题而Qwen能够提供全天候、个性化的智能客服解决方案。
在在线客服系统中Qwen可以充当虚拟助手自动回答用户的常见问题。例如在电商平台Qwen可以处理订单查询、退换货政策咨询、支付问题等提供即时、准确的答复减少人工客服的负担。此外Qwen能够根据用户的历史对话记录提供个性化推荐如推荐符合用户偏好的商品提升用户体验。
在电话客服系统中Qwen可以作为语音助手与用户进行自然的对话交互。例如在银行或电信服务中Qwen能够自动处理账户查询、业务办理、账单支付等操作减少用户等待时间提高服务效率。此外Qwen还能分析用户的语音情绪识别潜在的投诉或不满并提供相应的安抚建议优化客户关系管理。
### 3. **教育辅助:个性化学习与智能教学**
Qwen在教育领域的应用同样具有广阔的前景能够为学生、教师和教育机构提供智能化的教学辅助工具。
在个性化学习方面Qwen可以充当智能学习助手根据学生的学习进度和薄弱环节提供个性化的学习建议。例如在数学学习过程中Qwen可以自动分析学生的错题推荐相关练习题并提供详细的解题步骤帮助学生巩固知识点。此外Qwen还能根据学生的学习风格调整讲解方式使其更符合个人需求。
在语言学习方面Qwen可以充当智能语言导师提供语法纠正、发音评估和口语练习等功能。例如在英语学习过程中Qwen可以模拟真实的对话场景与用户进行互动纠正语法错误并提供地道的表达建议。此外Qwen还能根据用户的学习目标推荐适合的阅读材料提高语言学习的效率。
在教学辅助方面Qwen可以帮助教师优化教学内容。例如教师可以使用Qwen自动生成课程讲义、测试题或教学案例提高备课效率。此外Qwen还能分析学生的学习数据帮助教师发现班级的整体学习趋势并提供针对性的教学改进方案。
### 4. **科学研究:加速学术研究与数据分析**
Qwen在科学研究领域的应用也日益广泛能够为研究人员提供高效的信息检索、文献分析和实验辅助工具。
在学术写作方面Qwen可以协助研究人员撰写论文、报告和综述文章。例如Qwen可以根据研究主题自动生成论文摘要、文献综述或研究背景提高写作效率。此外Qwen还能帮助研究人员查找相关文献分析研究趋势并提供数据可视化建议使研究成果更易于理解和传播。
在数据分析方面Qwen可以协助研究人员处理大规模文本数据。例如在社会科学研究中Qwen可以自动分析社交媒体文本、调查问卷或新闻报道提取关键信息并生成结构化数据便于后续分析。此外Qwen还能结合机器学习技术进行情感分析、主题建模等任务提高研究的智能化水平。
在实验设计与优化方面Qwen能够提供科学实验的辅助建议。例如在生物医学研究中Qwen可以分析实验数据提供优化实验方案的建议。此外Qwen还能帮助研究人员设计实验变量、预测实验结果并提供统计分析支持提高实验的科学性和可重复性。
总体而言Qwen凭借其强大的语言理解和生成能力在内容创作、智能客服、教育辅助和科学研究等多个领域展现出卓越的应用价值。随着人工智能技术的不断发展Qwen的应用场景将进一步拓展为各行各业提供更加智能化的解决方案。
## Qwen的训练与优化方法
Qwen的训练与优化是其高性能表现的关键所在。作为一个超大规模语言模型Qwen的训练过程涉及庞大的数据集、复杂的训练流程以及多种优化策略以确保模型在不同应用场景下的高效性和准确性。以下将详细介绍Qwen的训练方法包括数据预处理、训练流程和优化策略并探讨其面临的挑战及可能的解决方案。
### 1. **数据预处理:构建高质量的训练数据集**
Qwen的训练数据来源于互联网上的海量文本涵盖网页、书籍、百科、新闻、论文、代码库等多个领域。为了确保训练数据的质量Qwen的训练过程采用了严格的数据预处理步骤包括数据清洗、去重、语言过滤和格式标准化。
首先Qwen的训练数据需要经过清洗以去除低质量或无效内容。例如训练数据中的拼写错误、乱码、重复文本以及含有噪声的网页内容都会影响模型的学习效果。为此Qwen采用了基于规则和机器学习的数据清洗技术以自动识别并剔除低质量数据。
其次去重处理是训练数据预处理的重要环节。由于互联网上的文本数据存在大量重复内容Qwen使用了高效的文本相似度计算方法如MinHash和SimHash以检测并去除高度相似的文本确保训练数据的多样性和有效性。
此外Qwen的训练数据包含多语言文本因此需要进行语言过滤和格式标准化。Qwen采用语言识别模型Language Identification Model来自动识别文本的语言并根据不同的语言制定相应的预处理规则。例如对于中文文本Qwen会进行分词处理而对于英文文本则采用标准的词干化Stemming和词形还原Lemmatization技术以统一文本格式并提高模型的泛化能力。
### 2. **训练流程:大规模分布式训练与优化**
Qwen的训练流程采用了大规模分布式训练策略以加速模型训练并提高计算效率。由于Qwen的参数规模庞大传统的单机训练方式难以满足计算需求因此Qwen采用了基于GPU和TPU的分布式训练架构利用多个计算节点并行处理数据。
在训练过程中Qwen采用了分阶段训练策略包括预训练Pre-training和微调Fine-tuning。预训练阶段的目标是让模型学习通用的语言表示能力通常采用自监督学习方法如掩码语言建模Masked Language Modeling, MLM和下一句预测Next Sentence Prediction, NSP。在这一阶段Qwen会学习大量的文本模式并建立强大的语言理解能力。
微调阶段则是在特定任务上进一步优化模型性能。例如在对话生成任务中Qwen会使用对话数据进行微调以提高模型在多轮对话中的连贯性和逻辑性。此外Qwen还采用了强化学习Reinforcement Learning, RL技术以优化对话交互模块的表现使其能够根据用户反馈不断调整生成策略。
为了提高训练效率Qwen还采用了混合精度训练Mixed Precision Training和梯度累积Gradient Accumulation等优化策略。混合精度训练通过使用半精度浮点数FP16进行计算以减少内存占用并加快训练速度而梯度累积则允许在较小的批量Batch Size下进行训练从而减少计算资源的消耗。
### 3. **优化策略:提高模型性能与推理效率**
Qwen的优化策略主要集中在模型压缩、推理加速和多任务学习等方面以确保模型在实际应用中的高效性和可扩展性。
首先模型压缩技术被广泛应用于Qwen以降低模型的计算成本。例如Qwen采用了知识蒸馏Knowledge Distillation技术通过使用一个较小的学生模型来模仿大模型的行为从而在保持较高性能的同时减少计算资源的消耗。此外Qwen还采用了量化Quantization技术将模型的浮点数参数转换为低精度整数以进一步减少模型的存储和计算需求。
其次Qwen在推理阶段采用了高效的解码策略以提高文本生成的速度。例如Qwen支持束搜索Beam Search和采样解码Sampling Decoding等方法以在生成文本时平衡多样性和准确性。此外Qwen还引入了缓存机制以加速多轮对话中的推理过程使其在实时交互应用中表现更加流畅。
最后Qwen采用了多任务学习Multi-Task Learning策略以提高模型的泛化能力。Qwen在训练过程中同时学习多个相关任务如文本分类、命名实体识别、问答系统等使模型能够更好地适应不同的应用场景。这种策略不仅提高了模型的性能还减少了针对特定任务进行微调的需求从而降低了训练和部署成本。
### 4. **挑战与可能的解决方案**
尽管Qwen的训练与优化方法已经取得了显著成果但仍面临一些挑战。例如训练数据的质量和多样性仍然存在一定的不确定性可能导致模型出现偏差或过度拟合。此外大规模模型的训练和推理成本较高限制了其在资源受限环境下的应用。
为了解决这些问题Qwen未来可能会采用更加精细的数据筛选机制以确保训练数据的多样性和公平性。此外Qwen还可以探索更加高效的模型架构如稀疏训练Sparse Training和自适应计算Adaptive Computation以进一步降低计算成本并提高模型的可扩展性。
## Qwen与其他主流语言模型的对比
在当前的人工智能领域Qwen与GPT系列、Claude等主流语言模型相比具备独特的技术特点和优势。以下将从功能、性能、应用场景和技术创新等方面进行对比分析以突出Qwen的核心竞争力。
### 1. **功能与多语言支持**
Qwen在功能上与GPT系列和Claude类似均具备强大的自然语言理解和生成能力能够进行多轮对话、文本摘要、问答系统、代码生成等任务。然而Qwen在多语言支持方面具有独特优势。相比于GPT-4主要专注于英文环境Qwen不仅支持中文还涵盖了100多种其他语言使其在国际化应用场景中更具适应性。此外Qwen在中文理解与生成方面表现出更强的准确性得益于其大规模的中文训练数据使其在中文语境下能够提供更自然、流畅的交互体验。
相比之下Claude 3在多语言支持上也有一定的能力但其在代码生成和中文处理方面不如Qwen。Qwen的代码生成能力经过专门优化能够理解和编写多种编程语言如Python、Java、C++等这使其在开发者社区中具备较高的实用性。而GPT-4虽然也支持代码生成但其训练数据主要来源于英文环境导致在中文代码解析和生成方面存在一定的局限性。
### 2. **性能与计算效率**
在模型性能方面Qwen采用了高效的训练和推理优化策略使其在大规模计算环境中具备更高的吞吐量和更低的延迟。Qwen支持混合精度训练和模型压缩技术如知识蒸馏和量化能够在保持较高性能的同时降低计算资源的消耗。这种优化策略使得Qwen在云端和边缘设备上均能高效运行而GPT-4和Claude 3在资源消耗上相对较高尤其是在处理长文本或多轮对话时计算需求较大。
此外Qwen的推理加速技术如缓存机制和高效的解码策略如束搜索和采样解码使其在实时交互场景中表现更加流畅。相比之下GPT-4和Claude 3在推理速度上略逊于Qwen尤其是在高并发请求的情况下可能会出现响应延迟。
### 3. **应用场景与行业适配性**
Qwen的应用场景广泛尤其在中文互联网生态中具有较强的适配性。例如在智能客服、内容创作、教育辅助和科研数据分析等领域Qwen能够提供高度定制化的解决方案。相比之下GPT-4主要面向英文用户和国际企业虽然在英语环境下的应用较为成熟但在中文市场上的本地化支持相对较弱。Claude 3则在隐私保护和安全性方面具有优势适用于金融、法律等对数据敏感的行业但其在中文处理和代码生成方面的能力不如Qwen。
此外Qwen在代码生成和开发者工具方面的优化使其在软件工程领域具有较强的应用价值。例如Qwen能够自动编写代码、调试错误并提供优化建议而GPT-4和Claude 3虽然也支持代码生成但其在特定编程语言上的优化程度不及Qwen。
### 4. **技术创新与模型迭代**
Qwen在技术创新方面也展现出独特的优势。例如Qwen采用了强化学习Reinforcement Learning技术使其在多轮对话和交互式任务中能够不断优化自身的回复策略。此外Qwen的多模态能力也在不断扩展未来有望支持图像和视频理解进一步提升其在多媒体交互场景中的表现。相比之下GPT-4和Claude 3在多模态支持方面仍处于早期阶段尚未完全整合视觉和语言模型的能力。
此外Qwen的模型迭代速度较快能够快速响应用户反馈并进行优化。例如Qwen的版本更新通常涵盖性能优化、功能增强和安全性提升而GPT-4和Claude 3的更新周期较长且主要依赖于核心模型的改进缺乏灵活的定制化调整能力。
综上所述Qwen在多语言支持、代码生成、计算效率、行业适配性和技术创新等方面均展现出独特的优势。相较于GPT系列和Claude等主流模型Qwen不仅具备强大的语言处理能力还在中文生态和开发者社区中具有更强的适用性使其成为当前AI语言模型领域的重要竞争者。
## Qwen的未来发展与潜在方向
随着人工智能技术的不断进步Qwen在未来的发展将围绕几个核心方向展开包括功能增强、技术优化和行业应用的进一步拓展。这些发展方向不仅将提升Qwen的性能和适用性也将推动其在更多领域的深度融合和创新应用。
### 1. **多模态能力的扩展**
当前Qwen主要专注于文本处理但未来的发展方向之一是增强其多模态能力使其能够同时理解和处理文本、图像、音频和视频等多种形式的信息。这一能力的提升将使Qwen在人机交互、智能助手和内容生成等场景中发挥更大的作用。例如在智能客服领域Qwen可以结合语音识别和图像分析技术提供更加自然和高效的交互体验在教育领域Qwen可以解析教材中的图文内容并提供个性化的学习建议。此外Qwen的多模态能力还可以应用于内容创作使其能够根据图像或视频生成相关的文本描述提高创作效率和质量。
### 2. **领域适应与垂直应用优化**
尽管Qwen已经具备广泛的应用能力但未来的发展将更加注重特定行业的垂直优化。例如在医疗领域Qwen可以结合医学知识库提供专业的诊断辅助和健康咨询在法律领域Qwen可以优化法律文书的自动生成和案例分析能力提高法律工作者的效率在金融领域Qwen可以增强对市场数据的分析能力提供智能投资建议和风险预测。通过针对不同行业的数据和需求进行微调Qwen将在各个垂直领域提供更加精准和高效的解决方案。
### 3. **推理与逻辑能力的提升**
当前Qwen在自然语言理解和生成方面表现出色但在复杂推理和逻辑分析方面仍有提升空间。未来的发展方向之一是增强Qwen的推理能力使其能够处理更复杂的逻辑任务如数学计算、科学推导和因果推理。这一能力的提升将使Qwen在科研、工程设计和决策支持等场景中发挥更大的作用。例如在科学研究中Qwen可以协助研究人员进行假设验证和数据分析在企业决策中Qwen可以提供基于逻辑推理的商业策略建议。此外Qwen还可以结合强化学习技术使其在交互过程中不断优化自身的推理策略提高回答的准确性和逻辑性。
### 4. **跨语言与全球化的进一步优化**
虽然Qwen已经支持100多种语言但未来的发展将进一步优化其跨语言能力使其在不同语言之间的转换和理解更加自然和准确。例如Qwen可以增强对小语种的支持使其在全球范围内提供更加均衡的语言服务。此外Qwen可以优化跨语言的对话交互能力使其在多语言环境中提供更加流畅的翻译和交流体验。这一能力的提升将使Qwen在国际化企业、跨文化交流和全球教育等领域发挥更大的作用。
### 5. **模型轻量化与部署优化**
为了适应不同的计算环境Qwen的未来发展方向还包括模型轻量化和部署优化。当前Qwen的模型规模较大主要适用于云端计算但未来的发展目标是使其能够在本地设备和边缘计算环境中高效运行。例如通过模型压缩、知识蒸馏和量化技术Qwen可以在手机、平板电脑和IoT设备上提供高效的推理能力而无需依赖云端计算资源。此外Qwen还可以优化其推理速度和内存占用使其在实时交互和高并发场景中表现更加稳定和高效。
随着这些发展方向的推进Qwen将在人工智能领域发挥更加重要的作用为各行各业提供更加智能和高效的解决方案。
## Qwen的技术优势与行业影响
Qwen凭借其先进的技术架构和强大的功能在人工智能领域展现出卓越的表现。其基于Transformer的深度学习模型结合大规模训练数据和超大规模参数体系使其在自然语言理解、生成和对话交互等方面达到行业领先水平。Qwen不仅能够精准解析用户意图还能生成高质量、符合语境的文本支持多轮对话和个性化交互。此外Qwen的代码生成能力、多语言支持和高效推理优化使其在开发者社区和国际化应用场景中具备独特优势。
在实际应用中Qwen已经广泛应用于内容创作、智能客服、教育辅助、科学研究等多个领域。在内容创作方面Qwen能够自动生成新闻报道、营销文案、剧本等提高创作效率在智能客服领域Qwen能够提供全天候的自动化应答提升客户体验在教育领域Qwen可以作为个性化学习助手帮助学生答疑解惑优化教学方案在科研领域Qwen能够辅助学术写作、数据分析和实验设计加速科研进程。这些应用不仅提升了各行业的效率也推动了人工智能技术的普及和落地。
Qwen的成功离不开其强大的技术基础和持续的优化策略。其训练数据覆盖广泛的文本来源结合数据清洗、去重和语言过滤技术确保模型的泛化能力。在训练过程中Qwen采用大规模分布式训练、混合精度训练和梯度累积等优化策略提高训练效率并减少计算资源消耗。此外Qwen的推理加速技术如缓存机制和高效的解码策略使其在实时交互场景中表现更加流畅。这些技术优化不仅提升了Qwen的性能也为其在不同计算环境下的部署提供了灵活性。
与其他主流语言模型相比Qwen在多语言支持、代码生成和计算效率方面展现出独特优势。相较于GPT系列和Claude等模型Qwen在中文处理和本地化应用方面更加精准同时支持多种编程语言的代码生成使其在开发者社区中具有更高的实用性。此外Qwen的模型压缩和轻量化技术使其在资源受限环境下仍能高效运行而GPT-4和Claude 3在推理速度和计算资源消耗方面相对较高。这些技术优势使Qwen在人工智能领域占据重要地位并推动其在更多行业的深入应用。
展望未来Qwen的发展方向将围绕多模态能力扩展、垂直行业优化、推理能力增强和全球化部署优化展开。Qwen将进一步提升其跨语言能力使其在全球范围内提供更加均衡的语言服务。此外Qwen的模型轻量化和部署优化将使其在本地设备和边缘计算环境中高效运行为实时交互和高并发场景提供稳定的解决方案。随着人工智能技术的持续演进Qwen将在内容创作、智能客服、教育、科研等领域发挥更大作用为各行各业提供更加智能、高效的AI解决方案。
## Qwen的技术优势与行业影响
Qwen凭借其先进的技术架构和强大的功能在人工智能领域展现出卓越的表现。其基于Transformer的深度学习模型结合大规模训练数据和超大规模参数体系使其在自然语言理解、生成和对话交互等方面达到行业领先水平。Qwen不仅能够精准解析用户意图还能生成高质量、符合语境的文本支持多轮对话和个性化交互。此外Qwen的代码生成能力、多语言支持和高效推理优化使其在开发者社区和国际化应用场景中具备独特优势。
在实际应用中Qwen已经广泛应用于内容创作、智能客服、教育辅助、科学研究等多个领域。在内容创作方面Qwen能够自动生成新闻报道、营销文案、剧本等提高创作效率在智能客服领域Qwen能够提供全天候的自动化应答提升客户体验在教育领域Qwen可以作为个性化学习助手帮助学生答疑解惑优化教学方案在科研领域Qwen能够辅助学术写作、数据分析和实验设计加速科研进程。这些应用不仅提升了各行业的效率也推动了人工智能技术的普及和落地。
Qwen的成功离不开其强大的技术基础和持续的优化策略。其训练数据覆盖广泛的文本来源结合数据清洗、去重和语言过滤技术确保模型的泛化能力。在训练过程中Qwen采用大规模分布式训练、混合精度训练和梯度累积等优化策略提高训练效率并减少计算资源消耗。此外Qwen的推理加速技术如缓存机制和高效的解码策略使其在实时交互场景中表现更加流畅。这些技术优化不仅提升了Qwen的性能也为其在不同计算环境下的部署提供了灵活性。
与其他主流语言模型相比Qwen在多语言支持、代码生成和计算效率方面展现出独特优势。相较于GPT系列和Claude等模型Qwen在中文处理和本地化应用方面更加精准同时支持多种编程语言的代码生成使其在开发者社区中具有更高的实用性。此外Qwen的模型压缩和轻量化技术使其在资源受限环境下仍能高效运行而GPT-4和Claude 3在推理速度和计算资源消耗方面相对较高。这些技术优势使Qwen在人工智能领域占据重要地位并推动其在更多行业的深入应用。
展望未来Qwen的发展方向将围绕多模态能力扩展、垂直行业优化、推理能力增强和全球化部署优化展开。Qwen将进一步提升其跨语言能力使其在全球范围内提供更加均衡的语言服务。此外Qwen的模型轻量化和部署优化将使其在本地设备和边缘计算环境中高效运行为实时交互和高并发场景提供稳定的解决方案。随着人工智能技术的持续演进Qwen将在内容创作、智能客服、教育、科研等领域发挥更大作用为各行各业提供更加智能、高效的AI解决方案。

View File

@ -0,0 +1,100 @@
import { SiliconFlow } from '../../../../../../src/provider/chat-adapter/siliconflow.ts';
import { VideoSiliconFlow } from '../../../../../../src/provider/media/video/siliconflow.ts';
import dotenv from 'dotenv';
import fs from 'fs';
import path from 'path';
import Stream from 'stream';
dotenv.config();
const siliconflow = new SiliconFlow({
apiKey: process.env.SILICONFLOW_API_KEY,
model: 'Qwen/Qwen2-7B-Instruct',
});
const videoSiliconflow = new VideoSiliconFlow({
apiKey: process.env.SILICONFLOW_API_KEY,
model: 'Qwen/Qwen2-7B-Instruct',
});
const main = async () => {
const usage = await siliconflow.getUsageInfo();
console.log(usage);
};
// main();
const mainChat = async () => {
const test2=`我永远记得那个改变一切的下午。十八岁生日后的第三天,我正坐在自家后院的老橡树杈上,用平板电脑调试我最新设计的森林动物追踪程序。我的红发——妈妈总说像"燃烧的枫叶"——在午后的阳光下泛着铜色的光泽,有几缕不听话的发丝被微风拂过我的脸颊。
"芮薇!""外婆发来加密信息,说需要你马上过去一趟。"
退"过度谨慎总比后悔莫及"`
try {
const res = await siliconflow.openai.audio.speech.create({
model: 'FunAudioLLM/CosyVoice2-0.5B',
// voice: 'FunAudioLLM/CosyVoice2-0.5B:diana',
voice: 'speech:test:h36jngt7ms:zarwclhblfjfyonslejr',
// input: '在一无所知中, 梦里的一天结束了,一个新的轮回便会开始',
// input: '这是一个新的轮回,非常有趣的故事。',
input: test2,
response_format: 'mp3',
});
console.log(res);
const dir = path.join(process.cwd(), 'videos');
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const filePath = path.join(dir, `output-${Date.now()}.mp3`);
// 假设 res 是一个可读流
if (res instanceof Stream.Readable) {
const writeStream = fs.createWriteStream(filePath);
res.pipe(writeStream);
return new Promise((resolve, reject) => {
writeStream.on('finish', () => {
console.log('文件已保存至:', filePath);
resolve(filePath);
});
writeStream.on('error', reject);
});
}
// 假设 res 是一个 ArrayBuffer 或 Buffer
else if (res.arrayBuffer) {
const buffer = Buffer.from(await res.arrayBuffer());
fs.writeFileSync(filePath, buffer);
console.log('文件已保存至:', filePath);
return filePath;
}
// 假设 res 是一个包含 blob 的对象
else if (res.blob) {
// @ts-ignore
const buffer = Buffer.from(res.blob, 'base64');
fs.writeFileSync(filePath, buffer);
console.log('文件已保存至:', filePath);
return filePath;
} else {
throw new Error('无法识别的响应格式');
}
} catch (error) {
console.error('保存音频文件时出错:', error);
throw error;
}
};
mainChat();
const vidioUpload = async () => {
const filePath = path.join(process.cwd(), 'videos', 'my_speech_text.mp3');
const fileBuffer = fs.readFileSync(filePath);
const fileBase64 = 'data:audio/mpeg;base64,' + fileBuffer.toString('base64');
console.log('fileBase64', fileBase64.slice(0, 100));
const fileBlob = new Blob([fileBuffer], { type: 'audio/wav' });
const file = new File([fileBlob], 'my_speech_text.mp3', { type: 'audio/mp3' });
const res = await videoSiliconflow.uploadAudioVoice(file);
// console.log('vidioUpload', res);
// uri:speech:test:h36jngt7ms:zarwclhblfjfyonslejr
return res;
};
// vidioUpload();

16
tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"extends": "@kevisual/types/json/backend.json",
"compilerOptions": {
"baseUrl": "./",
"allowImportingTsExtensions": true,
"paths": {
"@/*": [
"src/*"
]
}
},
"include": [
"src/**/*.ts",
],
"exclude": [],
}

BIN
videos/my_speech_text.mp3 Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
在一无所知中, 梦里的一天结束了,一个新的轮回便会开始

BIN
videos/my_speech_text.wav Normal file

Binary file not shown.