☰
TMS AI Studio 详解:在 Delphi 中接入大模型与 Florence 视觉识别
2026/10/6 5:42:20 网站建设 项目流程

简介:TMS AI Studio v1.3.0.0 for Delphi 11-13 Florence 是一套帮助 Delphi 开发者快速集成人工智能能力的控件库完整源代码。它面向熟悉 Delphi 但缺乏 AI 算法背景的开发者,可在熟悉环境下调用图像识别、自然语言处理、机器学习模型等现成组件,实现业务应用的智能升级。

包体共216个文件,大小61.44MB,以68个 pas 源码、32个 dproj/dpr 工程文件为主体,辅以 dpk 包定义、dfm/fmx 界面布局、res 资源文件以及 dll、示例数据库等,结构性展示了控件库的依赖与构建方式,便于按模块阅读与二次编译。

当前已有120人学习下载。由于提供 Full Source,使用者不仅能直接享用控件功能,还可深入研读 AI 集成内部实现、修改默认行为、扩展专属智能场景。压缩包内附多个演示窗体与示例工程,以及编译脚本和样例数据,适合希望将 AI 能力嵌入 Delphi 11/12/13 应用、兼顾定制化需求的中高级开发者在本地搭建验证。

1. 从控件到 Agent:TMS AI Studio 到底在解决 Delphi 的什么问题

拿到这个「Delphi 13.1 控件之 TMS AI Studio v1.3.0.0 for Delphi 11-13 Florence Full Source.rar」,第一反应先别是解压安装——先想清楚它解决的是哪一类问题。TMS AI Studio 不是又一个 JSON 序列化库,它是把「大模型调用」这整条链路做成 VCL/FMX 控件:从 OpenAI 兼容网关鉴权、聊天补全、流式回调,到 Florence 这类视觉模型的图像理解、OCR、目标检测,再到 Agent 工具调用,全都被封装成 Delphi 里能直接拖到 Form 上的可视组件。老 Delphi 项目要接 AI,最疼的不是写 HTTP 请求,而是密钥管理、JSON 拼装、流式解析、线程回调这一堆杂活。这套控件的价值就是把这些杂活收进面板,让业务代码只跟 Delphi 对象打交道。适合谁?手上压着老 VCL 工程、想用 Delphi 直接调大模型、又不想养一套 Python 中转服务的团队,这篇就是给你们看的。

2. 装包前先摸清底细:Full Source 包的正确拆法

2.1 为什么优先选 Full Source 而不是安装版

TMS 家的控件一直分两条线:编译好的安装版和 Full Source。这个 rar 名字里带 Full Source,含义很实在——所有 pas 和 dpk 都在里面,理论上你可以自己改控件源码,也可以手工编译到任何受支持的 Delphi 版本。实际使用中,我一般会优先要 Full Source,原因有三。第一,TMS 控件装进 IDE 后默认会注册到 Component Palette,但公司内网环境经常禁止往 C 盘 Program Files 写东西,Full Source 可以指定输出目录,不污染系统盘。第二,控件升级时如果只装了二进制版,一旦 Delphi 打补丁(从 12.0 升到 12.1),很多时候会让你重新下载安装包,手工编译就没这个烦恼。第三,你真的会碰到需要看源码定位问题的时候——比如某个请求头带得不对,黑匣子版只能提工单,Full Source 能自己断点进去看,省下的时间够买好几套控件了。选择时记住一句:能跑的工程,优先用 Full Source 方案;图省事的新手,才考虑直接跑安装向导。

2.2 解压与目录结构:先定位 dpk 和 lib 文件夹

拿到 rar 后,我习惯先解压到一个纯英文路径,比如D:\Libs\TMSAIStudio\,不放进 Delphi 的安装目录。原因是 Delphi 的 Library Path 对中文字符和空格支持很差,一旦路径里有中文,编译时会出现莫名其妙的File not found错误,这种玄学问题排查起来非常浪费时间。解压完先不要急着打开 Delphi,用命令行把目录结构列一遍,确认包内布局符不符合预期。常见的 Full Source 包会按 Delphi 版本分目录,或者统一放在Source或Lib目录下,里面有.dpk包文件、.pas源码和.res资源文件。用 PowerShell 看一眼:

