Files
LaodingBot/internal/knowledge/drafts.go

142 lines
3.8 KiB
Go
Raw Normal View History

package knowledge
import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
"laodingbot/internal/memory"
)
func GenerateSkillDraft(cluster memory.CapabilityGapCluster, draftRoot string) (string, bool, error) {
draftRoot = strings.TrimSpace(draftRoot)
if draftRoot == "" {
draftRoot = "./skills"
}
if err := os.MkdirAll(draftRoot, 0o755); err != nil {
return "", false, err
}
skillDirName := "auto_" + slugFromIntent(cluster.IntentKey)
if skillDirName == "" {
skillDirName = "auto_gap_skill"
}
dir := filepath.Join(draftRoot, skillDirName)
file := filepath.Join(dir, "skill.md")
if _, err := os.Stat(file); err == nil {
return file, false, nil
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", false, err
}
skillTitle := titleFromIntent(cluster.SampleIntent)
if skillTitle == "" {
skillTitle = "能力缺口补全技能"
}
content := buildDraftMarkdown(skillTitle, cluster)
if err := os.WriteFile(file, []byte(content), 0o644); err != nil {
return "", false, err
}
return file, true, nil
}
func buildDraftMarkdown(skillTitle string, cluster memory.CapabilityGapCluster) string {
createdAt := time.Now().Format(time.RFC3339)
return strings.TrimSpace(fmt.Sprintf(`---
name: %s
description: capability_gap 自动生成并用于自动补全缺失能力
source: capability_gap
generated_at: %s
cluster_intent_key: %s
cluster_reason: %s
cluster_count: %d
---
# Skill: %s
## 背景
- 该技能由系统根据高频能力缺口自动生成并已纳入技能目录
- 最近高频缺口聚类`+"`%s`"+`
- 缺口原因`+"`%s`"+`
- 出现次数`+"`%d`"+`
## 目标能力
- 明确该类问题应如何判断是否需要调用工具
- 约束输入输出避免泛化过度
- 在失败时提供可操作回退路径
## 建议触发信号
- 用户提问与下述意图高度相关`+"`%s`"+`
- 现有技能未命中或命中后无法完成
## 建议工具
- 优先使用现有工具`+"`shell`"+``+"`file`"+``+"`web_search`"+`
- 若能力不足需要创建新工具时
1. `+"`tools/<tool_name>/`"+` 下生成 Go 代码实现 Name/Description/Call 接口
2. `+"`internal/toolhost/runtime.go`"+` 中注册新工具
3. 生成/补充 `+"`*_test.go`"+`
4. 调用 `+"`go test ./...`"+` 验证
## ReAct 指南
1. 先确认用户目标和输入约束
2. 判断是否可直接回答若不行再选择工具
3. 工具调用前先最小化探测范围
4. 工具失败时输出原因与下一步建议
5. 若缺少 skill使用 `+"`file`"+` `+"`shell`"+` 创建新的 `+"`skills/<skill_name>/skill.md`"+`
6. 若缺少 tool `+"`tools/<tool_name>/`"+` 下生成工具代码与测试后执行 `+"`go test ./...`"+`
## 输出规范
- 结论一句话给出当前阶段结论
- 依据列出关键观察与证据
- 限制说明当前不确定性
- 下一步给用户可执行动作
`, skillTitle, createdAt, cluster.IntentKey, cluster.Reason, cluster.Count, skillTitle, cluster.IntentKey, cluster.Reason, cluster.Count, cluster.SampleIntent))
}
func slugFromIntent(intent string) string {
intent = strings.TrimSpace(strings.ToLower(intent))
if intent == "" {
return ""
}
b := strings.Builder{}
lastDash := false
for _, r := range intent {
isAlphaNum := (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9')
isCJK := r >= 0x4e00 && r <= 0x9fff
if isAlphaNum || isCJK {
b.WriteRune(r)
lastDash = false
continue
}
if !lastDash {
b.WriteRune('-')
lastDash = true
}
}
out := strings.Trim(b.String(), "-")
if out == "" {
return ""
}
runes := []rune(out)
if len(runes) > 48 {
out = string(runes[:48])
}
return out
}
func titleFromIntent(intent string) string {
intent = strings.TrimSpace(intent)
if intent == "" {
return ""
}
runes := []rune(intent)
if len(runes) > 32 {
intent = string(runes[:32])
}
return intent
}