-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstyle.go
More file actions
129 lines (120 loc) · 3.96 KB
/
Copy pathstyle.go
File metadata and controls
129 lines (120 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main
import (
"os"
"strings"
)
// styleBook — «мини-SFT»: примеры стиля, которые модель видит перед каждым
// ответом и подражает им. Хранится в style.txt рядом с программой.
// Это не настоящее дообучение весов, а few-shot: работает мгновенно и на CPU.
type styleBook struct {
path string
examples []styleExample
}
type styleExample struct {
when string // "" — всегда, "first" — первое сообщение, "later" — не первое
bad string // как отвечать НЕ надо (может быть пустым)
good string // как отвечать НАДО
}
func newStyleBook(path string) *styleBook {
sb := &styleBook{path: path}
sb.load()
return sb
}
func (sb *styleBook) load() {
data, err := os.ReadFile(sb.path)
if err != nil {
return // файла нет — учимся с нуля
}
sb.examples = parseStyle(string(data))
}
// parseStyle читает блоки вида:
//
// когда: первое (необязательно; ещё бывает «когда: не первое»)
// плохо: ... (необязательно)
// хорошо: ...
//
// Строки с # и всё остальное — комментарии, пропускаем.
func parseStyle(s string) []styleExample {
var out []styleExample
var bad, when string
for _, line := range strings.Split(s, "\n") {
line = strings.TrimSpace(line)
switch {
case strings.HasPrefix(line, "когда:"):
switch strings.TrimSpace(strings.TrimPrefix(line, "когда:")) {
case "первое":
when = "first"
case "не первое", "потом":
when = "later"
default:
when = ""
}
case strings.HasPrefix(line, "плохо:"):
bad = strings.TrimSpace(strings.TrimPrefix(line, "плохо:"))
case strings.HasPrefix(line, "хорошо:"):
good := strings.TrimSpace(strings.TrimPrefix(line, "хорошо:"))
if good != "" {
out = append(out, styleExample{when: when, bad: bad, good: good})
}
bad = ""
when = ""
}
}
return out
}
// whenLabel — человеческое название условия для /style.
func whenLabel(when string) string {
switch when {
case "first":
return "первое сообщение"
case "later":
return "не первое"
}
return "всегда"
}
// add дописывает новый пример в файл и в память (команда /teach).
func (sb *styleBook) add(bad, good, when string) error {
var b strings.Builder
switch when {
case "first":
b.WriteString("когда: первое\n")
case "later":
b.WriteString("когда: не первое\n")
}
if bad != "" {
b.WriteString("плохо: " + bad + "\n")
}
b.WriteString("хорошо: " + good + "\n\n")
f, err := os.OpenFile(sb.path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
defer f.Close()
if _, err := f.WriteString(b.String()); err != nil {
return err
}
sb.examples = append(sb.examples, styleExample{when: when, bad: bad, good: good})
return nil
}
// promptFor строит кусочек системного промпта с примерами,
// подходящими к ситуации: first — это первый вопрос за сессию или нет.
func (sb *styleBook) promptFor(first bool) string {
if sb == nil {
return ""
}
var b strings.Builder
for _, e := range sb.examples {
if (e.when == "first" && !first) || (e.when == "later" && first) {
continue
}
if e.bad != "" {
b.WriteString("ПЛОХО: " + e.bad + "\n")
}
b.WriteString("ХОРОШО: " + e.good + "\n")
}
if b.Len() == 0 {
return ""
}
return "Тебя дообучили на примерах стиля. Отвечай в стиле примеров ХОРОШО " +
"и никогда не отвечай как в примерах ПЛОХО:\n" + b.String()
}