# 列出 TMS AI Studio 包根目录下的所有子目录,确认版本目录与 dpk 文件位置 Get-ChildItem -Path "D:\Libs\TMSAIStudio" -Recurse -Include *.dpk,*.dpkg,*.pas | Select-Object FullName,Length | Format-Table -AutoSize

这条命令把包内所有 Delphi 包文件和源码文件列出来,输出里你会看到类似TMSAIStudio_D12.dpk或AIStudioCore.dpk这样的文件。逻辑说明:.dpk是 Delphi 包工程文件,Delphi 编译器通过它决定把哪些单元编进包、生成什么后缀的.bpl;.dpkg是群组工程,用来一次编译多个包。看到这些文件,就说明包的解压是完整的,可以进 IDE 操作了。

这里有个参数值得注意:-Include后面你可以自行追加*.inc,很多控件包有条件编译开关写在.inc文件里,TMS 也经常用.inc控制不同 Delphi 版本的差异。编译报错时先打开.inc看版本定义,往往比改代码更快。另外,解压后建议立刻右键 rar 查看属性,确认没有「已解压」提示,这个步骤容易被跳过,但 Full Source 包如果缺失资源文件,编译时不会立刻报错,而是到 IDE 启动加载包时才崩,到时候定位成本就高了。

2.3 添加 Library Path 与编译顺序:先 Core 后 UI

包的结构摸清后,标准的安装动作是三步:加 Library Path、编译 dpk、验证 Palette 注册。第一步在 Delphi 的Tools -> Options -> Language -> Delphi -> Library里,把包含源码的目录加到 Library Path 中。TMS 这类控件通常拆成运行期包和设计期包,运行期包是业务代码引用的,设计期包负责在 IDE 里画控件。编译顺序必须是先运行期、后设计期,否则 IDE 会提示找不到 xxx.dcp。

我一般会新建一个dcu输出目录,比如D:\Libs\TMSAIStudio\dcu\$(Platform),这样编译产物不混在源码里,升级时直接清空 dcu 目录重编就能得到干净环境,避免旧 dcu 导致的版本错乱。编译前把 Delphi 编译器选项里的Generate all .dcu files打开,不打开的话,跨平台工程(同时编 Win32/Win64)会因为 dcu 平台不匹配翻车。接下来打开 dpk 文件,按依赖顺序逐个Compile。这里注意看包名后缀,标题里写的是 for Delphi 11-13,解压后你会看到D11、D12、D13或_D12这样的后缀,必须选与你 IDE 版本对应的那个 dpk,选错版本编出来的 bpl 就算侥幸能装上,运行时也会报Package ... was compiled with a different version of ...的经典错误。

// 安装完成后,新建一个 VCL 工程,在单元头部引用核心命名空间来验证安装 // 如果 IDE 能编译通过并弹出窗体,说明运行期包已被正确链接 uses TMS.AI.Core, // 核心客户端组件所在单元 TMS.AI.Florence, // Florence 视觉模型组件单元 TMS.AI.Visual; // 可视化 Agent / 聊天组件单元

这段代码没有实际业务逻辑,它的作用是触发编译器去解析这三个单元。如果 Library Path 配错或 dpk 没编过,IDE 会在uses这一行直接报F2613 Unit 'TMS.AI.Florence' not found,这就是最直接的安装反馈。参数说明:TMS.AI.Core这一类单元名不是死的,具体以你解压包里的实际 pas 文件名称为准——有的版本用AIStudio.Core,有的用TMSAI.Types,看目录里的.pas列表即可。判断标准只有一个:编译不报错,并且能在Component Palette里看到新增的组件页签。安装到这里只算完成一半,接下来还要验证它真的能连上服务。

