能自动生成代码的AI对话框
本节内容参考CaseStudy目录下的文件"LLMCaller.Table"的窗口"自动生成Foxtable代码"。
我相信很多用户已经使用过FoxtableCoder技能库来自动生成Foxtable代码,大部分用户可能用的都是Trae和Workbuddy这种智能体软件。
其实有了上一节使用技能库的知识,我们已经知道自动生成代码根本不是什么黑科技,所以根本不需要借助Trae和Workbuddy,完全可以自己写一个,而且代码非常简单。
本节的任务就是设计一个AI对话框,能根据用户输入的问题自动生成代码,也能分析用户上传的代码文件,而且关闭对话框后,下次重新打开,可以延续之前的上下文继续对话:

设计步骤
1、将窗口的AfterLoad事件代码中初始化LLMCaller的代码修改如下,加上了读取技能库和参考文件的代码(来自上一节):
'''Async
'1、初始化LLMCaller
Vars("apikey")
=
"sk-a734b83536da45e9aedc3e42605bfb3a"
'请替换为自己的密钥
Dim
caller
As
New
LLMCaller("https://api.deepseek.com/chat/completions",
"deepseek-v4-pro",
Vars("apikey"))
caller.stream =
True
'开启流式输出
caller.Messages =
New
JArray()
'开启多轮对话
Vars("caller")
= caller
'将初始化好的LLMCaller存放在Var变量中,方便复用
'2.
读取总控路由文件(根目录的
SKILL.md)
Dim
baseDir
As
String
= System.IO.Path.Combine(ProjectPath,
"coder")
'
技能库根目录
Dim
routerPath
As
String
= System.IO.Path.Combine(baseDir,
"SKILL.md")
Dim
routerContent
As
String
=
""
If
System.IO.File.Exists(routerPath)
Then
routerContent = System.IO.File.ReadAllText(routerPath)
routerContent =
"```markdown"
& vbcrlf & routerContent & vbCrLf &
"```"
'路由用Markdown块包裹,提高AI识别率
Else
MessageBoxA.Show("找不到核心路由文件:SKILL.md")
Return
End
If
'3.
将路由文件内容作为系统提示词(SystemPrompt)
caller.SystemPrompt =
"你是一个资深的
Foxtable
代码开发专家。"
& vbCrLf &
"下面是你的【总控路由表与语法约定】(必须严格遵守):"
& vbCrLf &
routerContent & vbCrLf & vbCrLf &
"===
行动指南
==="
& vbCrLf &
"1.
遇到用户的需求,请先根据上述【路由表】匹配对应的技能名称。"
& vbCrLf &
"2.
调用
get_skill_content
获取该技能的详细说明。"
& vbCrLf &
"3.
技能的
SKILL.md
末尾通常有一个【参考文档】表格。如果需要查阅更多细节,请调用
read_reference
读取表格中列出的对应参考文件"
'4.
定义两个工具,分别用于读取技能文件和参考文件
With
caller.AddTool("get_skill_content",
"获取指定技能的核心
SKILL.md
说明文件。")
.AddParameter("skill_name",
"string",
"技能名称(即目录名),必须从
SystemPrompt
的路由表中提取。",
True)
End
With
With
caller.AddTool("read_reference",
"根据技能的
SKILL.md
末尾的《参考文档》表格,读取具体的参考文件内容。")
.AddParameter("skill_name",
"string",
"技能名称(即目录名)。",
True)
.AddParameter("file_name",
"string",
"参考文件名,例如
'01-基础知识.md'。",
True)
End
With
caller.ToolExecutor =
Function(ttoolName
As
String,
targs
As
JObject)
As
String
Select
Case
ttoolName
Case
"get_skill_content"
'读取技能文件
Dim
skillName
As
String
= targs("skill_name").ToString()
Dim
filePath
As
String
= System.IO.Path.Combine(baseDir, skillName,
"SKILL.md")
If
FileSys.FileExists(filePath)
Then
Return
FileSys.ReadAllText(filePath)
Else
Return
"{""error"":""未找到该技能的
SKILL.md
文件,请检查技能名是否拼写正确。""}"
End
If
Case
"read_reference"
'读取参考文件
Dim
skillName
As
String
= targs("skill_name").ToString()
Dim
fileName
As
String
= targs("file_name").ToString()
Dim
filePath
As
String
= System.IO.Path.Combine(baseDir, skillName,
"references",
fileName)
If
FileSys.FileExists(filePath)
Then
Return
FileSys.ReadAllText(filePath)
Else
Return
"{""error"":""未找到指定的参考文件。请检查文件名是否与《参考文档》表格中完全一致。""}"
End
If
Case
Else
Return
"未知工具:"
& ttoolName
End
Select
End
Function
2、在窗口的AfterClose事件加上下面的代码,用于将对话上下文保存在本地文件:
If
Vars("caller")
IsNot
Nothing
Then
'持久化上下文到本地文件,注意不同的对话使用不同的文件名,避免冲突
Dim
caller
As
LLMCaller = Vars("caller")
If
caller.Messages
IsNot
Nothing
Then
FileSys.WriteAllText(ProjectPath &
"aiCoder.txt",
caller.Messages.ToString(),
False)
'
End
If
End
If
3、在窗口的AfterLoad事件的最后面加上下面的代码,用于读取并显示保存在本地的对话上下文:
' 10.
将历史对话内容加载到WebViewer控件中显示
If
FileSys.FileExists(ProjectPath &
"aiCoder.txt")
Then
'
读取存储在本地的历史上下文
Try
Dim
msgstr
As
String
= FileSys.ReadAllText(ProjectPath &
"aiCoder.txt")
caller.Messages = JsonConvert.DeserializeObject(Of
JArray)(msgstr)
If
Await
wv.NavigationTaskAsync(10) = 0
Then
Dim
lastRole
As
String
=
""
For
Each
msg
As
JToken
In
caller.Messages
If
msg("role")
IsNot
Nothing
Then
Dim
role
As
String
= msg("role").ToString
Select
Case
role
Case
"user"
If
msg("content")
IsNot
Nothing
Then
Dim
content
As
String
= HtmlEncode(msg("content").ToString()).Replace(vbCrLf,
"<br>").Replace(vbLf,
"<br>")
Await
wv.ExecuteScriptAsync("addMessage('user',
"
& JsonConvert.ToString(content) &
")")
End
If
Case
"assistant"
Dim
content
As
String
=
""
If
msg("content")
IsNot
Nothing
Then
content = msg("content").ToString()
Dim
think
As
String
=
""
If
msg("reasoning_content")
IsNot
Nothing
Then
think = msg("reasoning_content").ToString()
'
关键:只有上一轮已经结束(上一条是
user)或这是第一条消息时,才创建新气泡
If
lastRole =
"user"
OrElse
lastRole =
""
Then
Await
wv.ExecuteScriptAsync("addMessage('assistant',
'')")
End
If
If
think <>
""
Then
think = HtmlEncode(think).Replace(vbCrLf,
"<br>").Replace(vbLf,
"<br>")
Await
wv.ExecuteScriptAsync("appendThink("
& JsonConvert.ToString(think) &
")")
End
If
If
content <>
""
Then
Await
wv.ExecuteScriptAsync("appendAnswer("
& JsonConvert.ToString(content) &
")")
End
If
Case
"tool"
' tool
消息不显示
End
Select
lastRole = role
End
If
Next
End
If
Catch
ex
As
Exception
caller.Messages =
New
JArray()
'如果文件读取或格式转换出错
End
Try
End
If
4、上传按钮的代码如下,这里只接受Markdown格式的代码文件,你可以根据需要调整:
Dim
dlg
As
New
OpenFileDialog()
dlg.Multiselect =
True
dlg.Filter =
"MarkDown文件(*.md)|*.md"
If
dlg.ShowDialog() = DialogResult.OK
Then
Dim
lstFiles
As
WinForm.ListBox = e.Form.Controls("lstFiles")
lstFiles.Items.Clear()
For
Each
fileName
As
String
In
dlg.FileNames
If
Not
fileName.EndsWith(".md",
StringComparison.OrdinalIgnoreCase)
Then
MessageBox.Show("只接受MarkDown格式的代码文件",
"提示",
MessageBoxButtons.OK, MessageBoxIcon.Warning)
Continue
For
End
If
lstFiles.Items.Add(fileName)
Next
End
If
5、发送按钮代码只需调整第一步(设置用户问题)的代码,需要将用户上传的Markdown格式的代码作为用户问题的一部分提交给AI:
'''Async
Dim
wv
As
WebViewer = e.Form.Controls("WebViewer1").WebViewer
Dim
inputBox
As
WinForm.TextBox = e.Form.Controls("TextBox1")
Dim
userMsg
As
String
= inputBox.Text.Trim()
'获取用户输入的问题
If
userMsg =
""
Then
Return
inputBox.Text =
""
'清空输入框
' ---- 1、设置用户问题(变化的只有这部分)
----
Dim
aiMsg
As
String
= userMsg
'分离“发给
AI 的内容(aiMsg)”与“显示在界面的内容(userMsg)”
Dim
caller
As
LLMCaller = Vars("caller")
Dim
lstFiles
As
WinForm.ListBox = e.Form.Controls("lstFiles")
If
lstFiles.Items.Count > 0
Then
'
判断是否有上传的Markdown格式的代码文件
Dim
sb
As
New
StringBuilder(aiMsg & vbCrLf & vbCrLf)
'用户问题和代码之间加一个空行,有利于大模型理解
For
Each
file
As
String
In
lstFiles.Items
If
FileSys.FileExists(file)
Then
sb.AppendLine("====="
& FileSys.GetName(file) &
"内容:=====")
sb.AppendLine("```markdown")
'用Markdown块包裹用户上传的代码,方便大模型理解
sb.AppendLine(FileSys.ReadAllText(file))
sb.AppendLine("```"
& vbCrLf)
End
If
Next
aiMsg = sb.ToString()
End
If
caller.UserMessage = aiMsg
后面的代码没有任何变化。