Skip to main content

🚀 快速上手指南

🎯 5分钟快速体验

步骤1:环境准备

# 检查 Go 版本(需要 1.21+)
go version

# 检查 Ollama 是否安装
ollama --version

# 如果没有安装 Ollama
curl -fsSL https://ollama.ai/install.sh | sh

步骤2:拉取模型

# 拉取 llama3.2 模型(约 2GB)
ollama pull llama3.2

# 测试模型是否正常
ollama run llama3.2 "你好"

步骤3:运行项目

# 进入项目目录
cd learn-langchain

# 安装依赖
go mod tidy

# 编译运行
go run main.go chat

步骤4:测试功能

🧑 北京的天气怎么样        # 测试天气工具
🧑 现在几点了 # 测试时间工具
🧑 计算 15+25 # 测试计算器
🧑 你好,介绍一下自己 # 测试对话功能
🧑 quit # 退出程序

📖 核心知识点速查

1. langchain-go 基础概念

LLM 抽象

// 统一的大语言模型接口
type Model interface {
GenerateContent(ctx context.Context, messages []MessageContent, options ...CallOption) (*ContentResponse, error)
}

// Ollama 实现
llm, err := ollama.New(ollama.WithModel("llama3.2"))

关键理解:

  • LLM 是一个抽象接口,可以切换不同的模型提供商
  • Ollama 提供本地模型运行能力
  • 统一的接口设计便于模型替换

Tools 工具系统

// 工具接口定义
type Tool interface {
Name() string // 工具名称
Description() string // 工具描述
Call(ctx context.Context, input string) (string, error) // 工具调用
}

设计原则:

  • 单一职责:每个工具专注一个功能
  • 标准化接口:便于集成和扩展
  • 上下文传递:支持取消和超时控制

Chains 链式处理

// 创建 LLM Chain
prompt := prompts.NewPromptTemplate(template, []string{"input"})
llmChain := chains.NewLLMChain(llm, prompt)

// 执行链处理
result, err := chains.Call(ctx, llmChain, map[string]any{
"input": userInput,
})

核心思想:

  • 模块化组合:将复杂任务分解为简单步骤
  • 可复用性:同一个 Chain 可以处理不同输入
  • 提示工程:通过模板管理 LLM 交互

2. 项目架构设计

分层架构