3. 跑通第一个 LLM 对话:从 Palatte 到 JSON 的完整链路

3.1 组件背后的调用链:TMS 到底替你做了哪些事

很多人拖了一个TTMSOpenAIClient到窗体上,以为它是个黑匣子,其实它做的事情完全可以拆开看。TMS AI Studio 的客户端组件本质上是把标准的 REST 调用折叠成了 Delphi 方法:你在属性面板里填 BaseURL、API Key、模型名,调用一个ChatCompletion方法,控件内部负责拼接 JSON 请求体、设置鉴权请求头、用异步 HTTP 发请求、再把返回 JSON 反序列化成TMSChatResponse对象。其中最容易被忽视的是流式输出的处理——直接调 REST API 通常返回一整段 JSON,大模型的输出可能要等十几秒甚至更久,用户界面会卡死。TMS 的组件默认把 HTTP 请求放在后台线程,通过OnStream或OnPartial回调往主线程抛数据块,这样聊天窗口能像 Web 版那样一个字一个字往外蹦。明白了这层关系,你就知道后面所有参数调整都是在跟这个链路打交道。

3.2 最小可运行的聊天 Demo:Delphi 代码里怎么配

找一张空 Form,放一个TMemo当聊天记录、一个TEdit当输入框、一个TButton当发送按钮,再加一个客户端组件,代码量控制在一屏以内。用 OpenAI 兼容协议的大模型网关最多,因为它是事实标准,不管是国内服务还是企业内网部署的模型网关,绝大多数都兼容这个协议。下面是核心代码,写在按钮的OnClick里:

procedure TForm1.btnSendClick(Sender: TObject); var Request: TMSChatRequest; Response: TMSChatResponse; ErrMsg: string; begin // 1. 组装对话请求:系统提示词约束角色,用户消息为输入框内容 Request := TMSChatRequest.Create; try Request.Model := 'qwen-plus'; // 选一个 OpenAI 兼容的模型名 Request.Temperature := 0.7; // 采样温度,控制输出随机性 Request.MaxTokens := 1024; // 单次回复的最大 token 数 Request.AddSystemMessage('你是一个熟悉 Delphi 的资深架构师,回答要简洁、给代码。'); Request.AddUserMessage(edtInput.Text); // 2. 同步发起调用(组件内部已在后台线程执行 HTTP,不会卡 UI) Response := TMSOpenAIClient1.ChatCompletion(Request, ErrMsg); try if Assigned(Response) then mmoChat.Lines.Add('AI: ' + Response.Content) else mmoChat.Lines.Add('[错误] ' + ErrMsg); finally Response.Free; end; finally Request.Free; end; end;

这段代码做完两件事:组装请求对象、发起调用并取回文本结果。逻辑说明:AddSystemMessage和AddUserMessage是组件提供的语义化方法,TMS 在内部把它们转换成 OpenAI 协议里的system和user角色数组,你不需要手工拼 JSON;ChatCompletion是同步方法的命名风格,但控件内部用了异步 HTTP,主线程不会假死。参数说明:Temperature在需要稳定输出的场景(比如数据抽取、代码生成)调到 0.2~0.3,在写作文、头脑风暴场景调到 0.8~0.9;MaxTokens不是回复长度的上限,是生成预算的上限,设太大会让单个慢请求占用连接,设太小会导致回答被截断,1024 对这个 Demo 够用。如果你用的组件派生产自 TMS 其他 AI 系列,方法名可能是CreateChatCompletion或GetChatCompletion,注意看 IDE 代码提示即可。

3.3 必调参数:流式、超时、重试与密钥保管

