Skip to content

Day 3:加RAG——让Agent有知识

昨天的问题

Day 2 的 Agent 有人设了,但它的"知识"全靠关键词匹配。用户问"你们的退款政策是什么",它只能靠 Mock LLM 瞎猜。

今天做什么

接入 RAG(检索增强生成)。把公司的产品文档、退款政策、常见问题存进知识库,用户提问时先检索相关内容,再让 LLM 基于真实资料回答。

代码

go
package main

import (
    "context"
    "fmt"
    "strings"

    "github.com/cloudwego/eino/components/model"
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/schema"
)

// ===== 知识库:公司文档(模拟) =====
var knowledgeBase = map[string]string{
    "退款政策": "我们支持7天无理由退款。退款金额将在3-5个工作日内退回原支付方式。",
    "退货流程": "请先提交退货申请,审核通过后将发送退货地址。退回商品需保持原包装完整。",
    "配送时效": "全国大部分地区2-3天送达。偏远地区(新疆、西藏等)5-7天。",
    "客服时间": "在线客服:每天9:00-22:00。电话客服:工作日9:00-18:00。",
}

// ===== 简易向量检索(模拟语义匹配) =====
func searchKnowledge(query string) string {
    var bestMatch string
    maxScore := 0

    for topic, content := range knowledgeBase {
        // 简单的关键词匹配(生产中替换为真正的向量检索)
        score := 0
        queryWords := strings.Fields(query)
        topicWords := strings.Fields(topic)
        for _, qw := range queryWords {
            for _, tw := range topicWords {
                if strings.Contains(qw, tw) || strings.Contains(tw, qw) {
                    score++
                }
            }
        }
        if score > maxScore {
            maxScore = score
            bestMatch = content
        }
    }

    if bestMatch == "" {
        return "未找到相关文档"
    }
    return bestMatch
}

// ===== 带 RAG 的 Agent =====
type CustomerServiceAgent struct {
    model ChatModel
}

type ChatModel interface {
    Generate(ctx context.Context,
        input []*schema.Message, opts ...model.Option) (*schema.Message, error)
}

func (a *CustomerServiceAgent) Answer(ctx context.Context,
    question string) (string, error) {

    // 第一步:检索相关知识
    context := searchKnowledge(question)

    // 第二步:拼接 Prompt
    input := []*schema.Message{
        schema.SystemMessage(systemPrompt + fmt.Sprintf(
            "\n\n你可以参考以下公司文档回答问题。如果文档中没有相关信息,诚实地说不知道。\n\n公司文档:\n%s", context)),
        schema.UserMessage(question),
    }

    // 第三步:让 LLM 基于文档生成回答
    result, err := a.model.Generate(ctx, input)
    if err != nil {
        return "", err
    }
    return result.Content, nil
}

func main() {
    ctx := context.Background()
    agent := &CustomerServiceAgent{model: &MockChatModel{}}

    questions := []string{
        "退款政策是什么?",
        "几天能收到货?",
        "客服几点下班?",
    }

    for _, q := range questions {
        answer, err := agent.Answer(ctx, q)
        if err != nil {
            fmt.Printf("❌ 错误: %v\n", err)
            continue
        }
        fmt.Printf("👤 用户: %s\n", q)
        fmt.Printf("🤖 小智: %s\n\n", answer)
    }
}

运行

bash
go run main.go

你学到了什么

  1. RAG 的核心流程:问题 → 检索 → 拼接 → 生成。Agent 不再瞎编,基于真实文档回答。
  2. 知识库设计:文档按主题组织,检索用关键词匹配(生产中用向量数据库替换)。
  3. 架构不变CustomerServiceAgent.Answer() 的接口没变——外部调用方式完全一样,内部加了检索。

明天的预告

Agent 会查文档了,但它还不会"动手"——比如帮用户查订单状态。明天我们给它装上 Eino Tool,让它真正能干活的工具。


Day 3 完成。你的 Agent 现在能查公司文档了。