update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kevisual/ai",
|
||||
"version": "0.0.18",
|
||||
"version": "0.0.19",
|
||||
"description": "AI Center Services",
|
||||
"main": "index.js",
|
||||
"basename": "/root/ai-center-services",
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { BailianProvider } from '../../provider/index.ts'
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
import { App } from '@kevisual/router'
|
||||
import util from 'node:util';
|
||||
const ai = new BailianProvider({
|
||||
apiKey: process.env.BAILIAN_API_KEY || '',
|
||||
model: 'qwen-turbo-latest',
|
||||
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
|
||||
})
|
||||
|
||||
|
||||
// const res = await ai.chat([
|
||||
|
||||
// {
|
||||
// role: 'user',
|
||||
// content: `1+1等于多少?`
|
||||
// },
|
||||
|
||||
// ],
|
||||
// )
|
||||
// // console.log('AI Response:', res);
|
||||
// const content = res.choices[0].message?.content || ''
|
||||
|
||||
// console.log(util.inspect(res, { depth: null }))
|
||||
|
||||
// console.log('responseText', ai.responseText)
|
||||
|
||||
|
||||
// const res = await ai.chatStream([
|
||||
|
||||
// {
|
||||
// role: 'user',
|
||||
// content: `1+1等于多少?`
|
||||
// },
|
||||
|
||||
// ],
|
||||
// )
|
||||
// // console.log('AI Response:', res);
|
||||
// export const readStream = async (chatStream) => {
|
||||
// let buffer = '';
|
||||
// for await (const chunk of chatStream) {
|
||||
// // chunk 已经是解码后的字符串,直接拼接即可
|
||||
// buffer += chunk;
|
||||
// }
|
||||
// console.log('AI Response:', buffer);
|
||||
// };
|
||||
|
||||
// await readStream(res);
|
||||
|
||||
const embe = await ai.generateEmbeddingCore([
|
||||
'你好,世界!',
|
||||
'Hello, world!',
|
||||
], {
|
||||
model: 'text-embedding-v4'
|
||||
});
|
||||
|
||||
console.log('Embedding Response:', util.inspect(embe, { depth: null }));
|
||||
@@ -1,65 +0,0 @@
|
||||
import { getChunks } from '../../provider/utils/chunk.ts';
|
||||
|
||||
const str = 'Hello world this is a test 你好沙盒 very big';
|
||||
|
||||
|
||||
const str2 = `不能直接使用 tiktoken(OpenAI的分词器)来计算 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);
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Jimen } from "../jimeng/index.ts"
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const jimeng = new Jimen({
|
||||
token: process.env.JIMENG_TOKEN,
|
||||
})
|
||||
|
||||
console.log("Generating image...");
|
||||
|
||||
await jimeng.generateImage({
|
||||
prompt: "创建一幅未来城市的数字艺术作品,充满科技感和创新元素,色彩鲜艳,细节丰富",
|
||||
resolution: "2k"
|
||||
}).then((res) => {
|
||||
console.log("Image generation response:", res);
|
||||
}).catch((err) => {
|
||||
console.error("Error generating image:", err);
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
import { encryptAES, decryptAES } from '../..//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);
|
||||
@@ -1,35 +0,0 @@
|
||||
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": "计算a,b,c算法的值,a=1,b=2,c=3",
|
||||
"name": "compouted",
|
||||
"parameters": {},
|
||||
"strict": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
@@ -1,116 +0,0 @@
|
||||
import { SiliconFlow } from '../../provider/chat-adapter/siliconflow.ts';
|
||||
import { Ollama } from '../../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);
|
||||
@@ -1,26 +0,0 @@
|
||||
import { ModelScope } from '../../provider/chat-adapter/model-scope.ts';
|
||||
import { logger } from '../../modules/logger.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();
|
||||
logger.info('test', res);
|
||||
};
|
||||
|
||||
main();
|
||||
const mainChat = async () => {
|
||||
const res = await chat.chat(chatMessage as any);
|
||||
logger.info('chat', res);
|
||||
};
|
||||
|
||||
// mainChat();
|
||||
@@ -1,37 +0,0 @@
|
||||
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();
|
||||
@@ -1,86 +0,0 @@
|
||||
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();
|
||||
@@ -1,7 +0,0 @@
|
||||
import { ProviderManager } from '../..//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);
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* 尝试从字符串中提取JSON对象
|
||||
*/
|
||||
export const getJsonFromString = (str: string) => {
|
||||
try {
|
||||
const jsonMatch = str.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
if (jsonMatch && jsonMatch[1]) {
|
||||
return JSON.parse(jsonMatch[1]);
|
||||
}
|
||||
} catch (error) {}
|
||||
return null;
|
||||
};
|
||||
Reference in New Issue
Block a user