同步 Demo 跑通只证明链路通,真正要上生产,还有四个参数必须动。第一个是流式回调:TMS 的客户端多半带EnableStreaming属性,设成True后,用OnStreamDelta事件逐段接收文本,在事件里往 Memo 追加内容,就能做到打字机效果。这个效果不只是好看,用户在等待时能看到输出在增长,就会愿意等,这是聊天类产品的基本体验。第二个是Timeout,默认值往往是 60 秒,大模型推理经常超过这个数,尤其在用慢模型或满负载网关时,我一般设到 180 秒。第三个是重试次数,TMS 底层用 Indy 或 NetHTTP 时,对 429(限流)和 5xx(网关错误)的重试策略不同,控件一般提供RetryCount属性,我建议设 2~3 次,但重试之间要有退避,否则网关限流会雪上加霜。第四个是密钥保管:APIKey属性如果直接写在 dfm 里,别人拿到工程就能看到,至少单独放到一个AIKeys.inc文件里并加入版本库忽略名单,或者运行时从外部配置读入,不要让明文密钥出现在窗体文件里。

4. 让 Delphi 看懂图:Florence 模型接入与图像理解

4.1 Florence 在 TMS 里扮演什么角色

Florence 不是聊天模型,它是微软公布的视觉基础模型,核心能力是把图片变成文字结构:可以做 OCR 文字识别、目标检测(返回目标框和标签)、区域描述、整图描述、视觉问答等。TMS 把它集成进 Delphi,意味着 VCL 程序里可以直接把TImage里的图、TBitMap 里的截图、甚至 PDF 渲染出的页面交给 Florence,它返回结构化 JSON,Delphi 再用TJSONObject解析。这个组合让 Delphi 老项目第一次能高效处理「看图说话」类需求。我见过最快的落地案例:用 Delphi 的WebBrowser控件打开一个内部系统,定时截取页面区域,再调用 Florence 把表格区域变成文字描述——整个流程不需要任何 OCR 中间服务,一台普通 Windows 机器就能跑。标题里特别提到 Florence Full Source,说明这个包不光能调 Florence 的服务端 API,还可能包含了本地推理接口或者对 Florence 模型的完整封装源码,这对要做离线识别、数据不出内网的团队价值很大。

4.2 把 TImage 转成 Florence 能吃的 Base64:压缩这一步不能省

Florence 服务端对图片有体积和分辨率限制,最常见的是图片最大 20MB、边长阈值在 2048 像素左右。Delphi 程序里截屏或加载的图往往远超这个数,所以必须先压缩。压缩策略我固定用两步:先等比缩放到最长边 1024,再保存为 JPEG 质量 85。这个组合在清晰度和体积之间最稳,OCR 对小字体的需求也能满足。用 VCL 里的TBitmap配合JPEG单元做转换:

function BitmapToFlorenceBase64(Src: TBitmap; MaxSide: Integer = 1024; Quality: Integer = 85): string; var Bmp: TBitmap; Jpg: TJPEGImage; MS: TMemoryStream; Scale: Double; begin Bmp := TBitmap.Create; try // 1. 等比缩放:长边控制在 MaxSide,避免服务端拒绝超大图 if Src.Width > Src.Height then Scale := MaxSide / Src.Width else Scale := MaxSide / Src.Height; Bmp.Width := Round(Src.Width * Scale); Bmp.Height := Round(Src.Height * Scale); Bmp.Canvas.StretchDraw(Rect(0, 0, Bmp.Width, Bmp.Height), Src); // 2. 转 JPEG 并编码为 Base64 字符串 Jpg := TJPEGImage.Create; MS := TMemoryStream.Create; try Jpg.Assign(Bmp); Jpg.CompressionQuality := Quality; Jpg.SaveToStream(MS); MS.Position := 0; // 调用 EncodeBase64 前要先解码字节,Delphi 自带 EncdDecd 单元 Result := TNetEncoding.Base64.EncodeBytesToString(MS.ToArray); finally Jpg.Free; MS.Free; end; finally Bmp.Free; end; end;

