1. 文本差异检查程序为什么需要接入 AI 能力
文本差异检查,说白了就是把两份文档摆在一起,找出哪里改了、哪里删了、哪里新增了。同一份合同的两个版本、同一段代码的两次提交、同一篇文章的初稿和终稿,都可能需要这种比对。Delphi 生态里有个经典的 Diff.pas,能把差异算出来,但算出来只是第一步,怎么把差异显示得清楚、可读,才是真正花时间的地方。
我之前的做法是用 Delphi 加载一个带 BootStrap 的 HTML 模板,把 Diff.pas 算出的差异段落用 span 包起来,加上背景色 class,再塞进 pre 标签里保证空格和换行不丢,最后用 TWebBrowser 渲染出来。这套骨架跑通之后,显示问题基本解决了。但新的问题来了:差异是找到了,可两段文本到底为什么不同、改动是否影响语义、有没有潜在风险,这些判断靠纯字符串比对是给不出来的。
这时候就需要把 Diff 工具接上大模型能力。比如让模型对差异段落做一次语义归纳,或者对代码差异做一次影响面分析。而接入 AI 能力最怕的就是配置散落各处、Key 管理混乱、换个模型就要改一遍代码。TaoToken 在这里的作用,就是提供一个统一的 Key 和 API 通道,让 Delphi 桌面端和 HTML 前端都能走同一套配置,不用为每个模型单独适配。
这篇面向的是用 Delphi 做桌面工具、前端用 HTML+BootStrap 渲染结果的开发者。目标很明确:给你一份可复制的 config.toml 和 settings.json 骨架,再演示一次差异比对请求的验证动作,让 Diff 工具接入 AI 能力这件事配置一次就能跑通。
2. TaoToken 前置准备:统一 Key 与 API 通道
在动手改 Delphi 代码之前,先把 TaoToken 这边的准备工作做完。核心就两件事:拿到 Key,确认 API 地址。
TaoToken 的官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基础地址是 https://taotoken.net/api ,这个地址后面会写进 config.toml 和 settings.json 里,不加任何多余参数。
Key 的获取在控制台的 API Keys 页面,地址是 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。进去之后创建一个新 Key,复制出来先存好。这个 Key 就是 Delphi 端和前端共用的凭证,不需要为 Diff 工具单独申请。
如果你只是想先验证模型能不能正常对话,可以走模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,在里面直接发一条测试消息,确认 Key 和通道都通。这一步不是必须的,但能帮你排除掉「到底是 Key 问题还是代码问题」的干扰。
对于长期做编码类工具、或者要把 Diff 能力嵌进 Agent 工作流的场景,可以关注 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,里面有针对持续编码场景的配置说明。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到参数不确定的时候以文档为准。
注意:Key 不要硬编码在 Delphi 的 .pas 文件里,也不要提交到版本库。后面我们会把它放在 settings.json 中,由程序运行时读取。
3. 可复制配置:config.toml 与 settings.json 骨架
配置分两层来设计。config.toml 放在项目根目录,管的是「走哪个通道、用哪个模型、超时多久」这类环境级参数。settings.json 放在用户目录或者程序同级目录,管的是「Key 是什么、上次选了哪个模型」这类用户级参数。这样分开的好处是,换机器只需要改 settings.json,通道和模型策略不用动。
先看 config.toml 的骨架:
# config.toml - Diff 工具 AI 通道配置 [api] base_url = "https://taotoken.net/api" chat_path = "/v1/chat/completions" timeout_seconds = 60 max_retries = 2 [model] default = "claude-3-5-sonnet" fallback = "gpt-4o-mini" max_tokens = 2048 temperature = 0.2 [diff] # 差异段落超过这个字符数才送 AI 分析,避免小改动也调用 min_chunk_chars = 80 # 单次最多送多少个差异块 max_chunks_per_request = 5 # 是否对代码差异做语法感知 code_aware = true [ui] # HTML 模板路径,相对程序目录 template = "templates/diff_template.html" # 渲染用的浏览器控件 browser = "TWebBrowser"再看 settings.json 的骨架:
{ "api_key": "在这里填入你的 TaoToken Key", "base_url": "https://taotoken.net/api", "model": "claude-3-5-sonnet", "last_diff_file": "", "encoding": "utf-8", "theme": "bootstrap", "ai_analysis_enabled": true, "chunk_summary": true }Delphi 端读取这两个文件的顺序建议是:先读 config.toml 拿到 base_url 和模型策略,再读 settings.json 拿到 api_key 和用户偏好。如果 settings.json 里的 base_url 为空,就用 config.toml 里的值兜底。这样即使前端页面单独调用,也能从同一份配置里取到地址。
HTML 模板这边,保留你原来的 BootStrap 骨架,只加一个用于展示 AI 分析结果的占位区域:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="bootstrap.min.css"> <style> .bg-green { background-color: #AAFFAA; } .bg-red { background-color: #FFAAAA; } .bg-yellow { background-color: #FFFFAA; } .ai-panel { border-left: 4px solid #0d6efd; padding: 8px 12px; margin: 12px 0; } </style> </head> <body> <div class="container"> <pre id="diff-content">{{DIFF_CONTENT}}</pre> <div id="ai-analysis" class="ai-panel">{{AI_ANALYSIS}}</div> </div> </body> </html>模板里的{{DIFF_CONTENT}}和{{AI_ANALYSIS}}就是替换标记,Delphi 加载模板后做字符串替换,再另存为临时 html 交给 TWebBrowser 打开。这里 charset 保持 utf-8,如果你的源文本是 gb2312,要么转码后再替换,要么在 WebBrowser 里手动切编码。
4. 验证请求:一次差异比对请求的完整动作
配置写好了,接下来验证通道是否真的通。不要一上来就在 Delphi 里写完整调用逻辑,先用一个最小的请求确认 Key 和地址没问题。
在命令行里用 curl 发一条请求,把两段有差异的文本送进去,让模型做一次差异归纳:
curl -X POST "https://taotoken.net/api/v1/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer 你的TaoTokenKey" \ -d '{ "model": "claude-3-5-sonnet", "max_tokens": 512, "temperature": 0.2, "messages": [ { "role": "system", "content": "你是一个文本差异分析助手。用户会给你两段文本,请用一句话说明它们的核心差异,并指出是否有语义变化。" }, { "role": "user", "content": "文本A:系统在用户登录失败三次后锁定账户。\n文本B:系统在用户登录失败五次后锁定账户。" } ] }'如果返回的 JSON 里 choices 数组有内容,说明通道通了。返回内容大概会指出「锁定阈值从三次改为五次,属于策略参数变更,语义上放宽了限制」。这一步验证的是三件事:Key 有效、base_url 正确、模型可调用。
Delphi 端对应的调用逻辑,核心是构造 JSON 请求体,用 TNetHTTPClient 或者 Indy 的 TIdHTTP 发出去。下面是一个精简的调用片段:
function CallDiffAI(const ATextA, ATextB: string): string; var HTTP: TNetHTTPClient; ReqBody: TStringStream; Resp: IHTTPResponse; JsonReq, JsonResp: TJSONObject; Messages: TJSONArray; Msg: TJSONObject; begin HTTP := TNetHTTPClient.Create(nil); try HTTP.CustomHeaders['Authorization'] := 'Bearer ' + GetApiKeyFromSettings; HTTP.ContentType := 'application/json'; HTTP.ConnectionTimeout := 60000; JsonReq := TJSONObject.Create; try JsonReq.AddPair('model', GetModelFromConfig); JsonReq.AddPair('max_tokens', TJSONNumber.Create(512)); JsonReq.AddPair('temperature', TJSONNumber.Create(0.2)); Messages := TJSONArray.Create; Msg := TJSONObject.Create; Msg.AddPair('role', 'system'); Msg.AddPair('content', '你是一个文本差异分析助手,请归纳两段文本的核心差异。'); Messages.AddElement(Msg); Msg := TJSONObject.Create; Msg.AddPair('role', 'user'); Msg.AddPair('content', '文本A:' + ATextA + #10 + '文本B:' + ATextB); Messages.AddElement(Msg); JsonReq.AddPair('messages', Messages); ReqBody := TStringStream.Create(JsonReq.ToString, TEncoding.UTF8); try Resp := HTTP.Post(GetBaseUrl + '/v1/chat/completions', ReqBody); Result := Resp.ContentAsString(TEncoding.UTF8); finally ReqBody.Free; end; finally JsonReq.Free; end; finally HTTP.Free; end; end;拿到返回的 JSON 后,解析出 choices[0].message.content,替换到 HTML 模板的{{AI_ANALYSIS}}位置,再用 TWebBrowser 加载。这样一次完整的「Diff 计算 → AI 分析 → HTML 渲染」链路就跑通了。
验证成功的标志是:TWebBrowser 里能看到带背景色的差异段落,下方 AI 分析面板里有一段针对差异的语义说明。如果只看到差异没有 AI 分析,说明请求环节有问题,回到 curl 那一步排查。
5. 本篇常见错排查
配置跑不通的时候,问题通常集中在几个地方。下面按出现频率从高到低排。
Key 无效或过期。表现是返回 401 或者 JSON 里带 error 字段。先确认 settings.json 里的 api_key 没有多余空格,再确认这个 Key 在控制台里是启用状态。如果 Key 刚创建,等几秒再试。
base_url 写错。常见的是多写了斜杠或者漏了 /api。正确写法是https://taotoken.net/api,后面拼/v1/chat/completions。如果你在 config.toml 里把 base_url 写成https://taotoken.net/api/,拼接后会变成双斜杠,部分 HTTP 客户端会报错。
中文乱码。这是 Diff 工具接 HTML 渲染时的老问题。模板 charset 是 utf-8,但源文本是 gb2312 的时候,替换进去就会乱。解决办法有两个:一是在 Delphi 里先把源文本转成 utf-8 再替换;二是保持模板 charset 不变,在 TWebBrowser 里右键切换编码。推荐第一种,从源头统一编码。
请求超时。差异文本很长的时候,模型响应会慢。config.toml 里的 timeout_seconds 默认 60,如果经常超时,可以调到 120,同时把 max_chunks_per_request 调小,分批送。
TWebBrowser 不渲染样式。BootStrap 的 css 文件路径要用绝对路径或者正确的相对路径。如果模板和 css 不在同一目录,TWebBrowser 加载临时 html 时会找不到样式。建议把 bootstrap.min.css 和模板放同一目录,或者用 file:// 绝对路径引用。
AI 分析结果为空。检查返回 JSON 的解析逻辑,有些模型返回的 content 是数组结构而不是字符串,需要按实际返回格式取。另外确认 max_tokens 没有设得太小,512 一般够用,太小会导致内容被截断。
提示:排查的时候先把 AI 分析关掉,只验证 Diff 计算和 HTML 渲染。这两步通了,再打开 AI 分析,问题范围就缩小到请求环节了。
6. 接入方式选择与后续动作
Diff 工具接入 AI 能力,配置一次跑通之后,后面就是按场景选通道。如果你主要是做排障和接入调试,重点看 API Keys 和接入文档,Key 管理在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,参数细节在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
如果你只是想验证某个模型对差异文本的理解能力,直接走模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,把两段文本贴进去对比效果,比在代码里反复改参数快得多。
如果你要把 Diff 能力做成长期运行的编码辅助工具,或者嵌进 Agent 工作流里持续调用,那就走 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,里面有面向持续编码场景的配置建议。
Claude Code 相关的接入说明在 https://taotoken.net/claude-code?utm_source=taotoken_aicg_blog_end&utm_content=claude-code&utm_campaign=rewrite ,如果你用 Claude 系列模型做代码差异分析,可以参考那边的配置方式。
实际用下来,Diff 工具接 AI 最值得花时间的地方不是调用本身,而是差异块的分割策略。把大段差异切成语义完整的小块再送模型,分析质量比整段送进去高很多。config.toml 里的 min_chunk_chars 和 max_chunks_per_request 就是干这个的,建议根据你的文档类型调一调,代码差异和自然语言差异的最优值不一样。