Files
BlackBean/internal/agent/config.go
T
2026-08-14 23:41:57 +08:00

138 lines
4.1 KiB
Go

package agent
import (
"bufio"
"os"
"path/filepath"
"strconv"
"strings"
"time"
)
type Config struct {
APIKey string
BaseURL string
Model string
Port string
Workspace string
DataDir string
// AuthCode 是 Web/API/WS 访问授权码。为空(默认)表示不启用授权;
// 非空(Docker 部署时通过环境变量 AGENT_AUTH_CODE 注入)表示启用:
// 访问者必须输入正确授权码才能使用本实例。
AuthCode string
MaxContextTokens int
KeepRecentMessages int
MaxToolResultChars int
MaxIterations int
ToolTimeout time.Duration
RequestTimeout time.Duration
StreamOutputTokens int
CompactionTokens int
Temperature float64
}
func LoadConfig() Config {
loadDotEnv(".env")
workspace := getenv("AGENT_WORKSPACE", "")
if workspace == "" {
workspace, _ = os.Getwd()
}
absWorkspace, err := filepath.Abs(workspace)
if err != nil {
absWorkspace = workspace
}
dataDir := getenv("AGENT_DATA_DIR", filepath.Join(absWorkspace, "data"))
if !filepath.IsAbs(dataDir) {
dataDir = filepath.Join(absWorkspace, dataDir)
}
cfg := Config{
APIKey: os.Getenv("SILICONFLOW_API_KEY"),
BaseURL: getenv("SILICONFLOW_BASE_URL", "https://api.siliconflow.cn/v1/chat/completions"),
Model: getenv("AGENT_MODEL", "Qwen/Qwen3-32B"),
Port: getenv("AGENT_PORT", "8080"),
Workspace: absWorkspace,
DataDir: dataDir,
// 全面放开限制以适配 DeepSeek(128K 上下文、大输出)跑复杂任务:
// - MaxContextTokens 给到 1M,确保 compactionInputBudget 公式(window - Compaction - Stream - window/8)仍有充足余量
// - StreamOutputTokens 提升到 65536:直接作为 max_tokens 传给 LLM(见 llm.go),是放开主 Agent 输出能力的关键
// - CompactionTokens 提升到 16384:压缩摘要允许足够长度,避免历史信息丢失
// - MaxToolResultChars 提升到 100000:复杂任务的脚本/日志能完整传回 LLM,不被截断
// - KeepRecentMessages 提升到 50:压缩后保留更多历史,复杂多步任务上下文不丢
// - ToolTimeout/RequestTimeout 提升到 600s:编译/长命令/深度推理有充足时间
MaxContextTokens: getenvInt("AGENT_MAX_CONTEXT_TOKENS", 1000000),
KeepRecentMessages: getenvInt("AGENT_KEEP_RECENT_MESSAGES", 50),
MaxToolResultChars: getenvInt("AGENT_MAX_TOOL_RESULT_CHARS", 100000),
MaxIterations: getenvInt("AGENT_MAX_ITERATIONS", 50),
ToolTimeout: time.Duration(getenvInt("AGENT_TOOL_TIMEOUT_SECONDS", 600)) * time.Second,
RequestTimeout: time.Duration(getenvInt("AGENT_REQUEST_TIMEOUT_SECONDS", 600)) * time.Second,
StreamOutputTokens: getenvInt("AGENT_STREAM_OUTPUT_TOKENS", 65536),
CompactionTokens: getenvInt("AGENT_COMPACTION_TOKENS", 16384),
Temperature: getenvFloat("AGENT_TEMPERATURE", 0.3),
// 授权码默认关闭:仅当 Docker 部署时显式注入 AGENT_AUTH_CODE 才启用访问授权。
AuthCode: getenv("AGENT_AUTH_CODE", ""),
}
return cfg
}
func loadDotEnv(path string) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
key, value, ok := strings.Cut(line, "=")
if !ok {
continue
}
key = strings.TrimSpace(key)
value = strings.TrimSpace(value)
value = strings.Trim(value, `"'`)
if key == "" {
continue
}
if _, exists := os.LookupEnv(key); !exists {
_ = os.Setenv(key, value)
}
}
}
func getenv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}
func getenvInt(key string, fallback int) int {
value := os.Getenv(key)
if value == "" {
return fallback
}
parsed, err := strconv.Atoi(value)
if err != nil {
return fallback
}
return parsed
}
func getenvFloat(key string, fallback float64) float64 {
value := os.Getenv(key)
if value == "" {
return fallback
}
parsed, err := strconv.ParseFloat(value, 64)
if err != nil {
return fallback
}
return parsed
}