逻辑说明:先把原始图StretchDraw到缩放后的画布,这一步保证图像不失真地变更尺寸;然后走 JPEG 压缩,利用 JPEG 对自然图像的压缩率把体积降下来;最后把 JPEG 字节流转成 Base64 字符串,供 Florence 请求装配。参数说明:MaxSide设 1024 是保险线,Florence 服务端如果支持更大分辨率,可以适当提高到 1536,但要同时观察请求耗时,图片越大推理越慢;Quality设 85 而不是 90 以上,是为了压体积,Florence 对 JPEG 质量不是特别敏感,85 足够。注意有个容易被忽略的坑:TNetEncoding.Base64在 Delphi 10.3+ 才有,旧版本请改用EncdDecd.pas的EncodeBase64(MS.Memory, MS.Size),接口形式不同,效果一样。另外,Florence 如果走的是本地推理模型,这一步就不是为了体积,而是为了归一化输入尺寸,因为本地模型往往固定输入大小,缩放是硬要求。

4.3 两个值得复用的落地场景:截图识别与表格 OCR

场景一:VCL 里定时截取全屏或某区域,把截图交给 Florence 做「界面描述」,再配合聊天模型生成操作指令,这就是轻量级 RPA 的雏形。Delphi 里截屏用TCanvas.CopyRect从桌面 DC 抓图,抓到TBitmap后直接调用上面的BitmapToFlorenceBase64方法,把 Base64 塞进请求的image字段。这比调用海康摄像头抓帧再走 OpenCV 的方案简单,因为控件层已经替你消化掉了 HTTP 细节。场景二:Delphi 的TRBuilder或TMS Flexcel导出 PDF 后,把 PDF 页面渲染成位图再做 OCR。热搜里那条「delphi rbuilder 导出成pdf」其实就接着这条线:导出的 PDF 如果后续要录入系统,先用 Florence 识别一遍,能自动填单。Florence 返回的目标检测 JSON 里带坐标框,你可以用 TCanvas 把识别结果框在原图上画出来,给用户一次可视化的校对机会。

5. TMS AI Studio 避坑指南:全源码模式下最常见的 5 个翻车现场

5.1 现象:IDE 启动报「Package 版本不匹配」或直接崩

原因:最常见的是机器上装了多个 Delphi 版本,或者之前装过 TMS 其他控件的旧编译产物,你手工编译的新 bpl 被 IDE 加载器和旧 bpl 混用,两边的 dcu 版本对不上。另一个高频原因是你在 Delphi 11 里编译了但Target Platforms里同时勾了 Win32 和 Win64,只编了其中一个平台却注册了全部。解决:先关掉 IDE,进C:\Users\<用户名>\AppData\Roaming\Embarcadero\BDS\下的对应版本目录,清空 Known Packages 缓存里的 TMS 条目;再回到源码目录,删除所有__history、dcu输出目录里的旧文件,只保留当前目标平台重新编译;最后只编译和你 IDE 版本匹配的那个 dpk,不要顺手把相邻版本的也编了。

5.2 现象:请求发出去,十几秒后超时,或返回 403/401

原因:不是模型的错,大多是 TLS 版本和证书校验的问题。Delphi 的 Indy 组件默认 SSL 版本可能是 TLS 1.0,很多模型网关在新版本里直接禁用了 TLS 1.0/1.1,只开 TLS 1.2/1.3。另外公司电脑如果装了安全代理,证书链会不完整,Indy 校验证书时直接失败。解决:在窗体创建时强制设置安全协议,用 Delphi 自带的TIdSSLIOHandlerSocketOpenSSL时,在OnConnect里把SSLOptions.Method设为sslvTLSv12;如果用的是 NetHTTP 客户端,检查SecureProtocols属性,把 TLS 1.2 打开。不要只开 TLS 1.3,部分网关和代理的兼容性还停在 1.2。证书报错时,先确认不是公司内部的证书劫持,如果是内网实验室环境,可以在代码里临时跳过证书校验做连通性测试,但生产环境不要这样干。

5.3 现象:Florence 返回「invalid image」或空结果

