十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

使用 LangChainGo 构建智能日志分析器:从日志解析到 AI 异常检测的完整实战

使用 LangChainGo 构建智能日志分析器:从日志解析到 AI 异常检测的完整实战 使用 LangChainGo 构建智能日志分析器从日志解析到 AI 异常检测的完整实战【免费下载链接】langchaingoLangChain for Go, the easiest way to write LLM-based programs in Go项目地址: https://gitcode.com/GitHub_Trending/la/langchaingo导读本文基于 LangChain for Golangchaingo官方教程手把手带你构建一个 AI 驱动的日志分析 CLI 工具它能解析 JSON、结构化文本、Nginx/Apache 等多种日志格式识别错误模式与异常生成摘要与趋势并针对检测到的问题给出处置建议、为关键故障生成告警。读完本文你将掌握 langchaingo 中llms.Model、prompts.PromptTemplate、chains.LLMChain等核心 API 的组合用法并得到一套可直接扩展为生产级日志监控平台的完整代码骨架。一、方案总览我们要构建什么一个智能日志分析器需要覆盖从「读日志」到「出结论」的完整链路解析多种格式的日志文件JSON、结构化文本、Nginx/Apache access log 等识别错误模式与异常行为汇总日志活动与趋势基于检测到的问题给出处置建议为关键故障生成告警。在架构上我们采用「规则引擎 大模型」双通道先用 Go 原生代码做确定性统计错误计数、Top 错误模式、时间范围再用 LLM 做更深层的语义分析异常检测、建议生成最后通过告警链Alert Chain把结论转成可操作的告警消息。这种设计兼顾了规则系统的可靠性与大模型的泛化能力。二、环境准备本教程基于 langchaingo 仓库go.mod 声明go 1.24.4编写需要满足以下前提Go 1.21建议与仓库一致使用 1.24.x一个 LLM API KeyOpenAI、Anthropic 等均可一份用于分析的示例日志文件。三、Step 1项目初始化mkdir log-analyzer cd log-analyzer go mod init log-analyzer go get github.com/tmc/langchaingo go get github.com/sirupsen/logrus # 用于生成结构化日志示例注意langchaingo 通过 Go module graph pruning 机制按需拉取依赖——你 import 哪个子包就只会引入对应的依赖不会因为一个大go.mod而拖入全部模块详见仓库根目录 go.mod 开头的注释说明。四、Step 2核心分析器实现创建main.go完整代码如下package main import ( bufio context encoding/json flag fmt log os regexp sort strings time github.com/tmc/langchaingo/llms github.com/tmc/langchaingo/llms/openai github.com/tmc/langchaingo/prompts ) type LogEntry struct { Timestamp time.Time json:timestamp Level string json:level Message string json:message Source string json:source Raw string json:raw } type LogAnalysis struct { TotalEntries int json:total_entries ErrorCount int json:error_count WarningCount int json:warning_count TopErrors []ErrorPattern json:top_errors TimeRange TimeRange json:time_range Recommendations []string json:recommendations Anomalies []Anomaly json:anomalies } type ErrorPattern struct { Pattern string json:pattern Count int json:count Example string json:example } type TimeRange struct { Start time.Time json:start End time.Time json:end } type Anomaly struct { Type string json:type Description string json:description Severity string json:severity Examples []string json:examples } type LogAnalyzer struct { llm llms.Model } func NewLogAnalyzer() (*LogAnalyzer, error) { llm, err : openai.New() if err ! nil { return nil, fmt.Errorf(creating LLM: %w, err) } return LogAnalyzer{llm: llm}, nil } func (la *LogAnalyzer) ParseLogFile(filename string) ([]LogEntry, error) { file, err : os.Open(filename) if err ! nil { return nil, fmt.Errorf(opening file: %w, err) } defer file.Close() var entries []LogEntry scanner : bufio.NewScanner(file) // Common log patterns patterns : []*regexp.Regexp{ // JSON logs regexp.MustCompile(^\{.*\}$), // Standard format: 2023-01-01 12:00:00 [ERROR] message regexp.MustCompile(^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s\[(\w)\]\s(.)$), // Nginx/Apache format regexp.MustCompile(^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*\[([^\]])\].*([^]*).*(\d{3})), } for scanner.Scan() { line : scanner.Text() if strings.TrimSpace(line) { continue } entry : LogEntry{Raw: line} // Try JSON first if line[0] { { var jsonEntry map[string]interface{} if err : json.Unmarshal([]byte(line), jsonEntry); err nil { entry parseJSONLog(jsonEntry, line) entries append(entries, entry) continue } } // Try structured patterns for _, pattern : range patterns[1:] { if matches : pattern.FindStringSubmatch(line); matches ! nil { entry parseStructuredLog(matches, line) break } } // Fallback: treat as unstructured if entry.Timestamp.IsZero() { entry LogEntry{ Timestamp: time.Now(), // Use current time as fallback Level: inferLogLevel(line), Message: line, Raw: line, } } entries append(entries, entry) } return entries, scanner.Err() } func parseJSONLog(data map[string]interface{}, raw string) LogEntry { entry : LogEntry{Raw: raw} if ts, ok : data[timestamp].(string); ok { if t, err : time.Parse(time.RFC3339, ts); err nil { entry.Timestamp t } } if level, ok : data[level].(string); ok { entry.Level level } if msg, ok : data[message].(string); ok { entry.Message msg } if src, ok : data[source].(string); ok { entry.Source src } return entry } func parseStructuredLog(matches []string, raw string) LogEntry { entry : LogEntry{Raw: raw} if len(matches) 4 { if t, err : time.Parse(2006-01-02 15:04:05, matches[1]); err nil { entry.Timestamp t } entry.Level matches[2] entry.Message matches[3] } return entry } func inferLogLevel(line string) string { lower : strings.ToLower(line) switch { case strings.Contains(lower, error) || strings.Contains(lower, fatal): return ERROR case strings.Contains(lower, warn): return WARN case strings.Contains(lower, debug): return DEBUG default: return INFO } } func (la *LogAnalyzer) AnalyzeLogs(entries []LogEntry) (*LogAnalysis, error) { if len(entries) 0 { return LogAnalysis{}, nil } // Basic statistics analysis : LogAnalysis{ TotalEntries: len(entries), TimeRange: TimeRange{ Start: entries[0].Timestamp, End: entries[len(entries)-1].Timestamp, }, } // Count by level errorMessages : []string{} for _, entry : range entries { switch strings.ToUpper(entry.Level) { case ERROR, FATAL: analysis.ErrorCount errorMessages append(errorMessages, entry.Message) case WARN, WARNING: analysis.WarningCount } } // Find error patterns analysis.TopErrors findErrorPatterns(errorMessages) // Use AI for deeper analysis if err : la.performAIAnalysis(entries, analysis); err ! nil { return nil, fmt.Errorf(AI analysis failed: %w, err) } return analysis, nil } func findErrorPatterns(messages []string) []ErrorPattern { patternCounts : make(map[string]int) patternExamples : make(map[string]string) for _, msg : range messages { // Normalize error messages by removing specific values pattern : normalizeErrorMessage(msg) patternCounts[pattern] if patternExamples[pattern] { patternExamples[pattern] msg } } // Sort by frequency type kv struct { Pattern string Count int } var sorted []kv for k, v : range patternCounts { sorted append(sorted, kv{k, v}) } sort.Slice(sorted, func(i, j int) bool { return sorted[i].Count sorted[j].Count }) var result []ErrorPattern for i, kv : range sorted { if i 10 { // Top 10 patterns break } result append(result, ErrorPattern{ Pattern: kv.Pattern, Count: kv.Count, Example: patternExamples[kv.Pattern], }) } return result } func normalizeErrorMessage(msg string) string { // Replace common variable patterns re1 : regexp.MustCompile(\d) re2 : regexp.MustCompile([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}) re3 : regexp.MustCompile(\b\w\w\.\w\b) normalized : re1.ReplaceAllString(msg, XXX) normalized re2.ReplaceAllString(normalized, UUID) normalized re3.ReplaceAllString(normalized, EMAIL) return normalized } func (la *LogAnalyzer) performAIAnalysis(entries []LogEntry, analysis *LogAnalysis) error { // Prepare sample of entries for AI analysis sampleSize : 50 if len(entries) sampleSize { sampleSize len(entries) } sample : entries[len(entries)-sampleSize:] // Last N entries template : prompts.NewPromptTemplate( You are an expert system administrator analyzing application logs. Based on the log data provided, identify: 1. **Anomalies**: Unusual patterns, spikes, or unexpected behaviors 2. **Recommendations**: Specific actions to improve system reliability 3. **Critical Issues**: Problems requiring immediate attention Log Summary: - Total Entries: {{.total_entries}} - Errors: {{.error_count}} - Warnings: {{.warning_count}} - Time Range: {{.time_range}} Top Error Patterns: {{range .top_errors}} - {{.pattern}} ({{.count}} occurrences) {{end}} Recent Log Sample: {{range .sample}} {{.timestamp}} [{{.level}}] {{.message}} {{end}} Respond in JSON format: { anomalies: [ { type: error_spike|performance|security|other, description: What was detected, severity: critical|high|medium|low, examples: [example log entries] } ], recommendations: [ Specific actionable recommendations ] }, []string{total_entries, error_count, warning_count, time_range, top_errors, sample}) sampleData : make([]map[string]string, len(sample)) for i, entry : range sample { sampleData[i] map[string]string{ timestamp: entry.Timestamp.Format(time.RFC3339), level: entry.Level, message: entry.Message, } } prompt, err : template.Format(map[string]any{ total_entries: analysis.TotalEntries, error_count: analysis.ErrorCount, warning_count: analysis.WarningCount, time_range: fmt.Sprintf(%s to %s, analysis.TimeRange.Start.Format(time.RFC3339), analysis.TimeRange.End.Format(time.RFC3339)), top_errors: analysis.TopErrors, sample: sampleData, }) if err ! nil { return fmt.Errorf(formatting prompt: %w, err) } ctx : context.Background() response, err : la.llm.GenerateContent(ctx, []llms.MessageContent{ llms.TextParts(llms.ChatMessageTypeHuman, prompt), }, llms.WithJSONMode()) if err ! nil { return fmt.Errorf(generating analysis: %w, err) } var aiResult struct { Anomalies []Anomaly json:anomalies Recommendations []string json:recommendations } if err : json.Unmarshal([]byte(response.Choices[0].Content), aiResult); err ! nil { return fmt.Errorf(parsing AI response: %w, err) } analysis.Anomalies aiResult.Anomalies analysis.Recommendations aiResult.Recommendations return nil } func (la *LogAnalysis) PrintReport() { fmt.Printf( Log Analysis Report\n) fmt.Printf(\n\n) fmt.Printf( Summary:\n) fmt.Printf( Total Entries: %d\n, la.TotalEntries) fmt.Printf( Errors: %d\n, la.ErrorCount) fmt.Printf( Warnings: %d\n, la.WarningCount) fmt.Printf( Time Range: %s to %s\n\n, la.TimeRange.Start.Format(2006-01-02 15:04:05), la.TimeRange.End.Format(2006-01-02 15:04:05)) if len(la.TopErrors) 0 { fmt.Printf( Top Error Patterns:\n) for i, pattern : range la.TopErrors { if i 5 { break } fmt.Printf( %d. %s (%d occurrences)\n, i1, pattern.Pattern, pattern.Count) } fmt.Println() } if len(la.Anomalies) 0 { fmt.Printf(⚠️ Detected Anomalies:\n) for _, anomaly : range la.Anomalies { fmt.Printf( %s - %s (%s)\n, anomaly.Type, anomaly.Description, anomaly.Severity) } fmt.Println() } if len(la.Recommendations) 0 { fmt.Printf( Recommendations:\n) for i, rec : range la.Recommendations { fmt.Printf( %d. %s\n, i1, rec) } fmt.Println() } } func main() { var ( file flag.String(file, , Log file to analyze) output flag.String(output, , Output file for JSON report) watch flag.Bool(watch, false, Watch file for changes) ) flag.Parse() if *file { fmt.Println(Usage: log-analyzer -fileapplication.log) os.Exit(1) } analyzer, err : NewLogAnalyzer() if err ! nil { log.Fatal(err) } if *watch { // Watch mode - simplified version fmt.Printf( Watching %s for changes...\n, *file) for { if err : analyzeFile(analyzer, *file, *output); err ! nil { log.Printf(Analysis error: %v, err) } time.Sleep(30 * time.Second) } } else { if err : analyzeFile(analyzer, *file, *output); err ! nil { log.Fatal(err) } } } func analyzeFile(analyzer *LogAnalyzer, filename, outputFile string) error { fmt.Printf( Analyzing %s...\n, filename) entries, err : analyzer.ParseLogFile(filename) if err ! nil { return fmt.Errorf(parsing log file: %w, err) } analysis, err : analyzer.AnalyzeLogs(entries) if err ! nil { return fmt.Errorf(analyzing logs: %w, err) } analysis.PrintReport() if outputFile ! { data, err : json.MarshalIndent(analysis, , ) if err ! nil { return fmt.Errorf(marshaling report: %w, err) } if err : os.WriteFile(outputFile, data, 0644); err ! nil { return fmt.Errorf(writing report: %w, err) } fmt.Printf( Report saved to %s\n, outputFile) } return nil }源码级解读这段代码背后的 langchaingo 机制1. 统一模型接口llms.ModelLogAnalyzer持有的是llms.Model接口而非具体的 OpenAI 客户端。在仓库 llms/llms.go 中Model接口定义了两个核心方法GenerateContent(ctx, messages []MessageContent, options ...CallOption) (*ContentResponse, error)最通用的多模态对话接口本教程的 AI 分析就基于它Call(ctx, prompt string, options ...CallOption) (string, error)简化的纯文本接口已标记 Deprecated建议改用GenerateContent或GenerateFromSinglePrompt。openai.New()返回的*openai.LLMllms/openai/openaillm.go实现了该接口。由于openai.New()默认读取环境变量你只需设置OPENAI_API_KEY也可以按需使用openai.WithModel、openai.WithToken、openai.WithBaseURL等 Option见 llms/openai/openaillm_option.go。接口抽象意味着你可以无缝替换为 Anthropic、GoogleAI、Bedrock、Ollama 等仓库内其它 provider见 llms 目录下各子包。2. 构造提示词prompts.NewPromptTemplateprompts.NewPromptTemplate(template, inputVars)prompts/prompt_template.go创建默认使用 Go template 语法的模板Format(values)渲染后返回字符串。它支持{{range}}遍历用于渲染 Top Error Patterns 列表、{{.field}}插值还能通过PartialVariables预置公共变量。本教程中的模板声明了total_entries、error_count、warning_count、time_range、top_errors、sample六个输入变量与Format传入的map[string]any一一对应。3. 请求 JSON 输出llms.WithJSONMode()llms.WithJSONMode()llms/options.go是CallOption之一用于设置响应格式为 JSON——这对我们解析Anomalies和Recommendations至关重要能显著提升模型按 JSON Schema 输出的稳定性。4. 多模态消息llms.TextParts与ChatMessageTypeHumanllms.TextParts(role, parts...)llms/generatecontent.go构造一条MessageContent其中 Role 为ChatMessageTypeHuman表示人类发送的消息见 llms/chat_messages.go。GenerateContent返回的ContentResponse.Choices[0].Content即模型生成的文本我们随后用json.Unmarshal反序列化为结构化结果。日志解析的三种策略ParseLogFile采用「逐行扫描 三级回退」策略JSON 优先行首为{时尝试json.Unmarshal成功则用parseJSONLog提取timestampRFC3339、level、message、source字段结构化正则内置标准时间戳格式2006-01-02 15:04:05 [LEVEL] message与 Nginx/Apache 访问日志格式两组正则兜底时间戳缺失时使用当前时间并通过inferLogLevel依据关键词error/fatal/warn/debug猜测级别。normalizeErrorMessage是错误模式归并的关键把数字替换为XXX、UUID 替换为UUID、邮箱替换为EMAIL使「同一类」错误如不同用户名的邮箱格式错误能聚合成一个 Pattern再按出现次数排序取 Top 10。采样策略控制成本performAIAnalysis不会把整份日志全部喂给模型而是只取最后最多 50 条作为sample连同统计摘要一起送入提示词。这在生产环境是控制 token 成本与延迟的常见做法——统计交给确定性代码模型只负责它擅长的语义判断。五、Step 3创建示例日志创建sample.log用于测试2024-01-15 10:30:01 [INFO] Application started successfully 2024-01-15 10:30:02 [INFO] Database connection established 2024-01-15 10:30:15 [ERROR] Failed to process user request: invalid email format user 2024-01-15 10:30:16 [WARN] High memory usage detected: 85% 2024-01-15 10:30:17 [ERROR] Database timeout after 30s 2024-01-15 10:30:18 [ERROR] Failed to process user request: invalid email format admin 2024-01-15 10:30:19 [INFO] Request processed successfully 2024-01-15 10:30:25 [ERROR] Database timeout after 30s 2024-01-15 10:30:30 [FATAL] Out of memory error - application terminating 2024-01-15 10:30:31 [INFO] Application shutdown initiated可以看到这份样例故意制造了两个可被归并的错误模式invalid email format与Database timeout其中邮箱地址不同但会被normalizeErrorMessage归一化、一个高内存警告和一个 FATAL 级 OOM——用于验证 Pattern 聚类与告警触发逻辑。六、Step 4运行分析器export OPENAI_API_KEYyour-openai-api-key-here go run main.go -filesample.log -outputreport.jsonmain函数通过标准库flag暴露三个参数参数默认值说明-file空必填待分析的日志文件路径缺失时打印 Usage 并退出-output空JSON 报告的输出文件路径空则只打印终端报告-watchfalse开启轮询监视模式每 30 秒重新分析一次运行后终端会依次输出「分析摘要 → Top 错误模式 → 检测到的异常 → 建议」同时把完整的LogAnalysis结构以缩进 JSON 写入report.json方便后续接入其他系统。七、Step 5增强实时监控仅靠手动运行远不够生产环境需要持续监控。创建monitor.go利用fsnotify监听文件写入事件并即时告警package main import ( context fmt log time github.com/fsnotify/fsnotify github.com/tmc/langchaingo/llms github.com/tmc/langchaingo/chains ) type LogMonitor struct { analyzer *LogAnalyzer watcher *fsnotify.Watcher alertChain chains.Chain thresholds MonitoringThresholds } type MonitoringThresholds struct { ErrorsPerMinute int CriticalKeywords []string ResponseTimeLimit time.Duration } func NewLogMonitor(analyzer *LogAnalyzer) (*LogMonitor, error) { watcher, err : fsnotify.NewWatcher() if err ! nil { return nil, err } // Create alert chain for notifications alertChain : chains.NewLLMChain(analyzer.llm, prompts.NewPromptTemplate( Generate a concise alert message for this log analysis: {{.analysis}} Format as: [SEVERITY] Brief description - Action needed Keep under 140 characters., []string{analysis})) return LogMonitor{ analyzer: analyzer, watcher: watcher, alertChain: alertChain, thresholds: MonitoringThresholds{ ErrorsPerMinute: 10, CriticalKeywords: []string{fatal, out of memory, database down}, ResponseTimeLimit: 5 * time.Second, }, }, nil } func (lm *LogMonitor) Start(filename string) error { err : lm.watcher.Add(filename) if err ! nil { return err } fmt.Printf( Monitoring %s for critical issues...\n, filename) for { select { case event, ok : -lm.watcher.Events: if !ok { return nil } if event.Opfsnotify.Write fsnotify.Write { go lm.checkForAlerts(filename) } case err, ok : -lm.watcher.Errors: if !ok { return nil } log.Printf(Watcher error: %v, err) } } } func (lm *LogMonitor) checkForAlerts(filename string) { // Read last N lines and check for critical issues entries, err : lm.analyzer.ParseLogFile(filename) if err ! nil { log.Printf(Error parsing file: %v, err) return } // Check recent entries (last minute) recent : lm.getRecentEntries(entries, time.Minute) if lm.shouldAlert(recent) { analysis, err : lm.analyzer.AnalyzeLogs(recent) if err ! nil { log.Printf(Error analyzing logs: %v, err) return } alert, err : chains.Run(context.Background(), lm.alertChain, fmt.Sprintf(Analysis: %v, analysis)) if err ! nil { log.Printf(Error generating alert: %v, err) return } fmt.Printf( ALERT: %s\n, alert) // Here you would send to Slack, email, etc. } } func (lm *LogMonitor) getRecentEntries(entries []LogEntry, duration time.Duration) []LogEntry { cutoff : time.Now().Add(-duration) var recent []LogEntry for i : len(entries) - 1; i 0; i-- { if entries[i].Timestamp.Before(cutoff) { break } recent append([]LogEntry{entries[i]}, recent...) } return recent } func (lm *LogMonitor) shouldAlert(entries []LogEntry) bool { errorCount : 0 for _, entry : range entries { if entry.Level ERROR || entry.Level FATAL { errorCount } // Check for critical keywords for _, keyword : range lm.thresholds.CriticalKeywords { if strings.Contains(strings.ToLower(entry.Message), keyword) { return true } } } return errorCount lm.thresholds.ErrorsPerMinute }告警链的源码原理这里的alertChain是chains.NewLLMChain(analyzer.llm, prompt)chains/llm.go。LLMChain内部流程为FormatPrompt渲染提示词 →llms.GenerateFromSinglePrompt调用模型 →OutputParser默认outputparser.NewSimple()解析输出 → 以text为 key 返回结果。chains.Run(ctx, chain, input)chains/chains.go是链执行的便捷入口它要求链恰好有一个输入 key、一个输出 key否则返回ErrMultipleInputsInRun/ErrMultipleOutputsInRun。在后台Run与Call一样会经历输入校验validateInputs、回调分发HandleChainStart/HandleChainEnd、以及内存变量的加载与保存见 chains/chains.go因此你可以随时给告警链挂上记忆memory或回调callbacks而无需改动调用代码。shouldAlert的判定逻辑值得注意——任一匹配CriticalKeywords默认fatal、out of memory、database down即触发或最近一分钟内 ERROR/FATAL 条数达到ErrorsPerMinute默认 10阈值也触发。checkForAlerts只分析最近一分钟的条目避免每次文件写入都全量扫描。八、Step 6与可观测性工具集成最后创建integrations.go把告警接入 Slack并向 Prometheus 暴露指标package main import ( bytes encoding/json fmt net/http ) type SlackAlert struct { Text string json:text } func (lm *LogMonitor) sendSlackAlert(message string, webhookURL string) error { alert : SlackAlert{Text: fmt.Sprintf(Log Alert: %s, message)} jsonData, err : json.Marshal(alert) if err ! nil { return err } resp, err : http.Post(webhookURL, application/json, bytes.NewBuffer(jsonData)) if err ! nil { return err } defer resp.Body.Close() return nil } // Prometheus metrics type MetricsCollector struct { errorCount int warningCount int } func (mc *MetricsCollector) UpdateFromAnalysis(analysis *LogAnalysis) { mc.errorCount analysis.ErrorCount mc.warningCount analysis.WarningCount } // Export to Prometheus format func (mc *MetricsCollector) PrometheusMetrics() string { return fmt.Sprintf( # HELP log_errors_total Total number of error log entries # TYPE log_errors_total counter log_errors_total %d # HELP log_warnings_total Total number of warning log entries # TYPE log_warnings_total counter log_warnings_total %d , mc.errorCount, mc.warningCount) }sendSlackAlert通过标准net/http向 Slack Incoming Webhook 投递 JSONMetricsCollector则累加各轮分析的错误/警告计数并渲染成 Prometheus 文本格式# HELP、# TYPE、counter均为 Prometheus 暴露协议的约定你可以把它挂到/metrics端点供 Prometheus 抓取。shouldAlert的触发点处调用sendSlackAlert即可完成「检测 → 告警 → 通知」闭环。九、典型使用场景这套日志分析器可以应用于生产监控在问题升级为严重故障前及早发现故障响应快速理解故障发生时到底发生了什么性能分析定位慢查询与性能瓶颈安全监控识别可疑访问模式与异常行为容量规划理解使用模式与增长趋势。十、进阶方向在现有骨架基础上可以继续扩展机器学习基于历史日志模式训练模型改进异常判定关联分析跨多个服务关联错误定位根因链条预测式告警在问题实际发生前发出预警自定义仪表盘将分析结果可视化呈现自动化修复对已知问题自动触发修复动作。结语本教程演示了 LangChainGo 如何驱动一个具备真实业务价值的运维工具确定性规则保证统计的可靠性LLM 提供语义级洞察chains层让告警生成变得可组合、可扩展。你可以进一步浏览仓库中的 chains链式编排、memory对话记忆、callbacks运行回调等模块把日志分析器升级为完整的 AI 运维平台。【免费下载链接】langchaingoLangChain for Go, the easiest way to write LLM-based programs in Go项目地址: https://gitcode.com/GitHub_Trending/la/langchaingo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表