
在实际的桌面与后端技术方案中用 Delphi 快速构建一个 AI 交易 agent 并不是天方夜谭。这个项目标题里的核心关键词很明确Delphi、AI、agent。目标是在秒级完成一个可运行的交易决策工作流拉取行情、交给大模型分析、生成结构化交易信号、再转成订单指令并记录日志。本篇文章会从原理、环境、最小案例、关键代码、验证方式、排错链路和生产化建议几个角度展开帮助你用 Object Pascal 快速搭出一个能跑、能查、能扩展的 AI trading agent 框架。需要注意文中所有示例都是技术研究用途不构成任何投资建议。真实交易涉及资金安全、合规审查、市场风险和系统稳定性示例只适合在模拟行情、回测环境和自己掌控的测试账号中运行。1. 先理解 AI 交易 Agent 的组成再谈 Delphi 为什么合适1.1 AI 交易 Agent 是什么AI 交易 agent 本质上是把“数据采集、策略决策、订单执行、结果记录”四个环节连成一条自动化链路。传统程序化交易靠人写死规则例如“价格突破 20 日均线就买入”AI 交易 agent 则把决策环节替换成大模型或者机器学习模型让模型根据行情上下文输出动作。这个动作不能是一段随意自然语言必须是结构化的例如{ action: buy, symbol: BTCUSDT, quantity: 0.01, confidence: 0.78, reason: short_term_momentum_and_volume_increase }agent 要做的事就是把上面的 JSON 从模型输出中解析出来校验 legality、做风控最后转成交易所或券商接口能识别的订单结构。所以它并不是“一个模型”而是一个围绕模型构建的工程系统。1.2 Delphi 在这个场景里的定位Delphi 的强项是 RADE快速应用开发编译生成原生程序不依赖虚拟机启动速度快适合做需要长时间运行的桌面行情监控程序、本地服务程序。相比 Python 这类解释型语言Delphi 在内存占用、并发线程稳定性、GUI 与后台任务结合方面都有优势。AI 部分并不需要在 Delphi 里训练模型。Delphi 通过 HTTP 调用大模型 API例如 OpenAI 兼容接口、本地 Ollama 服务、或者国内模型厂商提供的接口拿到返回结果后使用System.JSON解析。这样分工很清晰Delphi 负责工程调度、网络、解析、风控、日志、界面。AI 模型负责语义理解根据行情文本输出交易动作。这也是“Build your own AI trading agent in seconds”这类项目能成立的原因因为不需要从零写模型推理只要把工程链路串好就可以。1.3 整体工作链路一条最小可运行链路大概是这样定时器触发 - 拉取最新行情 - 组装行情上下文文本 - 发送给 AI 模型 - 获取模型返回 JSON - 解析交易信号 - 风控校验 - 模拟下单 - 写日志在工程实现上这个链路可以拆成下面几个模块模块职责关键技术点行情客户端拉取最新价格、K 线、成交量REST/WebSocket、JSON 解析、缓存提示词构造器把行情转成模型可读文本文本模板、上下文长度控制、字段白名单AI 客户端调用大模型接口HTTP 客户端、超时、重试、API Key 管理信号解析器解析模型返回的 JSON容错解析、枚举校验、数值范围校验风控模块限制单笔金额、每日次数、symbol 范围硬编码规则、配置文件、禁止真实资金调度器按频率触发任务处理并发TThread、TTimer、队列、互斥锁日志模块记录输入输出和异常本地文件、结构化 JSON、滚动日志理解这条链路之后才能知道哪些代码可以先写简化版哪些代码从一开始就不能偷懒。2. 环境准备与项目骨架2.1 Delphi 版本与依赖本文示例基于 Delphi 11 Alexandria 或 Delphi 12 Athens 编写使用 Windows 平台下的控制台程序模式方便专注于逻辑而非界面。实际项目如果要做行情监控界面可以使用 FMX 或 VCL 窗体工程后台逻辑不变。核心依赖全部来自 Delphi 自带的 RTL 和网络库功能单元说明HTTP 客户端System.Net.HttpClient提供TNetHTTPClient支持同步和异步请求JSON 解析System.JSON提供TJSONObject、TJSONArray等类型多线程System.Classes使用TThread做后台轮询定时触发System.Threading使用TTimer或TTask环境变量读取System.SysUtils通过GetEnvironmentVariable读取配置不需要安装第三方组件。如果要用数据库保存历史记录可以再引入 FireDAC 连接 SQLite如果要做更复杂的行情推送可以引入 WebSocket 客户端库但最小案例中先用 HTTP 轮询。2.2 创建项目打开 Delphi 后选择File - New - Other在 Delphi Projects 下选择 Console Application。生成的项目默认代码结构如下program AIAgentDemo; {$APPTYPE CONSOLE} uses System.SysUtils; begin try // 程序入口 except on E: Exception do Writeln(E.ClassName, : , E.Message); end; end.为了跑通一个最小 demo不需要急着建窗体和界面。把逻辑写在一个模块里反而更容易排查问题。2.3 目录结构建议虽然是最小案例也建议按模块划分文件方便后续扩展AIAgentDemo/ ├── AIAgentDemo.dpr ├── MarketDataSource.pas ├── AIClient.pas ├── SignalParser.pas ├── RiskManager.pas ├── AgentWorker.pas └── config.iniconfig.ini不是必须的Demo 阶段可以用常量但一旦准备接入真实 API就必须把 API Key、模型名称、超时时间、交易标的等配置外置。不要把密钥写死在代码里。注意先确认你的 Delphi 版本中System.Net.HttpClient支持 TLS 1.2 或 1.3。部分老旧 Delphi 版本默认 TLS 版本较低调用现代大模型 API 时会直接握手失败。3. 实现一个可运行的 AI 交易 Agent这一章会给出一个最小闭环实现。代码示例用于说明思路真实项目要结合自己的包名、路径和模型接口调整。3.1 封装行情数据源先定义行情数据接口这样后续可以方便替换成真实数据源unit MarketDataSource; interface type TMarketData record Symbol: string; Price: Double; Volume: Double; Timestamp: TDateTime; end; IMarketDataSource interface function GetLatestData(const ASymbol: string): TMarketData; end; TSimulatedMarketDataSource class(TInterfacedObject, IMarketDataSource) private FBasePrice: Double; public constructor Create; function GetLatestData(const ASymbol: string): TMarketData; end; implementation constructor TSimulatedMarketDataSource.Create; begin FBasePrice : 100.0; end; function TSimulatedMarketDataSource.GetLatestData(const ASymbol: string): TMarketData; begin // 模拟行情在基础价格上随机波动 Result.Symbol : ASymbol; Result.Price : FBasePrice Random * 10 - 5; Result.Volume : 1000 Random * 500; Result.Timestamp : Now; end; end.模拟数据源在开发和测试阶段非常重要。它不依赖外部网络可以稳定复现问题。真实环境下你只需要再实现一个类内部通过 HTTP 调用行情服务商接口并返回TMarketData即可。3.2 调用 AI 模型生成交易信号AI 客户端统一负责 HTTP 调用。下面是一个通用结构假设接口兼容 OpenAI Chat Completions 格式unit AIClient; interface uses System.Net.HttpClient, System.JSON; type TAIClient class private FHttpClient: TNetHTTPClient; FBaseUrl: string; FApiKey: string; FModel: string; public constructor Create(const ABaseUrl, AApiKey, AModel: string); destructor Destroy; override; function ChatCompletion(const APrompt: string): string; end; implementation constructor TAIClient.Create(const ABaseUrl, AApiKey, AModel: string); begin FHttpClient : TNetHTTPClient.Create(nil); FBaseUrl : ABaseUrl; FApiKey : AApiKey; FModel : AModel; end; destructor TAIClient.Destroy; begin FHttpClient.Free; inherited; end; function TAIClient.ChatCompletion(const APrompt: string): string; var LRequestBody: TJSONObject; LMessages: TJSONArray; LUserMessage: TJSONObject; LResponse: string; LJson: TJSONObject; LChoices: TJSONArray; begin LRequestBody : TJSONObject.Create; try LRequestBody.AddPair(model, FModel); LMessages : TJSONArray.Create; LUserMessage : TJSONObject.Create; LUserMessage.AddPair(role, user); LUserMessage.AddPair(content, APrompt); LMessages.AddElement(LUserMessage); LRequestBody.AddPair(messages, LMessages); // 建议限制输出格式 LRequestBody.AddPair(temperature, TJSONNumber.Create(0.2)); LRequestBody.AddPair(max_tokens, TJSONNumber.Create(300)); LResponse : FHttpClient.Post( FBaseUrl /chat/completions, LRequestBody.ToJSON, nil, procedure(ARequest: TNetHTTPRequest) begin ARequest.CustomHeaders[Authorization] : Bearer FApiKey; ARequest.CustomHeaders[Content-Type] : application/json; end ).ContentAsString(); // 解析 choices[0].message.content LJson : TJSONObject.ParseJSONValue(LResponse) as TJSONObject; try LChoices : LJson.GetValue(choices) as TJSONArray; Result : ((LChoices.Items[0] as TJSONObject).GetValue(message) as TJSONObject) .GetValuestring(content); finally LJson.Free; end; finally LRequestBody.Free; end; end; end.这段代码里有几个关键点先说明TNetHTTPClient.Post支持传入请求体字符串并能通过匿名过程改写请求头。Authorization头是常见的大模型 API 鉴权方式但不同厂商可能用api-key或x-api-key接入前必须查看对方文档。将temperature设置得较低例如0.2可以减少模型输出随机性让交易信号更稳定。max_tokens用于限制返回长度防止模型一次性输出很长的自然语言导致解析困难。3.3 解析 AI 返回的交易信号模型返回的内容应当是 JSON 字符串但实际输出可能会包含多余的解释文字例如“好的根据当前行情我建议买入{...}”。因此解析器要做两层处理。unit SignalParser; interface uses System.JSON; type TActionType (actBuy, actSell, actHold); TSignal record Action: TActionType; Symbol: string; Quantity: Double; Confidence: Double; Reason: string; end; TSignalParser class public class function Parse(const AContent: string): TSignal; end; implementation class function TSignalParser.Parse(const AContent: string): TSignal; var LJson: TJSONObject; LActionStr: string; LActionValue: TJSONValue; LStartPos, LEndPos: Integer; LCleanContent: string; begin // 如果模型输出包含额外文字先提取最外层花括号片段 LStartPos : Pos({, AContent); LEndPos : LastDelimiter(}, AContent); if (LStartPos 0) and (LEndPos LStartPos) then LCleanContent : Copy(AContent, LStartPos, LEndPos - LStartPos 1) else LCleanContent : AContent; LJson : TJSONObject.ParseJSONValue(LCleanContent) as TJSONObject; if LJson nil then raise Exception.Create(AI 返回内容不是有效 JSON); try LActionValue : LJson.GetValue(action); if Assigned(LActionValue) then LActionStr : LActionValue.Value else LActionStr : hold; // 动作白名单校验 if LActionStr buy then Result.Action : actBuy else if LActionStr sell then Result.Action : actSell else Result.Action : actHold; Result.Symbol : LJson.GetValuestring(symbol); Result.Quantity : LJson.GetValuedouble(quantity); Result.Confidence : LJson.GetValuedouble(confidence); Result.Reason : LJson.GetValuestring(reason); finally LJson.Free; end; end; end.这里要注意ParseJSONValue是无法保证模型永远输出合法 JSON 的。当模型返回内容为空、包含 Markdown 代码块、或者花括号嵌套不完整时解析就会失败。所以生产代码中还要加入异常捕获并考虑对模型输出做预处理例如去掉可能的 json 标记。3.4 组合调度线程与风控校验把 AI 客户端和行情数据源、信号解析器组合进一个后台线程实现周期轮询unit AgentWorker; interface uses System.Classes, System.SysUtils, MarketDataSource, AIClient, SignalParser, RiskManager; type TAgentWorker class(TThread) private FDataSource: IMarketDataSource; FAIClient: TAIClient; FRiskManager: TRiskManager; FSymbol: string; FIntervalSec: Integer; protected procedure Execute; override; public constructor Create(const ASymbol: string; AIntervalSec: Integer); destructor Destroy; override; end; implementation constructor TAgentWorker.Create(const ASymbol: string; AIntervalSec: Integer); begin inherited Create(True); FSymbol : ASymbol; FIntervalSec : AIntervalSec; FDataSource : TSimulatedMarketDataSource.Create; FAIClient : TAIClient.Create(https://api.example.com/v1, YOUR_API_KEY, gpt-4o-mini); FRiskManager : TRiskManager.Create; FreeOnTerminate : False; end; destructor TAgentWorker.Destroy; begin FAIClient.Free; FRiskManager.Free; inherited; end; procedure TAgentWorker.Execute; var LData: TMarketData; LPrompt: string; LContent: string; LSignal: TSignal; begin while not Terminated do begin try LData : FDataSource.GetLatestData(FSymbol); LPrompt : Format( 当前时间: %s, 标的价格: %.2f, 成交量: %.0f。 请判断交易动作只返回 JSON: {action:buy/sell/hold, symbol:%s, quantity: 数量, confidence: 0到1之间, reason: 简短原因}, [DateTimeToStr(LData.Timestamp), LData.Price, LData.Volume, FSymbol] ); LContent : FAIClient.ChatCompletion(LPrompt); LSignal : TSignalParser.Parse(LContent); if FRiskManager.Validate(LSignal) then // 这里接入模拟交易所接口Demo 阶段只打印 Writeln(Format(下单: %s %s 数量 %.4f, 置信度 %.2f, 原因 %s, [FSymbol, LSignal.Action.ToString, LSignal.Quantity, LSignal.Confidence, LSignal.Reason])) else Writeln(风控拦截: FSymbol 的信号未通过校验); except on E: Exception do Writeln(任务异常: , E.Message); end; Sleep(FIntervalSec * 1000); end; end; end.风控模块的职责在 Demo 中可以先简化为检查confidence范围和quantity是否超过上限unit RiskManager; interface uses SignalParser; type TRiskManager class public function Validate(const ASignal: TSignal): Boolean; end; implementation function TRiskManager.Validate(const ASignal: TSignal): Boolean; begin Result : True; // 超出置信度范围 if (ASignal.Confidence 0) or (ASignal.Confidence 1) then Exit(False); // 单次数量过大 if ASignal.Quantity 100 then Exit(False); // 不允许做空类高频买卖 if ASignal.Action actSell then Exit(False); end; end.Demo 中的风控很简单但“必须存在”这一点很重要。因为没有校验的 agent 一旦接入真实接口模型输出的一个小错误就可能变成不可控订单。4. 关键参数与执行流程详解4.1 提示词设计决定了 agent 的可靠性上限AI 交易 agent 的决策质量并不完全取决于模型提示词在很大程度上决定了模型输出是否稳定。同一份行情数据如果提示词写得模糊模型可能返回一大段分析文字而不是结构化 JSON如果提示词约束明确模型会更容易遵循输出格式。建议提示词包含以下信息当前时间和行情数据。要求返回的动作枚举buy、sell、hold。要求返回 JSON 结构。要求confidence在 0 到 1 之间。要求reason简短且不能出现非交易原因。示例提示词你现在是一个只负责输出 JSON 的交易决策程序。 当前时间2025-01-01 10:00:00 标的价格102.35 成交量1200 请根据短期趋势判断买入、卖出还是持有。 只返回 JSON不要包含其他解释格式如下 {action:buy,symbol:BTCUSDT,quantity:0.01,confidence:0.8,reason:...}temperature参数也建议调低。在 OpenAI 兼容接口里temperature越高输出越随机。对交易决策随机性过大会导致同一行情反复出现 buy/sell 抖动因此建议设置在0.1到0.3之间。max_tokens设置 200 到 500 就够用。交易信号字段不多太长输出反而容易在 JSON 之后夹带多余文本。4.2 网络参数超时、重试与流量控制调用外部 AI 接口时最常见的错误不是逻辑错误而是网络超时、限流、连接重置。相关参数必须在一开始就设计好参数推荐值说明ConnectTimeout10 秒建立连接的最长等待时间ReadTimeout30 秒等待响应体的最长等待时间重试次数2 到 3 次只有在连接超时和 429/5xx 错误时才重试重试退避1 秒、2 秒、4 秒指数退避避免连续打满接口单标的轮询间隔5 到 60 秒根据策略频率和数据源限制调整同时并发标的数建议不超过 5 个每增加一个标的就是多一份 API 成本和风险在 Delphi 中TNetHTTPClient可以设置超时参数FHttpClient.ConnectionTimeout : 10000; // 毫秒 FHttpClient.ResponseTimeout : 30000; // 毫秒重试逻辑不要放在 HTTP 客户端内部建议封装成一个服务方法捕获异常后按次数和退避策略重试。要特别注意当前代码里TAIClient.ChatCompletion没有重试生产版本需要加一层循环同时对429 Too Many Requests和5xx做不同处理。4.3 JSON 解析的容错策略大模型输出的 JSON 不像普通程序那样永远合法。常见问题有输出内容被 Markdown 代码块包裹例如json ... 。输出中包含解释性文字。JSON 字段名被模型改写成Action而不是action。数值字段返回字符串0.5。字段缺失。所以在解析前先做以下预处理移除首尾空白。如果包含代码块标记去掉json和。提取最外层{到最后一个}的内容。解析失败时记录原始内容方便后续调整提示词或增加 few-shot 示例。一个更稳妥的做法是不仅解析TSignalParser.Parse还要在解析失败时把原始返回写入日志文件。这样即使模型输出异常你也知道是哪个环节出了问题。5. 运行验证与结果分析5.1 控制台运行流程把主程序入口补全创建后台线程并启动program AIAgentDemo; {$APPTYPE CONSOLE} uses System.SysUtils, AgentWorker in AgentWorker.pas, MarketDataSource in MarketDataSource.pas, AIClient in AIClient.pas, SignalParser in SignalParser.pas, RiskManager in RiskManager.pas; var LWorker: TAgentWorker; begin try Randomize; Writeln(AI Trading Agent Demo 启动); LWorker : TAgentWorker.Create(BTCUSDT, 10); LWorker.Start; Writeln(按回车键退出); Readln; LWorker.Terminate; LWorker.WaitFor; LWorker.Free; except on E: Exception do Writeln(E.ClassName, : , E.Message); end; end.运行后预期输出类似AI Trading Agent Demo 启动 按回车键退出 下单: BTCUSDT buy 数量 0.0100, 置信度 0.78, 原因 short_term_momentum 下单: BTCUSDT hold 数量 0.0000, 置信度 0.55, 原因 no_clear_signalDemo 阶段没有真正连接交易所所以收到类似输出说明链路已经跑通。5.2 验证点不只是“能启动”很多项目卡在最容易出错的地方程序能启动但每次调用都失败或者偶尔成功偶尔失败。对 AI 交易 agent建议至少检查以下验证点行情数据是否成功拉取字段是否正确。AI 请求是否成功返回返回状态码是 200 还是 429/500。AI 返回内容是否可被解析成TSignal。风控逻辑是否拦截了异常信号。日志文件中是否有完整的请求输入和输出。一个快速的验证方法是写一个单独的测试入口不启动线程直接执行一次完整链路LData : FDataSource.GetLatestData(BTCUSDT); LPrompt : BuildPrompt(LData); LContent : FAIClient.ChatCompletion(LPrompt); LSignal : TSignalParser.Parse(LContent); Writeln(Format(action%s, symbol%s, quantity%.4f, confidence%.2f, reason%s, [LSignal.Action.ToString, LSignal.Symbol, LSignal.Quantity, LSignal.Confidence, LSignal.Reason]));这样可以在不依赖界面和定时任务的情况下快速定位问题在哪个模块。5.3 日志与回放交易 agent 的日志必须做到“可回放”。也就是说一段时间后你能根据之前记录的行情和模型输出重演当时的决策过程。推荐每次都记录以下信息时间戳精确到毫秒。行情快照价格、成交量、标的。发送给模型的完整提示词。模型原始返回。解析后的信号。风控结果。是否下单。异常信息如果有。日志可以使用 JSON Lines 格式每行一个事件方便用脚本分析。{ts:2025-01-01 10:00:00.123,symbol:BTCUSDT,price:102.35,action:buy,confidence:0.78,accepted:true}6. 常见问题排查6.1 典型问题速查表问题现象常见原因检查方式处理建议调用 AI 接口报 TLS 错误Delphi 版本默认 TLS 版本过低检查 HTTP 客户端日志确认握手失败位置升级 Delphi 版本或使用 WinHTTP 组件并启用 TLS 1.2AI 返回内容解析失败模型输出包含 Markdown 或解释文字打印模型原始返回内容增加代码块过滤、花括号提取、失败日志请求返回 401 或 403API Key 错误或者鉴权头格式不对检查请求头是否完整发送对照接口文档确认Authorization或api-key格式程序运行一段时间不再输出线程阻塞或异常被反复吞掉查看日志文件或添加异常详情增加日志记录避免空 except风控一直拦截信号模型输出的类型与预期不符打印解析后信号字段调整提示词或者在解析层增加字段类型转换请求被限流轮询频率过高超过 API 限制查看 HTTP 状态码是否为 429增加间隔使用指数退避重试模拟行情波动太大策略不稳定随机数波动范围不合理查看日志中的价格历史使用固定种子或更平滑的随机模型6.2 一条实用的排查链路如果运行出错不要盯着异常栈看而是按以下顺序排查先确认输入行情源是否真的返回了数据字段是否完整。再确认提示词发送给模型的内容是否包含合适上下文是否有格式约束。确认 AI 接口请求是否成功返回状态码原始返回内容。确认解析器是否把原始内容转成了TSignal。确认风控为什么拦截是置信度不合格还是数量越界。最后确认执行端是否真的按TSignal生成了订单指令。这个顺序的本质是从输入到输出逐层推进。每一层都留下日志问题就会很快浮出水面。注意不要只为 Success 情况写日志异常分支同样需要日志。很多时候agent 不触发交易并不代表它正常工作也可能是解析器一直失败信号被静默吞掉。7. 生产化前必须补齐的能力7.1 学习环境与生产环境的差异最小 Demo 能跑通和生产可用之间还有很长的距离。下表列出主要差异维度学习/Demo 环境生产环境行情源随机数模拟交易所官方 API、行情服务商、合规数据源AI 模型任意兼容接口根据时延、成本、稳定性选择模型考虑本地部署订单执行打印日志交易所/券商接口需要签名、私钥、订单确认资金安全无真实资金需要止损、最大回撤、单日亏损限制风控简单校验多层级风控、人工审批、交易熔断日志控制台输出结构化日志、集中采集、审计监控无指标监控、告警、看板版本管理单机源码CI/CD、回滚、灰度发布合规不做要求遵守当地法律法规和交易所规则Demo 阶段可以直接把 API Key 写在常量里但一旦进入生产不仅要把 Key 外置还要考虑密钥泄露后的吊销机制。建议至少使用环境变量或加密配置文件。7.2 风控是第一优先级对于交易 agent风控的价值高于所有策略优化。没有风控的 agent 就像没有刹车的车。生产环境至少要加入这些风控单笔订单数量上限。单日订单次数上限。单日亏损熔断。最大持仓数量限制。只允许操作白名单内的 symbol。模拟盘和真实盘配置严格隔离。关键订单走人工二次确认。风控参数不要写死在代码里建议放在配置文件中并限制只有特定角色可以修改。7.3 上线前检查清单下面是一份可复用的检查清单适合在接入真实交易前逐项确认检查项是否完成备注行情源接入是否通过官方接口验证是/否不要用抓包非正规接口AI 调用是否加入超时与重试是/否避免长时间阻塞AI 输出是否做了容错解析是/否记录原始内容风控是否覆盖异常信号是/否包括数量、置信度、频次模拟盘是否运行满 7 天是/否观察长周期稳定性是否配置结构化日志是/否可回放决策过程是否配置告警是/否进程崩溃、接口连续失败是否确认符合平台规则是/否不同平台对自动交易有不同要求是否有手动停止和应急回滚方案是/否一键断开下单接口API Key 是否安全存储是/否不提交到版本库7.4 下一步扩展方向最小 agent 跑通后可以从以下方向继续扩展增加真实行情源先用 REST 轮询再逐步过渡到 WebSocket 推送降低延迟。增加回测引擎把历史行情和 AI 决策组合观察策略在不同市场环境下的表现。多策略并行让多个模型或者多个提示词策略同时分析再通过投票机制决定最终动作。本地模型接入使用 Ollama 等本地推理方案可以减少外部接口的时延和网络依赖也避免行情数据外传。订单管理模块增加未成交订单查询、撤单、持仓管理形成真正可用的交易闭环。从工程角度看AI 交易 agent 的核心难点并不在于“让 AI 判断涨跌”而在于如何把模型输出放到一个稳定、可审计、有风控的工程系统里。Delphi 的价值恰好体现在这里它能让开发者快速搭建后端调度、网络、解析和日志链路同时保持原生程序的性能和稳定性。对于想研究 agent 架构的开发者来说用 Delphi 做一个最小版本是一条值得走完的实践路径。