原因:图片转 Base64 之后体积仍然超标,或者格式不对。很多模型网关要求图片提交为 JPEG/PNG 的原始字节流,而不是带有 data URL 前缀的完整串;如果你直接把data:image/jpeg;base64,前缀保留下来一起发出去,服务端解析器会报格式错误。另外监控摄像头取到的 BMP 位图里可能带透明通道或 32 位色深,Florence 模型不支持。解决:回看第二章的封装函数,确认输出的是纯 Base64 字符串;把Bmp.PixelFormat先强制设成pf24bit再转 JPG;压缩后如果体积还在 1MB 以上,把MaxSide降到 768。做这一步时用 Delphi 的TFileStream把编码结果直接落盘看一下大小,别靠猜。

5.4 现象:编译过程中报内存错误,或 IDE 越来越卡

原因:这就是热搜里那条「delphi 编译程序报内存错误」在控件场景下的变体。TMS 控件包比较大,全源码模式下 IDE 要加载的符号多,再加上开了 Code Insight 的自动成员提示,Delphi 编辑器会把你机器内存吃满。更隐蔽的诱因是安装时把输出目录放进了输入目录(dcu 和 pas 混在一个夹子里),Delphi 编译时反复扫描自身生成的文件,陷入循环。解决:准备一个 dcu 独立输出目录,并确保它不在源码目录的子路径里;关闭 IDE 的Background compilation功能,改成手动编译;如果 32 位 Delphi 在 Windows 上经常崩,换 64 位 IDE 或把大工程拆成多个包。这条属于环境整洁度问题,做对了能省掉后面几个月的火气。

5.5 现象:中文响应乱码或截断

原因:TMS 的 HTTP 客户端在解析响应体时用了 ISO-8859-1 或系统默认代码页,而大模型网关返回的流式 JSON 是 UTF-8。另一处是请求方的角色消息里如果带中文,组件内部拼 JSON 时发生了隐式编码转换。解决:看控件是否暴露ResponseCharset属性,有就显式设成UTF-8;没有的话,拿到Response.Content后先用TEncoding.UTF8.GetString(RawBytes)自己转一遍,不要直接用String。如果问题出在流式回调的分段字符上,多半是 TCP 分包把多字节中文切成了两半,TMS 的组件做了缓冲拼接还好,一旦没有,你需要在回调里维护一个TStringBuilder,把接收到的字节先累积再按 UTF-8 解码,而不是逐段TEncoding.UTF8.GetString。处理完中文,再把MaxTokens往上提一点,中文 token 密度比英文高,同样语义下多耗 1.5 到 2 倍 token,1024 对中文对话很容易截断。

6. 再往前走一步:把控件调用封装成自己的服务层

最后一招直接决定这套控件的寿命:不要在 Form 里到处直接TMSOpenAIClient1.ChatCompletion,控件的 API 跟着版本走,明年 TMS 升级到 v2 可能改方法名,你的业务代码会被牵着鼻子走。我习惯是在业务层定义自己的接口,把 AI 调用挡在接口后面:

type ITextAIService = interface function Complete(const Prompt: string; SystemPrompt: string = ''): string; function CompleteStream(const Prompt: string; OnDelta: TProc<string>): string; end;

然后写一个TTMSTextAIService类实现这个接口,内部持有 TMS 的客户端组件实例,把所有参数(模型名、温度、超时、密钥来源)收敛到类的构造器里。这样以后不管是把 TMS 换成别的控件,还是直接改成 REST 自研,Form 层的代码一行都不用动。做这个封装时带上一个习惯:给服务和模型写个真实可用性的验收清单,用 Delphi 写一个小工具,逐个测模型连通性、中文乱码、长文本截断、流式是否卡顿,测完勾选留档。这个清单比我在这里多写两千字都值——再遇到 5.2 节那种 TLS 冲突,你只需要跑一遍工具,就能定位是环境问题还是模型网关问题,不用再开两个工程对比玄学。我自己的 Delphi 项目里一直维持这个习惯,用 TMS AI Studio 只是把验证范围从「调通大模型」扩到了「调通控件封装层」而已,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询