能自动生成代码的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

后面的代码没有任何变化。


本页地址:http://www.foxtable.com/webhelp/topics/7238.htm