2025-02-20 23:20:06
在 C# 中,SemaphoreSlim
是 System.Threading
命名空间下的轻量级同步原语,用于限制同时访问共享资源的线程数量。
轻量高效
专为高性能设计,适用于进程内同步,比 Semaphore
类更高效。
异步支持
提供 WaitAsync()
方法,支持异步编程模型,避免线程阻塞。
并发控制
通过计数器限制资源访问线程数,初始计数(initialCount
)表示可用资源数,最大计数(maxCount
)为资源上限。
SemaphoreSlim(int initialCount); // 初始计数,最大计数默认为 int.MaxValue
SemaphoreSlim(int initialCount, int maxCount); // 指定初始和最大计数
说明:initialCount
必须 ≥0 且 ≤ maxCount
,否则抛出 ArgumentOutOfRangeException
。
方法 |
说明 |
---|---|
|
同步等待信号量,计数器减1;若计数为0则阻塞。 |
|
异步等待信号量,适用于非阻塞异步场景。 |
|
释放信号量,计数器加1(默认释放1次,可指定次数 |
|
释放资源,避免内存泄漏。 |
资源池管理
如数据库连接池、文件句柄池等,限制并发访问数量。
异步任务协调
控制异步任务并发度,避免资源过载。
生产者-消费者模型
同步生产者和消费者的资源访问节奏。
static SemaphoreSlim semaphore = new SemaphoreSlim(3);
static void AccessResource(string name, int seconds)
{
semaphore.Wait();
try
{
Console.WriteLine($"{name} 访问资源");
Thread.Sleep(seconds * 1000);
}
finally
{
semaphore.Release();
}
}
static SemaphoreSlim semaphore = new SemaphoreSlim(1,1);
static async Task AccessResourceAsync(string name, int seconds)
{
await semaphore.WaitAsync();
try
{
Console.WriteLine($"{name} 开始异步操作");
await Task.Delay(seconds * 1000);
}
finally
{
semaphore.Release();
}
}
初始计数为0:需手动调用Release(n)
初始化资源池,否则所有线程会被阻塞。
线程安全:Dispose()
方法非线程安全,需确保释放时无其他操作。
与Semaphore区别:Semaphore
支持跨进程和命名信号量,而SemaphoreSlim
仅限进程内使用但性能更优。
2025-02-20 10:39:06
不能实例化:抽象类不能被实例化,它通常作为基类存在,为子类提供一套通用的接口和部分实现。
包含实现:抽象类可以包含具体的方法实现和抽象方法。抽象方法必须在子类中被重写。
单继承:一个类只能继承自一个抽象类(C#中不支持多重继承)。
当你希望提供一个通用的基类,该基类定义了一些子类共有的方法实现,并且还有一些方法需要由子类提供具体实现时,使用抽象类是一个不错的选择。
namespace App01
{
// 抽象的图形基类
public abstract class Shape
{
// 抽象方法:计算面积
public abstract double Area();
// 具体实现的方法:显示形状信息
public void Display()
{
Console.WriteLine("This is a shape.");
}
}
// 圆形类,继承自Shape
publicclass Circle : Shape
{
publicdouble Radius { get; set; }
// 重写抽象方法:计算圆的面积
public override double Area()
{
return Math.PI * Radius * Radius;
}
}
// 矩形类,继承自Shape
publicclass Rectangle : Shape
{
publicdouble Width { get; set; }
publicdouble Height { get; set; }
// 重写抽象方法:计算矩形的面积
public override double Area()
{
return Width * Height;
}
}
internal class Program
{
static void Main(string[] args)
{
Circle circle = new Circle();
circle.Radius = 5;
Console.WriteLine("The area of the circle is {0}.", circle.Area());
Rectangle rectangle = new Rectangle();
rectangle.Width = 4;
rectangle.Height = 6;
Console.WriteLine("The area of the rectangle is {0}.", rectangle.Area());
}
}
}
说明:
Shape
类是一个抽象类,包含一个抽象方法Area()
和一个具体方法Display()
。
Circle
和Rectangle
类继承自Shape
,并实现了Area()
方法。
namespace App02
{
// 抽象的动物基类
public abstract class Animal
{
// 抽象方法:发出叫声
public abstract void MakeSound();
// 具体方法:共同的行为
public void Sleep()
{
Console.WriteLine("The animal is sleeping.");
}
}
// 狗类,继承自Animal
publicclass Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog barks: Woof!");
}
}
// 猫类,继承自Animal
publicclass Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Cat meows: Meow!");
}
}
internal class Program
{
static void Main(string[] args)
{
// 创建Dog对象
Dog dog = new Dog();
// 调用Dog的共同行为
dog.Sleep();
// 调用Dog的叫声
dog.MakeSound();
// 创建Cat对象
Cat cat = new Cat();
// 调用Cat的共同行为
cat.Sleep();
// 调用Cat的叫声
cat.MakeSound();
}
}
}
说明:
Animal
类定义了共有的行为Sleep()
,并要求子类实现MakeSound()
方法。
完全抽象:接口只能包含方法、属性、事件、索引器的声明,不能包含任何实现。
多实现:一个类可以实现多个接口,实现接口即需要实现其所有成员。
成员默认是公共的:接口成员默认是公共的,不能包含访问修饰符。
当你希望定义一组不相关类之间的通用行为契约,并且不涉及实现细节时,接口是最好的选择。
namespace App01
{
// 可绘制的接口
public interface IDrawable
{
void Draw();
}
// 可变形的接口
public interface ITransformable
{
void Rotate(double angle);
void Scale(double factor);
}
// 实现了IDrawable和ITransformable的形状类
publicclass TransformableShape : IDrawable, ITransformable
{
public void Draw()
{
Console.WriteLine("Drawing the shape.");
}
public void Rotate(double angle)
{
Console.WriteLine($"Rotating the shape by {angle} degrees.");
}
public void Scale(double factor)
{
Console.WriteLine($"Scaling the shape by a factor of {factor}.");
}
}
internal class Program
{
static void Main(string[] args)
{
TransformableShape shape = new TransformableShape();
shape.Draw();
shape.Rotate(45);
shape.Scale(2.0);
Console.ReadKey();
}
}
}
说明:
IDrawable
和ITransformable
是两个接口,定义了绘制和变形的行为。
TransformableShape
类实现了这两个接口,必须提供所有方法的实现。
namespace App02
{
// 数据存储接口
public interface IDataStore
{
void Save(string data);
string Load();
}
// 本地文件存储类
publicclass FileDataStore : IDataStore
{
public void Save(string data)
{
Console.WriteLine("Saving data to file.");
// 实际的文件保存逻辑
}
public string Load()
{
Console.WriteLine("Loading data from file.");
// 实际的文件加载逻辑
return"Data from file";
}
}
// 云端存储类
publicclass CloudDataStore : IDataStore
{
public void Save(string data)
{
Console.WriteLine("Saving data to cloud.");
// 实际的云保存逻辑
}
public string Load()
{
Console.WriteLine("Loading data from cloud.");
// 实际的云加载逻辑
return"Data from cloud";
}
}
internal class Program
{
static void Main(string[] args)
{
// 选择存储方式
IDataStore dataStore = new FileDataStore();
// 保存数据
dataStore.Save("Some data");
// 加载数据
string loadedData = dataStore.Load();
Console.WriteLine("Loaded data: " + loadedData);
}
}
}
有时,我们可以将抽象类和接口结合起来使用,以充分利用它们的优势。
namespace App03
{
// 抽象的动物类
public abstract class Animal
{
public abstract void Eat();
public void Breathe()
{
Console.WriteLine("Animal breathes.");
}
}
// 可移动的接口
public interface IMovable
{
void Move();
}
// 可飞行的接口
public interface IFlyable
{
void Fly();
}
// 狗类,继承自Animal并实现IMovable接口
publicclass Dog : Animal, IMovable
{
public override void Eat()
{
Console.WriteLine("Dog eats.");
}
public void Move()
{
Console.WriteLine("Dog runs.");
}
}
// 鸟类,继承自Animal并实现IMovable和IFlyable接口
publicclass Bird : Animal, IMovable, IFlyable
{
public override void Eat()
{
Console.WriteLine("Bird eats.");
}
public void Move()
{
Console.WriteLine("Bird walks.");
}
public void Fly()
{
Console.WriteLine("Bird flies.");
}
}
internal class Program
{
static void Main(string[] args)
{
// 创建Dog对象并调用方法
Dog dog = new Dog();
dog.Eat();
dog.Breathe();
dog.Move();
// 创建Bird对象并调用方法
Bird bird = new Bird();
bird.Eat();
bird.Breathe();
bird.Move();
bird.Fly();
Console.ReadKey();
}
}
}
说明:
Animal
是一个抽象类,定义了所有动物的共有行为。
IMovable
和IFlyable
是接口,定义了可移动和可飞行的行为。
Dog
类继承自Animal
并实现了IMovable
接口。
Bird
类继承自Animal并实现了IMovable
和IFlyable
接口。
特性 |
抽象类 |
接口 |
---|---|---|
实例化 |
❌ 不能实例化 |
❌ 不能实例化 |
实现内容 |
✅ 可包含具体方法和抽象方法 |
❌ 仅声明成员,无实现 |
继承/实现数量 |
单继承(一个子类只能继承一个父类) |
多实现(一个类可实现多个接口) |
成员访问修饰符 |
支持(如 |
成员默认 |
字段/属性 |
✅ 可定义字段、属性 |
❌ 只能声明属性(无字段) |
设计目的 |
提供代码复用和部分通用逻辑 |
定义行为契约,实现多态 |
结合优势:抽象类提供基础实现,接口扩展额外功能。
示例:
Animal
抽象类定义Eat()
和Breathe()
。
IMovable
和IFlyable
接口分别定义移动和飞行能力。
Dog
继承Animal
并实现IMovable
。
Bird
继承Animal
并实现IMovable
和IFlyable
。
抽象类:聚焦于代码复用和层次化设计,适合“是什么”(Is-A)关系。
接口:聚焦于行为契约和功能扩展,适合“能做什么”(Can-Do)关系。
组合使用:通过抽象类提供基础能力,接口扩展多样化功能,实现灵活设计。
2025-02-16 22:56:50
近期我完成了一个项目,该项目需求是在连接了双显示器的设备上,对屏幕显示设置的更改进行监听。具体而言,当显示设置调整为 “复制这些显示器” 时,程序要自动隐藏;而当显示设置变为 “扩展这些显示器” 时,程序则需显示在非主显示器的第二显示器上。接下来,我会对此次项目中的业务逻辑进行简单记录,希望能对你有所助益。
启动程序时通过调用ShowInSecondScreen2()
方法,将窗口移动至非主显示器上面。
private double left = 0;
private Rectangle primaryRect;
private Rectangle secondRect;
private double top = 0;
private void ShowInSecondScreen2()
{
this.Dispatcher.Invoke(new Action(() =>
{
this.WindowState = WindowState.Normal;
//获取所有显示器的信息
Screen[] screens = Screen.AllScreens;
//确定主显示器的分辨率,用于计算副显示器的起始位置
primaryRect = Screen.PrimaryScreen.Bounds;
//将窗口放置在第一个找到的副显示器上
bool placed = false;
foreach (Screen screen in screens)
{
if (screen.Primary == false) //找到的是非主显示器
{
Rectangle workingArea = screen.Bounds;
//设置窗口位置,使其在副显示器的左上角显示
left = this.Left = workingArea.Left;
top = this.Top = workingArea.Top;
this.Width = workingArea.Width;
this.Height = workingArea.Height;
secondRect = workingArea;
placed = true;
break;
}
}
if (!placed)
{
// 如果没有找到副显示器,就在主显示器上显示
this.Left = primaryRect.Left;
this.Top = primaryRect.Top;
this.Width = secondRect.Width;
this.Height = secondRect.Height;
}
this.Activate();
}));
}
注册SystemEvents.DisplaySettingsChanged
事件,用于监听屏幕显示设置的更改。
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = (System.Windows.Application.Current.Resources["Locator"] as ViewModelLocator).Main;
ShowInSecondScreen2();
Microsoft.Win32.SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
}
编写SystemEvents_DisplaySettingsChanged
事件方法,对单/双屏幕设置来控制窗口的显示位置。
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
try
{
var screens = System.Windows.Forms.Screen.AllScreens;
if (screens.Length >= 2)
{
this.Show();
this.Dispatcher.Invoke(new Action(() =>
{
this.Left = left;
this.Top = top;
this.Width = secondRect.Width;
this.Height = secondRect.Height;
this.Activate();
}));
}
else
{
this.Hide();
}
}
catch (Exception exp)
{
}
}
以上代码会受到系统缩放百分比影响,建议禁用DPI感知。在AssemblyInfo.cs
中,写入禁用DPI感知代码。
//禁用DPI感知
[assembly: System.Windows.Media.DisableDpiAwareness]
DPI感知模式影响数据获取
默认情况下,未声明DPI感知的应用程序会被Windows自动进行位图缩放,导致Screen.Bounds
返回逻辑分辨率而非物理分辨率。
当主副显示器缩放比例不同时,Screen
类可能仅返回主显示器缩放后的逻辑分辨率,而副显示器的物理分辨率无法正确识别。
Screen类的局限性
Screen.AllScreens
的Bounds
属性返回的是系统缩放后的逻辑分辨率,而非实际物理分辨率。
在多显示器不同缩放比例场景下,未正确设置DPI感知的应用程序可能无法区分各显示器的实际分辨率。
2025-02-14 10:31:58
在近期项目中,我使用了 WPF 原生的 MediaElement 媒体控件来播放视频。然而,当我的笔记本连接外接显示屏时,程序中 MediaElement 控件播放视频会出现短暂卡顿,尤其是在每次实例化该控件并自动播放视频时。而当我断开外接显示屏后,MediaElement 控件则能正常播放视频。经过仔细研究,我发现笔记本在外接显示屏的情况下运行该程序时,会自动调用 GPU 加速。由此我得出结论:
在 WPF 中使用 MediaElement 控件时,其自动调用 GPU 加速是导致视频卡顿的原因。
在需要禁用硬件加速的页面中,可以在App.xaml.cs
的OnStartup
方法中设置RenderOptions.ProcessMode
属性为SoftwareOnly
。这样可以确保MediaElement
在停止播放后不再出现卡顿现象。但需要注意的是,禁用硬件加速可能会导致播放时仍然出现卡顿,尤其是在高分辨率(如8K)或复杂场景下,WPF默认的硬件加速可能成为性能瓶颈。可以通过以下代码在特定页面禁用硬件加速:
RenderOptions.ProcessRenderMode = System.Windows.Interop.RenderMode.SoftwareOnly;
并在离开页面后重新启用硬件加速以恢复其他功能的性能。此方法能显著缓解UI卡顿,但可能影响播放时的流畅度。
将AllowsTransparency属性设置为False,避免透明层叠加带来的额外渲染开销:
<Window AllowsTransparency="False">
2025-02-11 18:49:03
维度 |
传统知识库 |
具有AI的知识库 |
---|---|---|
数据存储方式 |
结构化数据为主(表格、文档),依赖手动分类和标签 |
支持非结构化数据(文本、图片、音视频),利用嵌入技术(Embedding)自动编码为向量存储 |
检索机制 |
基于关键词匹配或固定规则(如SQL查询) |
支持语义搜索,通过向量相似度匹配理解用户意图(如“性价比高的手机”≈“低价高性能手机”) |
交互方式 |
用户需输入精确关键词,返回静态结果 |
支持自然语言对话(如提问“如何解决打印机卡纸?”),生成动态答案或分步骤指导 |
知识更新 |
依赖人工录入和定期维护,更新滞后 |
支持自动学习新数据(如爬取最新行业报告),部分系统可结合RAG(检索增强生成)实时整合外部知识 |
推理能力 |
无自主推理能力,仅提供已有信息 |
通过LLM(大语言模型)进行逻辑推理、总结归纳(如分析故障原因并推荐解决方案) |
应用场景 |
企业文档管理、FAQ库等静态场景 |
智能客服、个性化推荐、自动化报告生成等动态场景 |
相比传统的知识库,AI知识库具有更高的智能化程度。它不仅能够理解用户的查询意图,还能根据用户的历史行为和偏好进行个性化推荐。此外,AI大模型知识库还具备知识推理、问答生成等高级功能,能够为用户提供更加智能、个性化的知识服务。这使得AI大模型知识库在教育、医疗、金融、客服等多个领域具有广泛的应用前景。
云端AI知识库可能涉及敏感数据泄露风险,因此企业更倾向本地化部署以保障数据安全。为帮助用户实现这一目标,本教程将基于Windows系统,通过Ollama(本地模型管理) + DeepSeek(开源中文模型) + AnythingLLM(私有知识库框架) 的组合方案,逐步演示如何构建安全可控的本地AI知识库。
Ollama 是一个开源的本地大语言模型运行框架。
核心功能:Ollama 专注于在本地机器上便捷部署和运行大型语言模型(LLM),支持多种操作系统,包括 macOS、Windows、Linux 以及通过 Docker 容器运行。
主要特点:它提供对模型量化的支持,可以显著降低显存要求,使得在普通家用计算机上运行大型模型成为可能。
多种预训练语言模型支持:Ollama 提供了多种开箱即用的预训练模型,包括常见的 GPT、BERT 等大型语言模型,用户可以轻松加载并使用这些模型进行文本生成、情感分析、问答等任务。
易于集成和使用:Ollama 提供了命令行工具(CLI)和 Python SDK,简化了与其他项目和服务的集成,开发者无需担心复杂的依赖或配置,可以快速将 Ollama 集成到现有的应用中。
本地部署与离线使用:Ollama 允许开发者在本地计算环境中运行模型,这意味着可以脱离对外部服务器的依赖,保证数据隐私,并且对于高并发的请求,离线部署能提供更低的延迟和更高的可控性。
支持模型微调与自定义:用户不仅可以使用 Ollama 提供的预训练模型,还可以在此基础上进行模型微调,根据自己的特定需求,开发者可以使用自己收集的数据对模型进行再训练,从而优化模型的性能和准确度。
性能优化:Ollama 关注性能,提供了高效的推理机制,支持批量处理,能够有效管理内存和计算资源,这让它在处理大规模数据时依然保持高效。
跨平台支持:Ollama 支持在多个操作系统上运行,包括 Windows、macOS 和 Linux,这样无论是开发者在本地环境调试,还是企业在生产环境部署,都能得到一致的体验。
开放源码与社区支持:Ollama 是一个开源项目,这意味着开发者可以查看源代码,进行修改和优化,也可以参与到项目的贡献中,此外,Ollama 有一个活跃的社区,开发者可以从中获取帮助并与其他人交流经验。
本地模型管理:Ollama 支持从官方模型库或自定义模型库拉取预训练模型,并在本地保存和加载,它支持各种流行的模型格式(如 ONNX、PyTorch、TensorFlow)。
高效推理:通过 GPU/CPU 的加速,Ollama 提供高效的模型推理,适合本地化应用或需要控制数据隐私的场景。
多种接口访问:Ollama 支持命令行(CLI)、HTTP 接口访问推理服务,并通过 OpenAI 客户端实现更广泛的集成。
环境变量配置:通过灵活的环境变量,用户可以自定义推理设备(GPU/CPU)、缓存路径、并发数、日志级别等。
DeepSeek 是一款开源的大语言模型。
智能化:DeepSeek 能够理解复杂的问题,并提供精准的解决方案。它通过深度学习和自然语言处理技术,能够理解用户的需求并提供个性化的建议。
多功能性:DeepSeek 在多个领域都有广泛的应用,包括学习、工作和生活。它可以用作学习助手、编程助手、写作助手、生活助手和翻译助手等,满足用户在不同场景下的需求。
易用性:DeepSeek 通过自然语言交互,用户无需学习复杂的操作即可与模型进行对话。这种交互方式使得用户能够轻松地获取所需的信息和服务。
低成本:DeepSeek 的训练和推理成本较低,打破了传统 N 卡垄断,降低了大模型的使用门槛。这使得更多的企业和个人能够使用高性能的 AI 服务。
高效率:DeepSeek 在推理能力和响应速度上表现出色,能够快速处理复杂的查询和任务,提供准确的答案和解决方案。
开源生态:DeepSeek 采用了开源策略,吸引了大量开发者和研究人员的参与,推动了 AI 技术的发展和应用。
深度学习:DeepSeek 通过大量的数据训练,学会了如何理解和处理复杂的问题,提供个性化的建议和解决方案。
自然语言处理(NLP):DeepSeek 能够理解人类的语言,无论是中文、英文还是其他语言,支持自然方式的对话。
知识图谱:DeepSeek 存储了大量的结构化知识,能够快速找到相关信息,提供精准的答案。
混合专家模型(MoE):DeepSeek 采用了 MoE 框架,通过训练多个专家模型,并根据输入数据的特征动态选择最合适的专家模型进行处理,从而实现对复杂任务的高效处理。
多头潜在注意力机制(MLA):DeepSeek 的 MLA 技术显著降低了模型推理成本,通过减少对 KV 矩阵的重复计算,提高了模型的运行效率。
大规模强化学习:DeepSeek 通过大规模强化学习技术,增强了模型的推理能力和泛化能力,能够在多个领域中表现出色。
AnythingLLM 是一个全栈应用程序,允许用户使用商业现成的 LLM(大语言模型)或流行的开源 LLM 以及向量数据库解决方案,构建一个无需妥协的本地 ChatGPT。用户可以通过它与提供给它的任何文档进行智能交流,新颖的设计使得用户能够选择想要使用的 LLM 或向量数据库,并支持多用户管理和权限设置。
多用户支持和权限管理:允许多个用户同时使用,并可设置不同的权限。
支持多种文档类型:包括 PDF、TXT、DOCX 等。
简易的文档管理界面:通过用户界面管理向量数据库中的文档。
两种聊天模式:对话模式保留之前的问题和回答,查询模式则是简单的针对文档的问答。
聊天中的引用标注:链接到原始文档源和文本。
简单的技术栈:便于快速迭代。
100% 云部署就绪:适合云部署。
“自带 LLM”模式:可以选择使用商业或开源的 LLM。
高效的成本节约措施:对于大型文档,只需嵌入一次,比其他文档聊天机器人解决方案节省 90% 的成本。
完整的开发者 API:支持自定义集成。
自定义 AI 代理:用户可以根据需求创建自己的 AI 代理,使应用更具个性化。
支持多模态:不仅支持闭源 LLM,还兼容开源 LLM,拓展了应用的灵活性。
工作区内的代理:支持在工作区内浏览网页、运行代码等操作。
自定义可嵌入聊天小部件:可以嵌入到用户的网站。
前往 Ollama 官方网站(https://ollama.com/download)下载安装包。
直接通过安装包安装Ollama会直接安装在C盘,如果需要自定义安装路径,需要通过命令行指定安装路径,启动安装程序,点击 Install 后,Ollama 就会安装到指定的目录了。
OllamaSetup.exe /DIR=E:\MySoftware\Ollama
打开一个新的命令行,输入ollama
回车执行,如果返回以下内容,则表示安装成功。
在浏览器中输入127.0.0.1:11434
,显示Ollama is running
表示Ollama
已经成功运行。
更改模型存储位置。大模型资源包默认下载到 C 盘,可以手动创建大模型存储目录,然后在用户账户中设置环境变量OLLAMA_MODELS
,将其设置为希望存储模型的路径。
需要重启Ollama才能生效!
拉取模型到本地。在命令行输入ollama pull deepseek-r1:32b
。
根据本机配置和业务需求进行选择模型参数。
Models
Configuration
Command
DeepSeek-R1-1.5B
CPU :最低 4 核,推荐 Intel/AMD 多核处理器。
内存 :8GB +。
硬盘 :3GB + 存储空间,模型文件约 1.5-2GB。
显卡 :非必需,纯 CPU 推理即可,若 GPU 加速可选 4GB + 显存,如 GTX 1650。ollama pull deepseek-r1:1.5b
DeepSeek-R1-7B
CPU :8 核以上,推荐现代多核 CPU。
内存 :16GB +。
硬盘 :8GB +,模型文件约 4-5GB。
显卡 :推荐 8GB + 显存,如 RTX 3070/4060。ollama pull deepseek-r1:7b
DeepSeek-R1-8B
CPU :8 核以上,推荐现代多核 CPU。
内存 :16GB +。
硬盘 :8GB +,模型文件约 4-5GB。
显卡 :推荐 8GB + 显存,如 RTX 3070/4060。ollama pull deepseek-r1:8b
DeepSeek-R1-14B
CPU :12 核以上。
内存 :32GB +。
硬盘 :15GB +。
显卡 :16GB + 显存,如 RTX 4090 或 A5000。ollama pull deepseek-r1:14b
DeepSeek-R1-32B
CPU :16 核以上,如 AMD Ryzen 9 或 Intel i9。
内存 :64GB +。
硬盘 :30GB +。
显卡 :24GB + 显存,如 A100 40GB 或双卡 RTX 3090。ollama pull deepseek-r1:32b
DeepSeek-R1-70B
CPU :32 核以上,服务器级 CPU。
内存 :128GB +。
硬盘 :70GB +。
显卡 :多卡并行,如 2x A100 80GB 或 4x RTX 4090。ollama pull deepseek-r1:70b
DeepSeek-R1-671B
CPU :64 核以上,服务器集群。
内存 :512GB +。
硬盘 :300GB +。
显卡 :多节点分布式训练,如 8x A100/H100。ollama pull deepseek-r1:671b
等待模型拉取到本地。
拉取嵌入模型nomic-embed-text
。
ollama pull nomic-embed-text
前往 AnythingLLM 官方网站(https://anythingllm.com/desktop)下载安装包。
配置LLM首选项。平台选择Ollama
,模型选择我们刚拉取的模型deepseek-r1:32b
,其他保持默认,然后点击“Save changes”进行保存。
配置嵌入模型。平台选择Ollama
,模型选择nomic-embed-text:latest
,其他的保持默认,然后点击“Save changes”进行保存。
如果对英文不友好,可以在 Customization 中将 Display Language 设置成Chinese
。
创建工作区。给工作区设置一个名称,然后保存。
上传知识库文件。点击工作区名称旁边的上传图标,进入文件上传管理。
选择知识库文件上传。
将知识库文件移动到当前工作区。
保存并嵌入知识库文件。
在工作区中与大模型进行对话。
在使用 DeepSeek-R1 模型的过程中,我发现当参数在 32B 及以下时,模型在处理复杂推理任务时的表现似乎不太理想,感觉有些力不从心。具体来说,它会出现中英文混合输出的情况,这在一定程度上影响了结果的准确性和可读性。
另外,就 AnythingLLM 框架而言,其检索能力也存在一些不足之处。有时候我提出的问题,明明在知识库中是存在相关知识的,但系统却提示没有找到相关知识,这说明框架的检索功能还有待进一步优化和提升,以便能更精准地定位和提供知识库中的有效信息。
2025-01-18 22:46:06
本教程基于群晖的NAS设备DS423+的docker功能进行搭建,DSM版本为 DSM 7.2.2-72806 Update 2。
n.eko 支持多种类型浏览器在其虚拟环境中运行,本次教程使用 Chromium
浏览器镜像进行演示,支持访问内网设备和公网地址。
n.eko 是一款基于 Docker 的自托管虚拟浏览器,利用 WebRTC 技术实现实时音视频传输和多人协作功能。它允许用户在虚拟环境中运行功能齐全的浏览器(如 Firefox、Chrome 等),并支持多人同时访问和操作,适用于远程协作、观看派对、互动演示等场景。
虚拟浏览器:
支持多种浏览器内核(如 Firefox、Chrome、Opera 等),用户可以在虚拟环境中浏览网页、运行应用程序。
所有操作都在 Docker 容器中完成,确保安全性和隐私性。
多人协作:
支持多用户同时访问,用户可以共享浏览器画面并实时互动,适合团队协作、远程教学或家庭娱乐。
提供聊天功能、文件传输和剪贴板同步,增强协作体验。
实时音视频传输:
基于 WebRTC 技术,实现低延迟的音视频传输,支持屏幕共享和远程控制。
支持 RTMP 推流,可将内容广播到 Twitch 或 YouTube 等平台。
隐私与安全:
所有操作都在隔离的 Docker 容器中进行,避免数据泄露。
支持管理员权限控制,如踢出用户、锁定房间等。
灵活部署:
通过 Docker 部署,支持多种操作系统(Windows、Linux、macOS)。
提供丰富的配置选项,如分辨率、密码设置、文件传输路径等。
远程协作:团队成员可以共同浏览网页、调试代码或进行头脑风暴。
观看派对:与朋友或家人一起观看视频、动漫,并实时聊天互动。
教育培训:用于远程教学或演示,支持多人同时操作和互动。
隐私浏览:在隔离环境中访问敏感网站,避免留下痕迹。
在群晖NAS上面的“File Station”中新建一个docker映射文件,用于映射docker中neko-chromium的数据。
打开“Container Manager”,在“项目”中,点击“新增”。填写项目名称,路径选择创建好的映射文件夹,文件选择“创建 docker-compose.yml”,然后将以下配置代码复制粘贴进去。
version: "3.8"
services:
neko:
image: "m1k1o/neko:chromium"
container_name: "neko-chromium"
restart: "unless-stopped"
shm_size: "3gb" # 设置共享内存大小为 3GB,此设置为必须。
ports:
- "19800:8080"
- "52000-52100:52000-52100/udp"
cap_add:
- SYS_ADMIN # 使用 Chromium 内核时需添加,以获取必要的系统管理权限。
volumes:
- ./chromium/data:/home/neko/.config/chromium # 策略文件,重启依然能保留浏览器数据。
environment:
NEKO_SCREEN: 1280x720@30 # 自定义浏览器窗口分辨率。
NEKO_PASSWORD: neko # 普通用户的登录密码。
NEKO_PASSWORD_ADMIN: admin # 管理员(admin)用户的登录密码。
NEKO_EPR: 52000-52100 # 设置 WebRTC 的 UDP 端口范围,用于P2P连接。
NEKO_ICELITE: true # 启用 Ice Lite 协议以优化连接性能,可选。
NEKO_CONTROL_PROTECTION: true # 控制保护意味着,只有当至少有一个管理员在房间里时,用户才能获得控制权。
NEKO_NAT1TO1: 192.168.1.111 # 局域网使用时设置为服务器本地 IP,公网则自动获取公网 IP,可选。
最后点击“下一步”,等待镜像拉取和容器创建完成。
输入IP:Port
访问,使用管理员登录,用户名为登录后显示的名称,可自定义,密码根据之前配置进行填写。
设置中文语言。默认是英文,点击左下角en
,选择cn
切换至中文。
获取浏览器控制权。点击正下方的键盘图标,获取控制权。
调整屏幕尺寸。点击右上角显示器图标,进行分辨率切换。
对普通用户进行操作(需要管理员账号)。选中对应头像,鼠标右键单击,可对其进行选择“给予控制”或“踢出”等操作。
聊天室。点击右上角侧栏图标,然后点击聊天。
粘贴板共享。在右下角有个粘贴板图标,如果需要将文字内容复制进浏览器,需要将内容粘贴至粘贴板内。
仅支持纯文本。
使用自动加入链接。
示例:http(s)://[URL:Port](URL:Port)/?pwd=neko&usr=guest&cast=1
添加?pwd=<password>将预填充密码。
添加?usr=<display-name>将预填充用户名。
添加?cast=1将隐藏所有控件,只显示视频。
添加?embed=1将隐藏大多数附加组件,仅显示视频
添加?volume=<0-1>将音量设置为给定值。
添加?lang=<language>将语言设置为给定值。
添加?show_side=1将在启动时显示侧边栏。
添加?mute_chat=1将在启动时静音聊天。
更多使用教程,请参考官方文档 n.keo Doc(https://neko.m1k1o.net/#/getting-started/)