feat(decision): auto-reload prompt templates when starting trader (#833)

* feat: 启动交易员时自动重新加载系统提示词模板

## 改动内容
- 在 handleStartTrader 中调用 decision.ReloadPromptTemplates()
- 每次启动交易员时从硬盘重新加载 prompts/ 目录下的所有 .txt 模板文件
- 添加完整的单元测试和端到端集成测试

## 测试覆盖
- 单元测试:模板加载、获取、重新加载功能
- 集成测试:文件修改 → 重新加载 → 决策引擎使用新内容的完整流程
- 并发测试:验证多 goroutine 场景下的线程安全性
- Race detector 测试通过

## 用户体验改进
- 修改 prompt 文件后无需重启服务
- 只需停止交易员再启动即可应用新的 prompt
- 控制台会输出重新加载成功的日志提示

* feat: 在重新加载日志中显示当前使用的模板名称

* feat: fallback 到 default 模板时明确显示原因

* fix: correct GetTraderConfig return type to get SystemPromptTemplate

* refactor: extract reloadPromptTemplatesWithLog as reusable method
This commit is contained in:
Lawrence Liu
2025-11-11 10:37:46 +08:00
committed by GitHub
parent aa17bb020e
commit 4c4d4b7ea8
3 changed files with 549 additions and 1 deletions

View File

@@ -792,12 +792,15 @@ func (s *Server) handleStartTrader(c *gin.Context) {
traderID := c.Param("id")
// 校验交易员是否属于当前用户
_, _, _, err := s.database.GetTraderConfig(userID, traderID)
traderRecord, _, _, err := s.database.GetTraderConfig(userID, traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在或无访问权限"})
return
}
// 获取模板名称
templateName := traderRecord.SystemPromptTemplate
trader, err := s.traderManager.GetTrader(traderID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
@@ -811,6 +814,9 @@ func (s *Server) handleStartTrader(c *gin.Context) {
return
}
// 重新加载系统提示词模板(确保使用最新的硬盘文件)
s.reloadPromptTemplatesWithLog(templateName)
// 启动交易员
go func() {
log.Printf("▶️ 启动交易员 %s (%s)", traderID, trader.GetName())
@@ -2318,3 +2324,17 @@ func (s *Server) handleGetPublicTraderConfig(c *gin.Context) {
c.JSON(http.StatusOK, result)
}
// reloadPromptTemplatesWithLog 重新加载提示词模板并记录日志
func (s *Server) reloadPromptTemplatesWithLog(templateName string) {
if err := decision.ReloadPromptTemplates(); err != nil {
log.Printf("⚠️ 重新加载提示词模板失败: %v", err)
return
}
if templateName == "" {
log.Printf("✓ 已重新加载系统提示词模板 [当前使用: default (未指定,使用默认)]")
} else {
log.Printf("✓ 已重新加载系统提示词模板 [当前使用: %s]", templateName)
}
}