02-function-calling-rag

知识库
知识库文档
/tech-stacks/openai/examples/02-function-calling-rag.md

文档

OpenAI Function Calling + RAG 构建 AI Agent

目标

  • 掌握 Function Calling(工具调用)机制
  • 构建基于 Embeddings 的 RAG 知识库问答
  • 实现完整的 AI Agent 循环

完整代码

1. Function Calling —— 让 GPT 调用你的函数

import json
from openai import OpenAI

client = OpenAI()

# ============================================================
# 定义工具(函数)
# ============================================================
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,如 Beijing, Shanghai"
                    }
                },
                "required": ["city"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "search_database",
            "description": "在内部数据库中搜索订单信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {"type": "string"},
                    "customer_name": {"type": "string"},
                },
            },
        },
    },
]


# 模拟函数实现
def get_weather(city: str) -> dict:
    """模拟天气查询"""
    return {
        "city": city,
        "temperature": 22,
        "condition": "晴朗",
        "humidity": "45%",
    }


def search_database(order_id: str = None, customer_name: str = None) -> dict:
    """模拟数据库搜索"""
    return {
        "orders": [
            {"id": "ORD-001", "product": "MacBook Pro", "status": "已发货"},
        ]
    }


# 函数映射表
available_functions = {
    "get_weather": get_weather,
    "search_database": search_database,
}


def run_agent(user_query: str):
    """AI Agent 主循环"""
    messages = [{"role": "user", "content": user_query}]

    # 第一次调用:GPT 决定是否要调用工具
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        tools=tools,
        tool_choice="auto",
    )

    response_message = response.choices[0].message
    tool_calls = response_message.tool_calls

    # 如果 GPT 需要调用工具
    if tool_calls:
        messages.append(response_message)

        for tool_call in tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)

            print(f"🔧 调用函数: {function_name}({function_args})")

            # 执行函数
            function_to_call = available_functions[function_name]
            function_response = function_to_call(**function_args)

            # 将函数结果添加到消息中
            messages.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": json.dumps(function_response, ensure_ascii=False),
            })

        # 第二次调用:GPT 根据函数结果生成最终回复
        final_response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
        )

        return final_response.choices[0].message.content

    return response_message.content


# 测试
print(run_agent("北京今天天气怎么样?"))
print(run_agent("帮我查一下订单 ORD-001 的状态"))
print(run_agent("你好,你是谁?"))  # 不需要工具调用

2. RAG(检索增强生成)

import numpy as np

# ============================================================
# 构建知识库
# ============================================================
documents = [
    "Vibe 平台是一个智能元器件管理系统,支持元器件搜索、文档生成和代码例程。",
    "用户可以使用自然语言搜索技术栈,如 React、Spring Boot、PyTorch等。",
    "平台支持自动生成 Markdown 格式的技术文档和代码示例。",
    "价格单位为分(cents),1元 = 100分。",
    "所有回复必须使用中文,元器件处理需要按工作流顺序执行。",
]

# 获取 Embeddings
def get_embeddings(texts: list[str]) -> list[list[float]]:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=texts,
    )
    return [d.embedding for d in response.data]


doc_embeddings = get_embeddings(documents)
print(f"知识库大小: {len(documents)} 条文档, 向量维度: {len(doc_embeddings[0])}")

# ============================================================
# 语义搜索
# ============================================================
def semantic_search(query: str, top_k: int = 3) -> list[str]:
    """根据语义相似度检索最相关的文档"""
    query_embedding = get_embeddings([query])[0]

    # 计算余弦相似度
    def cosine_similarity(a, b):
        return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

    similarities = [
        cosine_similarity(query_embedding, doc_emb)
        for doc_emb in doc_embeddings
    ]

    # 取 top-k
    top_indices = np.argsort(similarities)[-top_k:][::-1]
    return [documents[i] for i in top_indices]


# ============================================================
# RAG 问答
# ============================================================
def rag_qa(query: str) -> str:
    """基于知识库的问答"""
    # 1. 检索相关文档
    relevant_docs = semantic_search(query, top_k=3)
    context = "\n\n".join(relevant_docs)

    print(f"📚 检索到 {len(relevant_docs)} 条相关文档")

    # 2. 用检索到的上下文增强 Prompt
    system_prompt = f"""你是一个知识库助手。请基于以下上下文回答问题。
如果上下文不足以回答,请说明。

上下文:
{context}"""

    # 3. 调用 GPT 生成答案
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": query},
        ],
        temperature=0.3,  # 降低随机性
    )

    return response.choices[0].message.content


# 测试
print("\n" + "="*50)
print(rag_qa("价格单位是什么?"))
print("\n" + rag_qa("如何搜索技术栈?"))

3. 完整的 Agent + RAG 架构

用户提问
    │
    ▼
┌─────────────────────┐
│   Router (GPT-4o)    │ → 判断:需要工具?需要检索?
└──────┬──────────────┘
       │
  ┌────┼────┐
  ▼    ▼    ▼
Tool  RAG  Direct
  │    │    │
  └────┼────┘
       ▼
   Final Answer

关键要点

概念 说明
tools 参数 向 GPT 注册可调用函数
tool_choice: "auto" GPT 自行决定是否调用工具
role: "tool" 返回函数执行结果给 GPT
text-embedding-3-small 性价比最高的嵌入模型
余弦相似度 衡量向量间语义相似度的标准方法
RAG vs 微调 RAG 更新知识只需改文档,微调需重新训练

信息

路径
/tech-stacks/openai/examples/02-function-calling-rag.md
更新时间
2026/5/30