敬意,%USERNAME%。我承认我真的很喜欢旧的 ThinkPad 笔记本电脑。在那些日子里,当该品牌归 IBM 所有时,这些设备因其周到和功能而受到钦佩。它们的价格很尖锐,但您可以肯定地知道,在严格的设计背后,有强大的硬件和出色的工程解决方案。值得一提的是 ThinkLight 键盘的背光(我在 R61i 上熟悉它),它让您可以在火车或飞机上舒适地工作,而不会通过打开灯给其他乘客带来压力。好吧,我仍然更喜欢跟踪点,而不是任何很酷的触摸板。
作系统和技术的进步已经将旧的 ThinkPad 抛在了后面。我的 X41 平板电脑的合理限制是 Windows Vista。它具有所有设备驱动程序,并且能够运行为 Windows XP 编写的大多数游戏和应用程序。但在当今环境中,当网络浏览器已成为用户的主要工具时,该系统已无可救药地过时了。
然后我想出了一个疯狂的想法:如果我们赋予这样的笔记本电脑第二次生命,并赋予它与现代神经网络一起工作的能力呢?当然,没有足够的资源进行全面的推理,但为什么不编写一个简单的客户端来与 Ollama 交互呢。结果,我将得到一台笔记本电脑,它让我能够与神经网络通信,并给我再次使用它的乐趣。这次冒险的结果,我接下来会告诉你。
为较旧的作系统进行开发始终是一个限制。它们将在每一步等待您:从无法安装熟悉库的最新版本到 Windows Vista 中缺少 TLS 1.2 等协议的问题。您必须在规划阶段考虑到这一点。
与 Ollama 服务器本身通信并不困难。后者提供了一个本地 HTTP API,网址为:http://[server_ip]:11434/api/generate。您需要向其发送提示 POST 请求,服务器将以 JSON 格式返回结果。需要对其进行解析并显示给用户。
HTTPS 不是,所以少了一个麻烦。我的 Ollama 服务器在我的本地网络上运行,因此我可以使用最常见的 HTTP 协议。事实证明,应用程序必须能够发送 HTTP 请求并使用 JSON。现在值得考虑会出现什么问题。
来自神经网络的 Haiku
Python + Tkinter
这似乎是最明显的选择。通过 requests(或 urllib)发送请求,通过标准库解析 JSON,使用 Tkinter 绘制接口,并通过 cx_Freeze 构建 exe。但是这里一下子出现了很多问题:
-
Vista 的 Python 最新版本是什么?
-
pip 的包管理器会工作吗?
-
哪些版本的库将正常工作?
在快速谷歌搜索的过程中,发现对于 Windows Vista,Python 的最新版本是 3.4.4,于 2015 年 12 月发布。不错,但 pip 已经过时了,将无法再在线下载软件包。您将不得不单独搜索并手动安装。即使您忘记了请求并通过 urllib 完成所有作,那么至少需要安装 cx_Freeze。
我设法找到了 ,我必须手动构建。这并不难,但首先你需要安装 Microsoft Visual C++ 2010 Express,没有它,这个东西根本无法工作。现在无法正式下载它 — Microsoft 网站上有一个存根。所以我不得不通过 Internet Archive 以迂回的方式获取它。
安装 Microsoft Visual C++ 2010 Express 后,我让计算机重新启动,然后构建cx_Freeze 5.0.2:
python setup.py build
然后我在系统中安装了:
python setup.py install
现在,我快速草拟了代码 (app.py):
import tkinter as tk
import urllib.request
import json
OLLAMA_API_URL = "http://[server_ip]:11434/api/generate"
def send_prompt():
prompt = prompt_entry.get("1.0", tk.END).strip()
if not prompt:
return
response_text.set("Ожидание ответа...")
try:
data = json.dumps({
"model": "deepseek-r1:14b",
"prompt": prompt,
"stream": False
}).encode("utf-8")
req = urllib.request.Request(
OLLAMA_API_URL,
data=data,
headers={"Content-Type": "application/json"}
)
with urllib.request.urlopen(req) as response:
result = response.read().decode("utf-8")
result_json = json.loads(result)
content = result_json.get("response", "Нет текста в ответе.")
response_text.set(content)
except Exception as e:
response_text.set("Ошибка: " + str(e))
root = tk.Tk()
root.title("Ollama Клиент (Vista)")
tk.Label(root, text="Введите запрос:").pack(pady=5)
prompt_entry = tk.Text(root, height=5, width=50)
prompt_entry.pack(pady=5)
tk.Button(root, text="Отправить", command=send_prompt).pack(pady=5)
response_text = tk.StringVar()
tk.Label(root, textvariable=response_text, wraplength=400, justify="left").pack(pady=10)
root.mainloop()
他跳起来,试图打个招呼:
这只不过是一个表情符号。Python 3.4.4 中内置的 Tkinter 并不完全支持 Unicode,只允许您显示代码不高于 U+FFFF 的字符。这意味着在将响应传递给 StringVar 之前,您必须从响应中完全删除此类“危险”片段。我更改了这一行:
response_text.set(content)
替换为以下内容('' 是两个单引号):
safe_content = ''.join(c for c in content if ord(c) <= 0xFFFF)
response_text.set(safe_content)
我再试一次:

