Delphi与大型语言模型应用:从理论到实践
大型语言模型(LLM)如ChatGPT、Claude和Llama等正在彻底改变软件开发和用户交互方式。本文将探讨如何在Delphi应用中集成和利用这些强大的AI模型,从理论基础到实际应用案例。
大型语言模型简介
大型语言模型是基于Transformer架构的深度学习模型,通过大规模文本数据训练而成。它们具有以下特点:
- 理解和生成自然语言:能够理解用户输入并生成连贯、相关的文本
- 上下文学习:能够在对话中保持上下文连贯性
- 知识库:包含训练数据中的广泛知识
- 任务多样性:可用于文本生成、翻译、摘要、问答等多种任务
为什么在Delphi应用中集成LLM?
将LLM集成到Delphi应用中可以带来以下优势:
- 增强用户交互:提供自然语言界面,简化复杂操作
- 自动化内容生成:自动生成报告、描述、电子邮件等内容
- 智能辅助功能:提供上下文相关的建议和帮助
- 数据分析和洞察:从非结构化数据中提取见解
- 个性化体验:根据用户偏好和行为定制内容和功能
技术准备
在开始集成LLM之前,你需要准备以下内容:
- Delphi开发环境:建议使用Delphi 10.4或更高版本
- API密钥:如OpenAI API、Anthropic API或其他LLM服务提供商的密钥
- REST客户端组件:用于API调用,如Indy、REST Debugger或TRESTClient
- JSON处理库:如System.JSON或其他第三方JSON库
- 基础NLP知识:了解提示工程(Prompt Engineering)的基本概念
基础:创建通用LLM客户端
首先,我们创建一个通用的LLM客户端类,支持不同的模型提供商:
unit LLMClient;
interface
uses
System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient,
System.JSON, System.Generics.Collections;
type
TLLMProvider = (lpOpenAI, lpAnthropic, lpGoogle);
TLLMMessage = record
Role: string;
Content: string;
constructor Create(const ARole, AContent: string);
end;
TLLMClient = class
private
FApiKey: string;
FProvider: TLLMProvider;
FModel: string;
FMessages: TList<TLLMMessage>;
FTemperature: Double;
FMaxTokens: Integer;
FHttpClient: THTTPClient;
function GetEndpoint: string;
function BuildRequestBody: TJSONObject;
function ParseResponse(const ResponseContent: string): string;
public
constructor Create(const ApiKey: string; Provider: TLLMProvider);
destructor Destroy; override;
function SendMessage(const UserMessage: string): string;
procedure ClearConversation;
property Provider: TLLMProvider read FProvider write FProvider;
property Model: string read FModel write FModel;
property Temperature: Double read FTemperature write FTemperature;
property MaxTokens: Integer read FMaxTokens write FMaxTokens;
end;
implementation
{ TLLMMessage }
constructor TLLMMessage.Create(const ARole, AContent: string);
begin
Role := ARole;
Content := AContent;
end;
{ TLLMClient }
constructor TLLMClient.Create(const ApiKey: string; Provider: TLLMProvider);
begin
inherited Create;
FApiKey := ApiKey;
FProvider := Provider;
FMessages := TList<TLLMMessage>.Create;
FHttpClient := THTTPClient.Create;
// 设置默认值
FTemperature := 0.7;
FMaxTokens := 1000;
// 根据提供商设置默认模型
case Provider of
lpOpenAI: FModel := 'gpt-3.5-turbo';
lpAnthropic: FModel := 'claude-2';
lpGoogle: FModel := 'gemini-pro';
end;
end;
destructor TLLMClient.Destroy;
begin
FMessages.Free;
FHttpClient.Free;
inherited;
end;
function TLLMClient.GetEndpoint: string;
begin
case FProvider of
lpOpenAI: Result := 'https://api.openai.com/v1/chat/completions';
lpAnthropic: Result := 'https://api.anthropic.com/v1/messages';
lpGoogle: Result := 'https://generativelanguage.googleapis.com/v1beta/models/' + FModel + ':generateContent';
else
raise Exception.Create('不支持的LLM提供商');
end;
end;
function TLLMClient.BuildRequestBody: TJSONObject;
var
RequestObj, MessageObj: TJSONObject;
MessagesArray: TJSONArray;
Message: TLLMMessage;
begin
RequestObj := TJSONObject.Create;
try
case FProvider of
lpOpenAI:
begin
RequestObj.AddPair('model', FModel);
RequestObj.AddPair('temperature', TJSONNumber.Create(FTemperature));
RequestObj.AddPair('max_tokens', TJSONNumber.Create(FMaxTokens));
MessagesArray := TJSONArray.Create;
for Message in FMessages do
begin
MessageObj := TJSONObject.Create;
MessageObj.AddPair('role', Message.Role);
MessageObj.AddPair('content', Message.Content);
MessagesArray.AddElement(MessageObj);
end;
RequestObj.AddPair('messages', MessagesArray);
end;
lpAnthropic:
begin
RequestObj.AddPair('model', FModel);
RequestObj.AddPair('temperature', TJSONNumber.Create(FTemperature));
RequestObj.AddPair('max_tokens', TJSONNumber.Create(FMaxTokens));
MessagesArray := TJSONArray.Create;
for Message in FMessages do
begin
MessageObj := TJSONObject.Create;
MessageObj.AddPair('role', Message.Role);
MessageObj.AddPair('content', Message.Content);
MessagesArray.AddElement(MessageObj);
end;
RequestObj.AddPair('messages', MessagesArray);
end;
lpGoogle:
begin
RequestObj.AddPair('temperature', TJSONNumber.Create(FTemperature));
RequestObj.AddPair('maxOutputTokens', TJSONNumber.Create(FMaxTokens));
MessagesArray := TJSONArray.Create;
for Message in FMessages do
begin
MessageObj := TJSONObject.Create;
MessageObj.AddPair('role', Message.Role);
MessageObj.AddPair('parts', TJSONArray.Create(TJSONObject.Create
.AddPair('text', Message.Content)));
MessagesArray.AddElement(MessageObj);
end;
RequestObj.AddPair('contents', MessagesArray);
end;
end;
Result := RequestObj;
except
RequestObj.Free;
raise;
end;
end;
function TLLMClient.ParseResponse(const ResponseContent: string): string;
var
ResponseObj: TJSONObject;
ChoicesArray: TJSONArray;
begin
Result := '';
ResponseObj := TJSONObject.ParseJSONValue(ResponseContent) as TJSONObject;
try
case FProvider of
lpOpenAI:
begin
ChoicesArray := ResponseObj.GetValue('choices') as TJSONArray;
if (ChoicesArray <> nil) and (ChoicesArray.Count > 0) then
Result := (ChoicesArray.Items[0] as TJSONObject)
.GetValue<TJSONObject>('message')
.GetValue<string>('content');
end;
lpAnthropic:
begin
if ResponseObj.GetValue('content') <> nil then
Result := (ResponseObj.GetValue<TJSONArray>('content').Items[0] as TJSONObject)
.GetValue<string>('text');
end;
lpGoogle:
begin
if ResponseObj.GetValue('candidates') <> nil then
Result := ((ResponseObj.GetValue<TJSONArray>('candidates').Items[0] as TJSONObject)
.GetValue<TJSONArray>('content').Items[0] as TJSONObject)
.GetValue<TJSONArray>('parts').Items[0].GetValue<string>('text');
end;
end;
finally
ResponseObj.Free;
end;
end;
function TLLMClient.SendMessage(const UserMessage: string): string;
var
URL: string;
RequestBody: TJSONObject;
Response: IHTTPResponse;
ResponseContent: string;
begin
Result := '';
URL := GetEndpoint;
// 添加用户消息到历史
FMessages.Add(TLLMMessage.Create('user', UserMessage));
RequestBody := BuildRequestBody;
try
// 设置请求头
FHttpClient.CustomHeaders.Clear;
FHttpClient.CustomHeaders['Content-Type'] := 'application/json';
case FProvider of
lpOpenAI:
FHttpClient.CustomHeaders['Authorization'] := 'Bearer ' + FApiKey;
lpAnthropic:
FHttpClient.CustomHeaders['x-api-key'] := FApiKey;
lpGoogle:
URL := URL + '?key=' + FApiKey;
end;
// 发送请求
Response := FHttpClient.Post(URL, TStringStream.Create(RequestBody.ToJSON), nil);
ResponseContent := Response.ContentAsString;
// 解析响应
if Response.StatusCode = 200 then
begin
Result := ParseResponse(ResponseContent);
// 添加AI回复到历史
FMessages.Add(TLLMMessage.Create('assistant', Result));
end
else
raise Exception.CreateFmt('API错误: %d - %s', [Response.StatusCode, ResponseContent]);
finally
RequestBody.Free;
end;
end;
procedure TLLMClient.ClearConversation;
begin
FMessages.Clear;
end;
end.
中级:提示工程与上下文管理
提示工程是使用LLM的关键技术,通过精心设计的提示可以引导模型生成更好的结果:
unit PromptEngineering;
interface
uses
System.SysUtils, System.Classes, System.Generics.Collections;
type
TPromptTemplate = class
private
FTemplate: string;
FVariables: TDictionary<string, string>;
public
constructor Create(const Template: string);
destructor Destroy; override;
procedure SetVariable(const Name, Value: string);
function Render: string;
end;
TConversationManager = class
private
FSystemPrompt: string;
FConversationHistory: TList<TPair<string, string>>;
FMaxHistoryLength: Integer;
public
constructor Create(const SystemPrompt: string);
destructor Destroy; override;
procedure AddExchange(const UserMessage, AIResponse: string);
function GetFormattedConversation: string;
procedure ClearHistory;
property SystemPrompt: string read FSystemPrompt write FSystemPrompt;
property MaxHistoryLength: Integer read FMaxHistoryLength write FMaxHistoryLength;
end;
implementation
{ TPromptTemplate }
constructor TPromptTemplate.Create(const Template: string);
begin
inherited Create;
FTemplate := Template;
FVariables := TDictionary<string, string>.Create;
end;
destructor TPromptTemplate.Destroy;
begin
FVariables.Free;
inherited;
end;
procedure TPromptTemplate.SetVariable(const Name, Value: string);
begin
FVariables.AddOrSetValue(Name, Value);
end;
function TPromptTemplate.Render: string;
var
Result: string;
Pair: TPair<string, string>;
begin
Result := FTemplate;
for Pair in FVariables do
Result := StringReplace(Result, '{' + Pair.Key + '}', Pair.Value, [rfReplaceAll]);
Result := Result;
end;
{ TConversationManager }
constructor TConversationManager.Create(const SystemPrompt: string);
begin
inherited Create;
FSystemPrompt := SystemPrompt;
FConversationHistory := TList<TPair<string, string>>.Create;
FMaxHistoryLength := 10; // 默认保留最近10轮对话
end;
destructor TConversationManager.Destroy;
begin
FConversationHistory.Free;
inherited;
end;
procedure TConversationManager.AddExchange(const UserMessage, AIResponse: string);
begin
FConversationHistory.Add(TPair<string, string>.Create(UserMessage, AIResponse));
// 如果历史记录超过最大长度,删除最早的记录
while FConversationHistory.Count > FMaxHistoryLength do
FConversationHistory.Delete(0);
end;
function TConversationManager.GetFormattedConversation: string;
var
SB: TStringBuilder;
Exchange: TPair<string, string>;
begin
SB := TStringBuilder.Create;
try
// 添加系统提示
if not FSystemPrompt.IsEmpty then
begin
SB.AppendLine('系统: ' + FSystemPrompt);
SB.AppendLine;
end;
// 添加对话历史
for Exchange in FConversationHistory do
begin
SB.AppendLine('用户: ' + Exchange.Key);
SB.AppendLine('助手: ' + Exchange.Value);
SB.AppendLine;
end;
Result := SB.ToString;
finally
SB.Free;
end;
end;
procedure TConversationManager.ClearHistory;
begin
FConversationHistory.Clear;
end;
end.
高级:创建特定领域的LLM应用
以下是如何创建特定领域的LLM应用,以代码助手为例:
unit CodeAssistant;
interface
uses
System.SysUtils, System.Classes, LLMClient, PromptEngineering;
type
TCodeAssistantMode = (camExplain, camRefactor, camGenerate, camDebug);
TCodeAssistant = class
private
FLLMClient: TLLMClient;
FPromptTemplates: TDictionary<TCodeAssistantMode, TPromptTemplate>;
function GetModePrompt(Mode: TCodeAssistantMode; const Code, Language, AdditionalContext: string): string;
public
constructor Create(const ApiKey: string);
destructor Destroy; override;
function ExplainCode(const Code, Language: string): string;
function RefactorCode(const Code, Language, Requirements: string): string;
function GenerateCode(const Description, Language, Requirements: string): string;
function DebugCode(const Code, Language, ErrorMessage: string): string;
end;
implementation
{ TCodeAssistant }
constructor TCodeAssistant.Create(const ApiKey: string);
begin
inherited Create;
FLLMClient := TLLMClient.Create(ApiKey, lpOpenAI);
FLLMClient.Model := 'gpt-4';
FLLMClient.Temperature := 0.3; // 较低的温度,更确定性的输出
// 初始化提示模板
FPromptTemplates := TDictionary<TCodeAssistantMode, TPromptTemplate>.Create;
// 解释代码模板
FPromptTemplates.Add(camExplain, TPromptTemplate.Create(
'请详细解释以下{language}代码的功能和工作原理,包括关键算法、数据结构和设计模式。' + sLineBreak +
'使用通俗易懂的语言,并指出代码中的重要部分。' + sLineBreak + sLineBreak +
'```{language}' + sLineBreak +
'{code}' + sLineBreak +
'```'));
// 重构代码模板
FPromptTemplates.Add(camRefactor, TPromptTemplate.Create(
'请重构以下{language}代码,使其更加清晰、高效和易于维护。' + sLineBreak +
'重构要求:{requirements}' + sLineBreak +
'请保持代码的功能不变,并解释你所做的改变。' + sLineBreak + sLineBreak +
'```{language}' + sLineBreak +
'{code}' + sLineBreak +
'```'));
// 生成代码模板
FPromptTemplates.Add(camGenerate, TPromptTemplate.Create(
'请根据以下描述,生成{language}代码:' + sLineBreak +
'{code}' + sLineBreak + sLineBreak +
'要求:{requirements}' + sLineBreak +
'请提供完整、可运行的代码,并添加必要的注释。'));
// 调试代码模板
FPromptTemplates.Add(camDebug, TPromptTemplate.Create(
'请帮我调试以下{language}代码,找出并修复错误:' + sLineBreak +
'```{language}' + sLineBreak +
'{code}' + sLineBreak +
'```' + sLineBreak + sLineBreak +
'错误信息:{requirements}' + sLineBreak +
'请解释错误原因,并提供修复后的代码。'));
end;
destructor TCodeAssistant.Destroy;
begin
FLLMClient.Free;
for var Template in FPromptTemplates.Values do
Template.Free;
FPromptTemplates.Free;
inherited;
end;
function TCodeAssistant.GetModePrompt(Mode: TCodeAssistantMode; const Code, Language, AdditionalContext: string): string;
var
Template: TPromptTemplate;
begin
if FPromptTemplates.TryGetValue(Mode, Template) then
begin
Template.SetVariable('code', Code);
Template.SetVariable('language', Language);
Template.SetVariable('requirements', AdditionalContext);
Result := Template.Render;
end
else
Result := '';
end;
function TCodeAssistant.ExplainCode(const Code, Language: string): string;
begin
FLLMClient.ClearConversation;
Result := FLLMClient.SendMessage(GetModePrompt(camExplain, Code, Language, ''));
end;
function TCodeAssistant.RefactorCode(const Code, Language, Requirements: string): string;
begin
FLLMClient.ClearConversation;
Result := FLLMClient.SendMessage(GetModePrompt(camRefactor, Code, Language, Requirements));
end;
function TCodeAssistant.GenerateCode(const Description, Language, Requirements: string): string;
begin
FLLMClient.ClearConversation;
Result := FLLMClient.SendMessage(GetModePrompt(camGenerate, Description, Language, Requirements));
end;
function TCodeAssistant.DebugCode(const Code, Language, ErrorMessage: string): string;
begin
FLLMClient.ClearConversation;
Result := FLLMClient.SendMessage(GetModePrompt(camDebug, Code, Language, ErrorMessage));
end;
end.
实际应用案例
1. 智能文档生成器
// 使用LLM自动生成软件文档
procedure TDocumentationGenerator.GenerateDocumentation(const SourceCode, Language: string);
var
LLMClient: TLLMClient;
Prompt, Response: string;
begin
LLMClient := TLLMClient.Create(ConfigManager.GetValue('OPENAI_API_KEY'), lpOpenAI);
try
LLMClient.Model := 'gpt-4';
LLMClient.Temperature := 0.2;
Prompt := Format(
'请为以下%s代码生成详细的技术文档,包括:' + sLineBreak +
'1. 功能概述' + sLineBreak +
'2. 参数说明' + sLineBreak +
'3. 返回值说明' + sLineBreak +
'4. 使用示例' + sLineBreak +
'5. 注意事项' + sLineBreak +
'请使用Markdown格式。' + sLineBreak + sLineBreak +
'```%s' + sLineBreak +
'%s' + sLineBreak +
'```',
[Language, Language, SourceCode]
);
Response := LLMClient.SendMessage(Prompt);
// 保存生成的文档
SaveDocumentation(Response);
// 显示结果
ShowDocumentationPreview(Response);
finally
LLMClient.Free;
end;
end;
2. 智能客户支持系统
// 使用LLM处理客户查询
function TCustomerSupportSystem.ProcessCustomerQuery(const Query: string): string;
var
LLMClient: TLLMClient;
KnowledgeBase: string;
Prompt: string;
begin
// 加载知识库
KnowledgeBase := LoadKnowledgeBase;
LLMClient := TLLMClient.Create(ConfigManager.GetValue('ANTHROPIC_API_KEY'), lpAnthropic);
try
LLMClient.Model := 'claude-2';
Prompt := Format(
'你是一个客户支持助手。请根据以下知识库信息回答客户的问题。' + sLineBreak +
'如果知识库中没有相关信息,请礼貌地表示你无法回答,并建议客户联系人工客服。' + sLineBreak + sLineBreak +
'知识库:' + sLineBreak +
'%s' + sLineBreak + sLineBreak +
'客户问题:%s',
[KnowledgeBase, Query]
);
Result := LLMClient.SendMessage(Prompt);
// 记录查询和回复
LogCustomerInteraction(Query, Result);
finally
LLMClient.Free;
end;
end;
3. 数据分析助手
// 使用LLM分析数据并生成报告
procedure TDataAnalysisAssistant.GenerateReport(const DataSummary, AnalysisRequirements: string);
var
LLMClient: TLLMClient;
Prompt, Report: string;
begin
LLMClient := TLLMClient.Create(ConfigManager.GetValue('GOOGLE_API_KEY'), lpGoogle);
try
LLMClient.Model := 'gemini-pro';
Prompt := Format(
'请根据以下数据摘要生成一份详细的分析报告。' + sLineBreak +
'分析要求:%s' + sLineBreak + sLineBreak +
'数据摘要:' + sLineBreak +
'%s' + sLineBreak + sLineBreak +
'请包含以下内容:' + sLineBreak +
'1. 执行摘要' + sLineBreak +
'2. 关键发现' + sLineBreak +
'3. 详细分析' + sLineBreak +
'4. 建议' + sLineBreak +
'5. 结论' + sLineBreak +
'请使用Markdown格式,并添加适当的图表描述。',
[AnalysisRequirements, DataSummary]
);
Report := LLMClient.SendMessage(Prompt);
// 保存报告
SaveReport(Report);
// 显示报告预览
ShowReportPreview(Report);
finally
LLMClient.Free;
end;
end;
最佳实践与注意事项
- 提示设计:精心设计提示是获得良好结果的关键,包括明确指令、上下文和格式要求
- API密钥安全:不要在代码中硬编码API密钥,使用配置文件或环境变量
- 错误处理:实现完善的错误处理机制,处理网络问题和API限制
- 成本控制:监控API使用情况,实施合理的使用限制
- 用户体验:添加加载指示器和取消选项,提升用户体验
- 内容过滤:实现内容过滤机制,防止生成不适当的内容
- 版本管理:跟踪模型版本,确保应用与模型更新兼容
结论
通过在Delphi应用中集成大型语言模型,我们可以为传统应用注入强大的AI能力,显著提升用户体验和应用功能。从简单的文本生成到复杂的智能助手,LLM为Delphi开发者提供了广阔的创新空间。
随着LLM技术的不断发展和成本的降低,我们可以期待更多创新的LLM应用出现在Delphi开发的软件中。通过持续学习和实践,Delphi开发者可以充分利用这些技术,创建更智能、更有价值的应用程序。
关于作者:付乙,资深Delphi开发者,专注于将现代技术与传统应用相结合,提升软件价值和用户体验。