Skip to content

Commit 7557af6

Browse files
committed
feat: 新增多语言prompt支持,添加中文配置与切换能力
新增了完整的中文prompt模板体系,包括默认中文提示配置、中文实体定义与关系类型,为GraphIndexer添加了promptLang字段与UseEnglish切换方法,实现中英文prompt自动适配。
1 parent b7e0c5f commit 7557af6

6 files changed

Lines changed: 480 additions & 59 deletions

File tree

formatter/prompt.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type PromptConfig struct {
2929
Separator string
3030
}
3131

32-
// DefaultPromptConfig 默认 Prompt 配置
32+
// DefaultPromptConfig 默认 Prompt 配置(英文版)
3333
func DefaultPromptConfig() *PromptConfig {
3434
return &PromptConfig{
3535
SystemPrompt: `You are a knowledgeable assistant. Answer the user's question based strictly on the reference documents provided below.
@@ -57,6 +57,34 @@ Please answer the following question based on the reference documents above: {{.
5757
}
5858
}
5959

60+
// DefaultPromptConfigZH 默认 Prompt 配置(中文版)
61+
func DefaultPromptConfigZH() *PromptConfig {
62+
return &PromptConfig{
63+
SystemPrompt: `你是一个知识渊博的助手。请严格基于下面提供的参考文档来回答用户的问题。
64+
65+
要求:
66+
1. 你的回答必须完全基于提供的参考文档。
67+
2. 如果参考文档中没有相关信息,请明确说明:"根据提供的文档,我无法回答这个问题。"
68+
3. 不要编造或推断文档中不存在的信息。
69+
4. 你可以引用文档编号来支持你的回答。
70+
5. 使用与用户问题相同的语言回答。如果用户用中文提问,用中文回答;如果用英文提问,用英文回答,以此类推。`,
71+
ContextTemplate: `以下是相关的参考文档:
72+
73+
{{range $i, $doc := .Documents}}
74+
{{$doc}}
75+
{{end}}
76+
77+
请根据上面的参考文档回答以下问题:{{.Query}}`,
78+
DocumentTemplate: `[文档 {{.Index}}]{{if .Score}} (相关度: {{printf "%.2f" .Score}}){{end}}
79+
{{.Content}}`,
80+
IncludeScore: true,
81+
IncludeSource: true,
82+
ContentMax: 1000,
83+
MaxDocuments: 10,
84+
Separator: "\n\n---\n\n",
85+
}
86+
}
87+
6088
// PromptFormatter LLM Prompt 格式化器
6189
// 用于生成抑制幻觉的提示词
6290
type PromptFormatter struct {

indexer/graph.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,16 @@ import (
2626
"gopkg.in/yaml.v3"
2727
)
2828

29-
// minContentLength 是图索引的最小内容长度(按字符数,非 token)。
29+
// minContentLength 是图索引最小内容长度(按字符数,非 token)。
3030
// 短于此长度的文本直接静默丢弃,避免浪费 token。
3131
const minContentLength = 20
3232

33+
// Prompt language constants.
34+
const (
35+
LangEN = "en"
36+
LangZH = "zh"
37+
)
38+
3339
// IndexError 包含 LLM 索引失败的详细信息,传递给 OnFail 钩子。
3440
type IndexError struct {
3541
DocID string // 文档 ID
@@ -116,6 +122,7 @@ type GraphIndexer struct {
116122
entityDefs []EntityDef // 来自 WithSchemas 的全局实体类型定义
117123
regionEntityDefs map[string][]EntityDef // 按 regionID 隔离的实体类型定义
118124
chatClient chat.Client // 缓存的 LLM client,懒加载初始化后复用
125+
promptLang string // Prompt 模板语言: "zh"(默认) | "en"
119126

120127
// ── 统计计数器(累积值,跨多次 Add/AddFile 调用) ──
121128
entitiesCreated int // 累计写入 graphDB 的实体数量
@@ -260,11 +267,12 @@ func New(
260267
model.MaxTokens = defaultMaxTokens
261268
}
262269
idx := &GraphIndexer{
263-
model: model,
264-
embedder: embedder,
265-
vectorDB: vectorDB,
266-
graphDB: graphDB,
267-
logger: logging.DefaultNoopLogger(),
270+
model: model,
271+
embedder: embedder,
272+
vectorDB: vectorDB,
273+
graphDB: graphDB,
274+
logger: logging.DefaultNoopLogger(),
275+
promptLang: LangZH,
268276
}
269277
for _, opt := range opts {
270278
opt(idx)
@@ -294,6 +302,12 @@ func (idx *GraphIndexer) CheckReady() error {
294302
return nil
295303
}
296304

305+
// UseEnglish switches prompt templates to English.
306+
// Default is Chinese. Call this to use English prompts instead.
307+
func (idx *GraphIndexer) UseEnglish() {
308+
idx.promptLang = LangEN
309+
}
310+
297311
// ---------------------------------------------------------------------------
298312
// core.Indexer 接口实现
299313
// ---------------------------------------------------------------------------
@@ -364,9 +378,9 @@ func (idx *GraphIndexer) Add(ctx context.Context, content string) ([]*core.Chunk
364378
if lang == "" {
365379
lang = "English"
366380
}
367-
systemMsgs := buildSystemMessages(docID, lang, idx.getEntityDefs(ctx))
381+
systemMsgs := buildSystemMessages(docID, lang, idx.promptLang, idx.getEntityDefs(ctx))
368382
if isCodeContent(content) {
369-
systemMsgs = buildCodeSystemMessages(docID, lang)
383+
systemMsgs = buildCodeSystemMessages(docID, lang, idx.promptLang)
370384
}
371385

372386
// 3. 分页:按行将内容拆为多页,每页不超过 MaxTokens × 80%
@@ -452,9 +466,9 @@ func (idx *GraphIndexer) AddFile(ctx context.Context, filePath string) ([]*core.
452466
lang = "English"
453467
}
454468
ext := strings.ToLower(filepath.Ext(filePath))
455-
systemMsgs := buildSystemMessages(docID, lang, idx.getEntityDefs(ctx))
469+
systemMsgs := buildSystemMessages(docID, lang, idx.promptLang, idx.getEntityDefs(ctx))
456470
if isCodeExt(ext) {
457-
systemMsgs = buildCodeSystemMessages(docID, lang)
471+
systemMsgs = buildCodeSystemMessages(docID, lang, idx.promptLang)
458472
}
459473

460474
// 3. 分页:按行将内容拆为多页,每页不超过 MaxTokens × 80%

indexer/ontologies.go

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ const globalRelationTypes = `### Relation Types
1414
**Semantic**: DESCRIBES, CITES, RELATED_TO
1515
**Logical**: IMPLIES, PRECEDES, DEPENDS_ON`
1616

17+
const globalRelationTypesZH = `### 关系类型
18+
**结构关系**: IS_A, PART_OF, CONTAINS
19+
**语义关系**: DESCRIBES, CITES, RELATED_TO
20+
**逻辑关系**: IMPLIES, PRECEDES, DEPENDS_ON`
21+
1722
// =============================================================================
1823
// 全局统一的提取约束 — 所有领域共享。
1924
// =============================================================================
@@ -26,6 +31,14 @@ const globalExtractionConstraints = `### Extraction Constraints
2631
- Short content (<=3 lines) → extract only Topic, Term, and Concept (if applicable).
2732
- Always create a "Chunk DESCRIBES Entity" edge for each extracted entity.`
2833

34+
const globalExtractionConstraintsZH = `### 提取约束
35+
- 每个实体的 "type" 字段成为图中的节点标签 — 使用 MATCH (n:TypeName) 进行匹配。
36+
- 仅从下面定义的类型中提取实体。标准化缩写。
37+
- 每个 chunk 的 entity_ids 字段必须列出从该 chunk 中提取的所有实体 ID。每个实体必须至少出现在一个 chunk 的 entity_ids 中。这创建了双向链接:chunk→entity 通过 entity_ids,entity→chunk 通过 SourceChunkIDs。
38+
- 每个 chunk 最多 5 个实体和 5 个关系。
39+
- 短内容(<=3 行)→ 仅提取 Topic、Term 和 Concept(如适用)。
40+
- 为每个提取的实体创建 "Chunk DESCRIBES Entity" 边。`
41+
2942
// entityPropertyHints 常见实体类型的推荐属性。
3043
// 由 buildPropertyGuidance 根据类型名查找。
3144
var entityPropertyHints = map[string]string{
@@ -41,6 +54,19 @@ var entityPropertyHints = map[string]string{
4154
"Metric": "measurement_unit, value_range",
4255
}
4356

57+
var entityPropertyHintsZH = map[string]string{
58+
"Concept": "领域或学科",
59+
"Term": "定义",
60+
"Method": "领域, 步骤或阶段",
61+
"Resource": "格式, 来源或作者",
62+
"Tool": "用途, 平台",
63+
"Person": "角色, 专业领域, 所属机构",
64+
"Topic": "描述",
65+
"Event": "时间, 地点",
66+
"Work": "创作者, 格式",
67+
"Metric": "测量单位, 数值范围",
68+
}
69+
4470
// defaultEntityDefs 无自定义实体定义时的通用兜底。
4571
var defaultEntityDefs = []EntityDef{
4672
{Prompt: "**Concept** — core idea, theory, principle, paradigm"},
@@ -55,6 +81,19 @@ var defaultEntityDefs = []EntityDef{
5581
{Prompt: "**Metric** — KPI, measurement, score, statistic"},
5682
}
5783

84+
var defaultEntityDefsZH = []EntityDef{
85+
{Prompt: "**Concept** — 核心概念、理论、原则、范式"},
86+
{Prompt: "**Term** — 领域特定术语、行话、名词"},
87+
{Prompt: "**Method** — 方法论、流程、技术、工作流"},
88+
{Prompt: "**Resource** — 文档、书籍、文章、网页、参考资料"},
89+
{Prompt: "**Tool** — 软件、平台、设备、工具"},
90+
{Prompt: "**Person** — 作者、专家、贡献者、角色"},
91+
{Prompt: "**Topic** — 主题、领域、分类、标签"},
92+
{Prompt: "**Event** — 里程碑、会议、事件、历史事件"},
93+
{Prompt: "**Work** — 创作成果(博客、视频、故事、艺术品、代码)"},
94+
{Prompt: "**Metric** — KPI、测量指标、分数、统计数据"},
95+
}
96+
5897
// extractTypeName 从 Prompt 格式 "**Name** — description" 中提取类型名。
5998
func extractTypeName(prompt string) string {
6099
// 查找 **Name** 模式
@@ -72,21 +111,40 @@ func extractTypeName(prompt string) string {
72111

73112
// buildPropertyGuidance 生成 ### Entity Properties 段文本。
74113
// 对有 Schema 的类型引用其定义,对其他类型推断常见属性。
75-
func buildPropertyGuidance(defs []EntityDef) string {
114+
func buildPropertyGuidance(defs []EntityDef, promptLang string) string {
76115
var b strings.Builder
77-
b.WriteString("### Entity Properties\n")
78-
b.WriteString("Each entity's \"properties\" object MUST include \"description\" plus type-specific fields.\n")
116+
hints := entityPropertyHints
117+
if promptLang == LangZH {
118+
b.WriteString("### 实体属性\n")
119+
b.WriteString("每个实体的 \"properties\" 对象必须包含 \"description\" 以及类型特定字段。\n")
120+
hints = entityPropertyHintsZH
121+
} else {
122+
b.WriteString("### Entity Properties\n")
123+
b.WriteString("Each entity's \"properties\" object MUST include \"description\" plus type-specific fields.\n")
124+
}
79125
for _, d := range defs {
80126
typeName := extractTypeName(d.Prompt)
81127
if typeName == "" {
82128
continue
83129
}
84130
if d.Schema != "" {
85-
b.WriteString(fmt.Sprintf("- **%s**: use the schema defined in ### Entity Schema\n", typeName))
86-
} else if hint, ok := entityPropertyHints[typeName]; ok {
87-
b.WriteString(fmt.Sprintf("- **%s**: description, %s\n", typeName, hint))
131+
if promptLang == LangZH {
132+
b.WriteString(fmt.Sprintf("- **%s**: 使用 ### 实体 Schema 中定义的 schema\n", typeName))
133+
} else {
134+
b.WriteString(fmt.Sprintf("- **%s**: use the schema defined in ### Entity Schema\n", typeName))
135+
}
136+
} else if hint, ok := hints[typeName]; ok {
137+
if promptLang == LangZH {
138+
b.WriteString(fmt.Sprintf("- **%s**: description, %s\n", typeName, hint))
139+
} else {
140+
b.WriteString(fmt.Sprintf("- **%s**: description, %s\n", typeName, hint))
141+
}
88142
} else {
89-
b.WriteString(fmt.Sprintf("- **%s**: description, fields semantically relevant to %s\n", typeName, typeName))
143+
if promptLang == LangZH {
144+
b.WriteString(fmt.Sprintf("- **%s**: description, 与 %s 语义相关的字段\n", typeName, typeName))
145+
} else {
146+
b.WriteString(fmt.Sprintf("- **%s**: description, fields semantically relevant to %s\n", typeName, typeName))
147+
}
90148
}
91149
}
92150
return b.String()
@@ -96,7 +154,8 @@ func buildPropertyGuidance(defs []EntityDef) string {
96154
// entityDefs 为 EntityDef 列表,每个 def.Prompt 追加在 ### Entity Types 下,
97155
// 非空的 def.Schema 追加在 ### Entity Schema 下。
98156
// 无实体定义时使用通用兜底定义。
99-
func buildOntology(entityDefs []EntityDef) string {
157+
// promptLang 控制提示词语言(LangEN | LangZH)。
158+
func buildOntology(entityDefs []EntityDef, promptLang string) string {
100159
// 收集非空定义
101160
var defs []EntityDef
102161
for _, d := range entityDefs {
@@ -107,13 +166,23 @@ func buildOntology(entityDefs []EntityDef) string {
107166

108167
// 无实体定义 → 通用兜底
109168
if len(defs) == 0 {
110-
defs = defaultEntityDefs
169+
if promptLang == LangZH {
170+
defs = defaultEntityDefsZH
171+
} else {
172+
defs = defaultEntityDefs
173+
}
111174
}
112175

113176
var b strings.Builder
114-
b.WriteString("## Entity Extraction Rules\n\n")
115-
b.WriteString("### Entity Types\n")
116-
b.WriteString("Each entity type listed below becomes a node Label (MATCH (n:TypeName)).\n")
177+
if promptLang == LangZH {
178+
b.WriteString("## 实体提取规则\n\n")
179+
b.WriteString("### 实体类型\n")
180+
b.WriteString("下面列出的每个实体类型成为图中的节点标签(MATCH (n:TypeName))。\n")
181+
} else {
182+
b.WriteString("## Entity Extraction Rules\n\n")
183+
b.WriteString("### Entity Types\n")
184+
b.WriteString("Each entity type listed below becomes a node Label (MATCH (n:TypeName)).\n")
185+
}
117186
b.WriteString(defs[0].Prompt)
118187
for _, d := range defs[1:] {
119188
b.WriteByte('\n')
@@ -122,7 +191,7 @@ func buildOntology(entityDefs []EntityDef) string {
122191

123192
// Entity Properties Guidance(动态生成,每个类型不同的属性建议)
124193
b.WriteString("\n\n")
125-
b.WriteString(buildPropertyGuidance(defs))
194+
b.WriteString(buildPropertyGuidance(defs, promptLang))
126195

127196
// Entity Schema 段(仅当有非空 Schema 时追加)
128197
hasSchema := false
@@ -133,7 +202,11 @@ func buildOntology(entityDefs []EntityDef) string {
133202
}
134203
}
135204
if hasSchema {
136-
b.WriteString("### Entity Schema — Each entity type's schema below is keyed by its Label (type name).\n")
205+
if promptLang == LangZH {
206+
b.WriteString("### 实体 Schema — 下面每个实体类型的 schema 以其标签(类型名)为键。\n")
207+
} else {
208+
b.WriteString("### Entity Schema — Each entity type's schema below is keyed by its Label (type name).\n")
209+
}
137210
for _, d := range defs {
138211
if d.Schema == "" {
139212
continue
@@ -149,9 +222,15 @@ func buildOntology(entityDefs []EntityDef) string {
149222
}
150223

151224
b.WriteString("\n")
152-
b.WriteString(globalRelationTypes)
153-
b.WriteString("\n\n")
154-
b.WriteString(globalExtractionConstraints)
225+
if promptLang == LangZH {
226+
b.WriteString(globalRelationTypesZH)
227+
b.WriteString("\n\n")
228+
b.WriteString(globalExtractionConstraintsZH)
229+
} else {
230+
b.WriteString(globalRelationTypes)
231+
b.WriteString("\n\n")
232+
b.WriteString(globalExtractionConstraints)
233+
}
155234

156235
return b.String()
157236
}

indexer/ontologies_code.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,50 @@ const codeRelationTypes = `### Relation Types (Code Domain)
166166
**Structural**: IMPLEMENTS, EXTENDS, CONTAINS, PARAMETER_OF
167167
**Semantic**: CALLS, IMPORTS, DEFINES, RETURNS
168168
**Metadata**: ANNOTATED_BY`
169+
170+
// codeRelationTypesZH 是代码域专属的关系类型定义 — 中文版。
171+
const codeRelationTypesZH = `### 关系类型(代码域)
172+
**结构关系**: IMPLEMENTS, EXTENDS, CONTAINS, PARAMETER_OF
173+
**语义关系**: CALLS, IMPORTS, DEFINES, RETURNS
174+
**元数据**: ANNOTATED_BY`
175+
176+
// codeEntityDefsZH 是代码文件专用的中文实体类型定义列表。
177+
// 类型名保持英文(因为是图节点标签),描述翻译为中文。
178+
var codeEntityDefsZH = []EntityDef{
179+
{
180+
Prompt: "**Interface** — 接口、协议、trait,定义方法契约",
181+
Schema: codeEntityDefs[0].Schema,
182+
},
183+
{
184+
Prompt: "**Struct** — 结构体、记录、数据类,包含命名字段和可选方法",
185+
Schema: codeEntityDefs[1].Schema,
186+
},
187+
{
188+
Prompt: "**Class** — 类,包含字段、方法、继承(OOP 范式)",
189+
Schema: codeEntityDefs[2].Schema,
190+
},
191+
{
192+
Prompt: "**Function** — 函数、方法、过程、闭包定义",
193+
Schema: codeEntityDefs[3].Schema,
194+
},
195+
{
196+
Prompt: "**Package** — 包、模块、命名空间、库,组织代码",
197+
Schema: codeEntityDefs[4].Schema,
198+
},
199+
{
200+
Prompt: "**Enum** — 枚举,包含命名变体或常量值",
201+
Schema: codeEntityDefs[5].Schema,
202+
},
203+
{
204+
Prompt: "**TypeAlias** — 类型别名、typedef,创建替代名称",
205+
Schema: codeEntityDefs[6].Schema,
206+
},
207+
{
208+
Prompt: "**Variable** — 变量、常量、全局变量、配置值声明",
209+
Schema: codeEntityDefs[7].Schema,
210+
},
211+
{
212+
Prompt: "**Import** — import、include、require、using 指令,引用外部代码",
213+
Schema: codeEntityDefs[8].Schema,
214+
},
215+
}

0 commit comments

Comments
 (0)