表示层(CLI)     ←→ cmd/chat.go
业务逻辑层 ←→ processWithChain()
工具抽象层 ←→ tools.Tool interface
具体实现层 ←→ tool/*.go

数据流向

用户输入 → 工具识别 → 工具调用/LLM处理 → 结果返回

关键组件

  • 工具注册器: toolMap 快速查找工具
  • 智能路由: identifyTool() 识别用户意图
  • 处理链路: processWithChain() 统一处理入口

3. 工具开发规范

标准实现模板

type XxxTool struct{}

func (x XxxTool) Name() string {
return "tool_name" // 全局唯一标识
}

func (x XxxTool) Description() string {
return "工具功能描述,用于 LLM 理解"
}

func (x XxxTool) Call(ctx context.Context, input string) (string, error) {
// 1. 输入验证
if input == "" {
return "", fmt.Errorf("输入不能为空")
}

// 2. 业务处理
result := processInput(input)

// 3. 返回结果
return result, nil
}

错误处理最佳实践

func (x XxxTool) Call(ctx context.Context, input string) (string, error) {
// 使用 defer 进行资源清理
defer func() {
// 清理逻辑
}()

// 检查上下文取消
select {
case <-ctx.Done():
return "", ctx.Err()
default:
}

// 具体业务逻辑
// ...
}

4. 智能路由算法

关键词匹配策略

func identifyTool(input string) (toolName, toolInput string) {
inputLower := strings.ToLower(input)

// 优先级匹配:精确 > 模糊 > 默认
patterns := map[string][]string{
"weather": {"天气", "weather", "气温"},
"current_time": {"时间", "几点", "现在", "当前"},
"calculator": {"计算", "+", "-", "*", "/", "="},
"text_summary": {"摘要", "总结", "概括"},
}

for toolName, keywords := range patterns {
for _, keyword := range keywords {
if strings.Contains(inputLower, keyword) {
return toolName, processInput(input, toolName)
}
}
}

return "", ""
}

输入预处理

func processInput(input, toolName string) string {
switch toolName {
case "weather":
return extractCityName(input)
case "calculator":
return extractMathExpression(input)
case "current_time":
return "" // 无需输入参数
default:
return input
}
}

5. 提示工程技巧

模板设计原则

template := `你是一个专业的 {{.role}} 助手。

任务要求:
{{.requirements}}

用户输入:{{.input}}

请按以下格式回复:
1. 理解:[重复用户需求]
2. 分析:[问题分析过程]
3. 回答:[具体答案]

回复:`

设计要点:

  • 角色定义清晰
  • 任务要求具体
  • 输出格式规范
  • 示例引导明确

上下文管理

type ConversationContext struct {
History []Message
UserInfo map[string]any
Session string
}

func buildPromptWithContext(template string, ctx ConversationContext, input string) string {
// 构建包含上下文的完整提示
}

🔍 业务逻辑深度解析

核心处理流程

1. 请求接收与预处理

func (chatCmd *cobra.Command) Run(cmd *cobra.Command, args []string) {
// 初始化组件
llm := initializeLLM()
toolMap := buildToolMap()

// 启动交互循环
for {
input := readUserInput()
response := processWithChain(llm, toolMap, input)
displayResponse(response)
}
}

关键点:

  • 组件初始化:一次性完成 LLM 和工具的初始化
  • 交互循环:保持会话状态,处理连续对话
  • 异常处理:优雅处理用户中断和系统错误

2. 智能路由决策

func processWithChain(llm *ollama.LLM, toolMap map[string]tools.Tool, input string) string {
// 决策树:工具 > 对话
if toolName, toolInput := identifyTool(input); toolName != "" {
return callToolDirectly(toolMap[toolName], toolInput)
}
return callLLMChain(llm, input)
}

决策逻辑:

  1. 工具优先:明确的功能需求直接调用工具
  2. 对话降级:复杂或模糊需求使用 LLM
  3. 性能优化:避免不必要的 LLM 调用

3. 工具执行机制

func callToolDirectly(tool tools.Tool, input string) string {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

result, err := tool.Call(ctx, input)
if err != nil {
return formatError(tool.Name(), err)
}
return result
}

执行特点:

  • 超时控制:防止工具调用阻塞
  • 错误格式化:提供用户友好的错误信息
  • 上下文传递:支持取消和清理操作

4. LLM 对话处理

func callLLMChain(llm *ollama.LLM, input string) string {
// 构建提示
prompt := buildConversationPrompt(input)

// 创建处理链
chain := chains.NewLLMChain(llm, prompt)

// 执行对话
result, err := chains.Call(context.Background(), chain, map[string]any{
"input": input,
})

return extractResponse(result, err)
}

处理特点:

  • 提示工程:使用模板确保输出质量
  • 链式处理:利用 langchain 的抽象能力
  • 结果提取:处理不同格式的 LLM 响应

错误处理与容错机制

分层错误处理

// 工具层错误
func (t Tool) Call(ctx context.Context, input string) (string, error) {
if err := validateInput(input); err != nil {
return "", fmt.Errorf("输入验证失败: %w", err)
}
// ...
}

// 业务层错误
func callToolDirectly(tool tools.Tool, input string) string {
result, err := tool.Call(ctx, input)
if err != nil {
return fmt.Sprintf("⚠️ %s工具调用失败: %v", tool.Name(), err)
}
return result
}

// 表示层错误
func displayResponse(response string) {
if strings.HasPrefix(response, "⚠️") {
// 错误信息特殊显示
fmt.Printf("🔴 %s\n", response)
} else {
fmt.Printf("🤖 %s\n", response)
}
}

降级策略

func processWithChain(llm *ollama.LLM, toolMap map[string]tools.Tool, input string) string {
// 主路径:工具调用
if toolName, toolInput := identifyTool(input); toolName != "" {
if result := tryToolCall(toolMap[toolName], toolInput); result != "" {
return result
}
// 工具失败降级到 LLM
}

// 降级路径:LLM 对话
if result := tryLLMCall(llm, input); result != "" {
return result
}

// 最终降级:静态回复
return "抱歉,我暂时无法处理您的请求,请稍后再试。"
}

性能优化策略

1. 工具映射表

// 预构建映射表,避免线性查找
toolMap := make(map[string]tools.Tool)
for _, tool := range availableTools {
toolMap[tool.Name()] = tool
}

2. 关键词索引

// 构建关键词到工具的映射
keywordIndex := map[string]string{
"天气": "weather",
"时间": "current_time",
"计算": "calculator",
// ...
}

3. 缓存机制

type ResponseCache struct {
cache map[string]CacheEntry
mutex sync.RWMutex
}

func (c *ResponseCache) Get(key string) (string, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()

entry, exists := c.cache[key]
if !exists || time.Since(entry.Timestamp) > entry.TTL {
return "", false
}
return entry.Value, true
}

🎓 进阶开发指南

1. 添加新工具

步骤详解

// 1. 定义工具结构
type TranslateTool struct{}

// 2. 实现接口方法
func (t TranslateTool) Name() string {
return "translate"
}

func (t TranslateTool) Description() string {
return "翻译文本到指定语言,格式:'翻译 [文本] 到 [目标语言]'"
}

func (t TranslateTool) Call(ctx context.Context, input string) (string, error) {
// 解析输入:源文本和目标语言
sourceText, targetLang := parseTranslateInput(input)

// 调用翻译服务(示例)
result := callTranslateAPI(sourceText, targetLang)

return result, nil
}

// 3. 注册工具
availableTools = append(availableTools, TranslateTool{})

// 4. 添加识别规则
if strings.Contains(inputLower, "翻译") || strings.Contains(inputLower, "translate") {
return "translate", input
}

2. 自定义提示模板

高级模板示例

template := `你是一个专业的编程助手,具有以下特点:
- 擅长 {{.language}} 编程语言
- 注重代码质量和最佳实践
- 提供清晰的解释和示例

用户问题:{{.question}}
相关上下文:{{.context}}

请按照以下结构回答:
1. 问题理解:简要复述用户需求
2. 解决方案:提供具体的代码或方法
3. 解释说明:解释关键点和注意事项
4. 最佳实践:相关的编程建议

回答:`

// 使用模板
prompt := prompts.NewPromptTemplate(template, []string{"language", "question", "context"})

3. 集成外部服务

HTTP 客户端封装

type HTTPTool struct {
client *http.Client
baseURL string
apiKey string
timeout time.Duration
}

func (h *HTTPTool) Call(ctx context.Context, input string) (string, error) {
// 构建请求
req, err := h.buildRequest(ctx, input)
if err != nil {
return "", fmt.Errorf("构建请求失败: %w", err)
}

// 发送请求
resp, err := h.client.Do(req)
if err != nil {
return "", fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()

// 处理响应
return h.parseResponse(resp)
}

📊 监控与调试

日志系统

import "log/slog"

// 结构化日志
func logToolCall(toolName, input string, duration time.Duration, err error) {
slog.Info("tool_call",
"tool", toolName,
"input_length", len(input),
"duration_ms", duration.Milliseconds(),
"success", err == nil,
"error", err,
)
}

性能监控

type Metrics struct {
ToolCalls map[string]int64 // 工具调用次数
Latencies map[string][]time.Duration // 延迟分布
ErrorRates map[string]float64 // 错误率
}

func (m *Metrics) RecordToolCall(toolName string, duration time.Duration, err error) {
m.ToolCalls[toolName]++
m.Latencies[toolName] = append(m.Latencies[toolName], duration)
if err != nil {
m.ErrorRates[toolName]++
}
}

调试技巧

// 开发模式
const DEBUG = true

func debugLog(format string, args ...any) {
if DEBUG {
log.Printf("[DEBUG] "+format, args...)
}
}

// 在关键路径添加调试信息
func identifyTool(input string) (toolName, toolInput string) {
debugLog("识别工具输入: %s", input)

// ... 识别逻辑

debugLog("识别结果: tool=%s, input=%s", toolName, toolInput)
return toolName, toolInput
}

🎯 现在您已经掌握了项目的核心知识点和业务逻辑,可以开始深入学习和实践了!