工作
总的来说,这已经是一场胜利了。请求被正确形成,并收到正确的答案。很明显,你需要添加 query 字段的清空,将 **** 标签框定的推理分开,让应用程序更美观。
由于现在请求不是异步发送的,因此程序会停止响应,直到它从服务器收到 JSON。但即便如此,这个概念仍然有效并且有权使用。使用以下代码,可以轻松地将完成的应用程序构建到 exe 中:
from cx_Freeze import setup, Executable
setup(
name="OllamaVistaClient",
version="0.1",
description="Простой GUI-клиент для Ollama",
executables=[Executable("app.py")],
)
我把它放在它旁边,叫它 setup.py 并组装它:
python setup.py build
结果是如此美丽:
即用型应用程序文件
附言。直到这时,我才注意到系统中指示的年份是错误的:2026 年而不是 2025 年。事实证明,这是一次通往未来的旅程
德尔福 7 号
虽然 Python 相对简单,但 Delphi 7 带来了很多惊喜。我既喜欢又讨厌这种开发环境。一方面,它非常适合编写简单的实用程序。但另一方面,它对代码更加严格,有时您需要绞尽脑汁,寻找问题的原因。尽管如此,我在大学时就很了解她,所以我决定记住我的青春。
尽管 Delphi 7 从未正式支持 Vista,但它在 Vista 上运行良好。有些人建议禁用用户帐户控制 (UAC),但我对此没有遇到任何问题。
我将通过 InDy 组件(Internet Direct 的缩写)— IdHTTP 发送请求。起初,我尝试通过 SuperObject 解析 JSON,但失败并放弃了这个想法。首先,我想出了最简单的形式:
总共有四个元素:
-
MemoInput 是一个输入字段。
-
ButtonSend 是发送按钮。
-
label1 - 输入您的消息。
-
MemoOutput 是一个输出字段。
在 curl 的帮助下,我仔细研究了我必须解析的答案:
{“model”:“deepseek-r1:14b”,“created_at”:“2025-04-07T20:31:19.179407862Z”,“message”:{“role”:“assistant”,“content”:“\u003cthink\u003e\n\u003c/think\u003e\n\n\n您好!“},”done_reason“:”停止“,”完成“:true,”total_duration“:2226372627,”load_duration“:18673060,”prompt_eval_count“:6,”prompt_eval_duration“:169000000,”eval_count“:18,”eval_duration“:203700
很明显,我必须处理 \u003、\u003e 和 \n 等转义序列。您还需要实现从 UTF-8 到 ANSI 的转码,以便应用程序正确处理俄语。总共有四个函数:
-
SendToOllama — 向本地 Ollama 服务器发送 HTTP POST 请求,并接收 JSON 格式的响应。
-
UTF8ToAnsiSafe - 将字符串从 UTF-8 转换为 Windows-1251 (ANSI) 编码。
-
ExtractContent - 从 JSON 响应中提取子字符串 “content”:“。
-
DecodeEscapes 处理转义序列。
代码的最终外观如下所示:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdHTTP;
type
TForm1 = class(TForm)
MemoInput: TMemo;
ButtonSend: TButton;
MemoOutput: TMemo;
Label1: TLabel;
procedure ButtonSendClick(Sender: TObject);
private
function SendToOllama(const UserMessage: string): string;
function UTF8ToAnsiSafe(const UTF8Str: string): string;
function ExtractContent(const JSONText: string): string;
function DecodeEscapes(const S: string): string;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function TForm1.UTF8ToAnsiSafe(const UTF8Str: string): string;
begin
Result := UTF8Decode(UTF8Str); // Convert UTF-8 to Windows-1251
end;
function TForm1.DecodeEscapes(const S: string): string;
begin
Result := StringReplace(S, 'n', #13#10, [rfReplaceAll]);
Result := StringReplace(Result, 'r', '', [rfReplaceAll]);
Result := StringReplace(Result, 't', ' ', [rfReplaceAll]);
Result := StringReplace(Result, '"', '"', [rfReplaceAll]);
Result := StringReplace(Result, 'u003c', '', [rfReplaceAll]);
Result := StringReplace(Result, 'u003C', '', [rfReplaceAll]);
end;
function TForm1.ExtractContent(const JSONText: string): string;
var
StartPos, EndPos: Integer;
Extracted: string;
begin
Result := '(no content found)';
StartPos := Pos('"content":"', JSONText);
if StartPos > 0 then
begin
StartPos := StartPos + Length('"content":"');
EndPos := StartPos;
while (EndPos <= Length(JSONText)) and not (JSONText[EndPos] in ['"']) do
Inc(EndPos);
Extracted := Copy(JSONText, StartPos, EndPos - StartPos);
Result := DecodeEscapes(Extracted);
end;
end;
function TForm1.SendToOllama(const UserMessage: string): string;
var
HTTP: TIdHTTP;
RequestBody: TStringStream;
Mem: TMemoryStream;
JSONStr: UTF8String;
RawBytes, RawResponse: string;
begin
HTTP := TIdHTTP.Create(nil);
RequestBody := TStringStream.Create('');
Mem := TMemoryStream.Create;
try
HTTP.Request.ContentType := 'application/json';
HTTP.Request.Connection := 'close';
JSONStr := UTF8Encode(
'{' +
'"model":"deepseek-r1:14b",' +
'"messages":[{"role":"user","content":"' + UserMessage + '"}],' +
'"stream":false' +
'}'
);
RequestBody.WriteString(string(JSONStr));
RequestBody.Position := 0;
HTTP.Post('http://[server_ip]:11434/api/chat', RequestBody, Mem);
SetLength(RawBytes, Mem.Size);
Mem.Position := 0;
Mem.Read(RawBytes[1], Mem.Size);
RawResponse := UTF8ToAnsiSafe(RawBytes);
Result := ExtractContent(RawResponse);
finally
HTTP.Free;
RequestBody.Free;
Mem.Free;
end;
end;
procedure TForm1.ButtonSendClick(Sender: TObject);
begin
MemoOutput.Lines.Text := SendToOllama(MemoInput.Lines.Text);
end;
end.
我运行它,它工作正常:

Deepseek-R1 俳句写得不是很好,但 Llama 3.1 还是挺不错的
当然,这里有一百万个问题。例如,ExtractContent 尚未考虑嵌套引号,也没有使用成熟的 JSON 解析器。因此,某些答案可能无法正确显示。输出字段中的滚动不够,如果使用 Enter 传输输入字段中的文本,应用程序会崩溃。但所有这些都可以在以后完成和完善。最主要的是这个想法已经实现。
结果是什么
你们中的许多人可能会说,“Python 或 Delphi 到底是什么?将 C# 与 WinForms 配对,并编写一个尽可能接近 Windows Vista 功能的客户端。这可能是正确的,但前提是您了解 C#。我相信许多读者将能够毫无问题地做到这一点。
我尝试以最小的努力为旧作系统编写一个简单的应用程序,它奏效了。现在我的 ThinkPad 将完美地作为一个单独的通信终端工作,与使用 Ollama 启动的不同神经网络一起工作。将来,我计划用 Python 完成客户端,添加用户友好的界面并实现对话存储。
您是否曾经为过时的作系统编写过软件?我们在评论中等你!