大模型为什么要和外部世界连接?

  • 训练数据不涵盖所有信息,比如垂直、非公开的数据
  • 模型数据无法实时更新,训练一次消耗大
  • 大模型表现得逻辑、推理其实是训练文本的统计规律,结果拥有不确定性

因此需要对接真逻辑系统,来控制输出的不确定性和幻觉,达到预期效果。

OpenAI Function Calling:https://platform.openai.com/docs/guides/function-calling

function calling的定义:是一种让模型调用外部函数的能力。模型不仅仅根据自身数据库知识进行问答,还可以额外挂载一个函数库,然后根据用户提问,模型返回调用指令,应用执行调用指令,模型再基于调用函数的结果进行回答。

function calling的完整调用流程:

JSON Schema:用于描述JSON数据格式和结构的元数据标注。

参考⽹址:https://json-schema.org/learn/getting-started-step-by-step

function 调用示例(获取高德地图开放接口)

from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
import requests
import json
from typing import Optional, Dict, Any, List  #导入注释包
api_key = os.getenv("SPARKAI_API_KEY") # 替换自己的api key和url
api_base = os.getenv("SPARKAI_API_BASE")
client = OpenAI(api_key=api_key, base_url=api_base)


# 假设 amap_key 在模块级变量或配置中提供
amap_key = os.getenv("AMAP_KEY")#高德地图申请免费key

def get_location_coordinate(location: str, city: str = "北京", timeout: int = 5) -> Optional[Dict[str, Any]]:
    """
    使用 高德的 Place Text 检索(text)接口,根据关键词和城市返回第一个 POI(若有)。
    返回 POI dict(原始 API 返回的 pois[0]),或 None(无结果或出错)。
    """
    url = "https://restapi.amap.com/v5/place/text"
    params = {
        "key": amap_key,
        "keywords": location,
        # 高德 v5 文档中可使用 region 或 city,通常 region 更通用(也可用 city)
        "region": city,
        "limit": 1,      # 只要第一个结果
        "output": "JSON"
    }
    try:
        resp = requests.get(url, params=params, timeout=timeout)
        resp.raise_for_status()
        result = resp.json()
    except Exception as e:
        # 记录日志(此处简化为打印)
        print(f"get_location_coordinate 请求出错: {e}")
        return None

    # 高德接口会返回一个结构,通常包含 count、pois 等
    pois = result.get("pois")
    if pois:
        return pois[0]
    return None


def format_pois_text(pois):
    """将 POI 列表拼接为文本"""
    lines = []
    for p in pois:
        name = p.get("name", "未知名称")
        address = p.get("address", "未知地址")
        distance = p.get("distance", "")
        if distance:
            distance_str = f"距离:{distance}米"
        else:
            distance_str = ""

        lines.append(f"{name}\n{address}\n{distance_str}\n")
    return "\n".join(lines)


def search_nearby_pois(longitude: float, latitude: float, keyword: str,
                       radius: int = 2000, limit: int = 3, timeout: int = 5) -> str:
    """
    周边检索并直接返回格式化好的文本(使用 format_pois_text)
    """
    url = "https://restapi.amap.com/v5/place/around"
    params = {
        "key": amap_key,
        "keywords": keyword,
        "location": f"{longitude},{latitude}",
        "radius": radius,
        "limit": limit,
        "sortrule": "distance"
    }

    try:
        resp = requests.get(url, params=params, timeout=timeout)
        resp.raise_for_status()
        result = resp.json()
    except Exception as e:
        print("search_nearby_pois error:", e)
        return "查询失败,请稍后再试。"

    pois = result.get("pois", [])[:limit]
    if not pois:
        return "附近没有找到相关地点。"

    return format_pois_text(pois)

#大模型调用方法

def generate_from_llm(messages,model="spark-x"):
    try:
        response = client.chat.completions.create(
            model=model,   # 模型可替换
            messages=messages,
            temperature=0,
            tools=[{
                "type": "function",
                "function": {
                    "name": "get_location_coordinate",
                    "description": "根据地点名称和城市,获取该地点的经纬度坐标。",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "location": {
                                "type": "string",
                                "description": "POI名称必须为中文"
                            },
                            "city": {
                                "type": "string",
                                "description": "POI所在城市的名称,必须为中文"
                            }
                        },
                        "required": ["location", "city"],
                    }
                }
            },
                {
                    "type": "function",
                    "function": {
                        "name": "search_nearby_pois",
                        "description": "根据经纬度坐标和关键词,搜索附近的POI信息。",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "longitude": {
                                    "type": "number",
                                    "description": "经度"
                                },
                                "latitude": {
                                    "type": "number",
                                    "description": "纬度"
                                },
                                "keyword": {
                                    "type": "string",
                                    "description": "目标POI的搜索关键词"
                                }
                            },
                            "required": ["longitude", "latitude", "keyword"],
                        }
                    }
                }
            ]

        )
        return response.choices[0].message
    except Exception as e:
        return f"-- 生成 SQL 时出错:{e}"

prompt = "上海陆家嘴附近的咖啡"
messages = [
 {"role": "system", "content": "你是⼀个地图通,你可以找到任何地址。"},
 {"role": "user", "content": prompt}
]
response = generate_from_llm(messages)
if (response.content is None): # 解决 OpenAI 的⼀个 400 bug
     response.content = ""
messages.append(response) # 把⼤模型的回复加⼊到对话中,包括传入的参数和调用哪个函数
print("=====AI回复=====")
print(response)
# 如果返回的是函数调⽤结果,则打印出来
while (response.tool_calls is not None):
 # 1106 版新模型⽀持⼀次返回多个函数调⽤请求
   for tool_call in response.tool_calls:
     args = json.loads(tool_call.function.arguments)
     print(args)
     if (tool_call.function.name == "get_location_coordinate"):
       print("Call: get_location_coordinate")
       result = get_location_coordinate(**args)
     elif (tool_call.function.name == "search_nearby_pois"):
       print("Call: search_nearby_pois")
       result = search_nearby_pois(**args)
     print("=====函数返回=====")
     print(result)
     messages.append({
          "tool_call_id": tool_call.id, # ⽤于标识函数调⽤的 ID
          "role": "tool",
          "name": tool_call.function.name,
          "content": str(result) # 数值result 必须转成字符串
 })
     response = generate_from_llm (messages)
     if (response.content is None): # 解决 OpenAI 的⼀个 400 bug
          response.content = ""
     messages.append(response) # 把⼤模型的回复加⼊到对话中
print("=====最终回复=====")
print(response.content)


重点详解:

tools参数: ⽤ JSON 格式描述函数。可以定义多个,由⼤模型决定调⽤谁。

tools=[{
                "type": "function",
                "function": {
                    "name": "get_location_coordinate",
                    "description": "根据地点名称和城市,获取该地点的经纬度坐标。",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "location": {
                                "type": "string",
                                "description": "POI名称必须为中文"
                            },
                            "city": {
                                "type": "string",
                                "description": "POI所在城市的名称,必须为中文"
                            }
                        },
                        "required": ["location", "city"],
                    }
                }
            },
                {
                    "type": "function",
                    "function": {
                        "name": "search_nearby_pois",
                        "description": "根据经纬度坐标和关键词,搜索附近的POI信息。",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                "longitude": {
                                    "type": "number",
                                    "description": "经度"
                                },
                                "latitude": {
                                    "type": "number",
                                    "description": "纬度"
                                },
                                "keyword": {
                                    "type": "string",
                                    "description": "目标POI的搜索关键词"
                                }
                            },
                            "required": ["longitude", "latitude", "keyword"],
                        }
                    }
                }
            ]

JSON Schema自动生成工具:

def auto_functions(func_list: List[Callable]) -> List[dict]:
    tools_description = []
    for func in func_list:
        # 获取函数的签名信息
        sig = inspect.signature(func)
        func_params = sig.parameters
        # 函数的参数描述
        parameters = {
            'type': 'object',
            'properties': {},
            'required': []
        }
        for param_name, param in func_params.items():
            # 添加参数描述和类型
            parameters['properties'][param_name] = {
                'description': param.annotation.__doc__ if param.annotation is not inspect._empty else "",
                'type': str(param.annotation) if param.annotation != param.empty else 'Any'
            }
            # 如果参数有默认值,那么它不是必须的
            if param.default != param.empty:
                parameters['required'].append(param_name)
        # 函数描述字典
        func_dict = {
            "type": "function",
            "function": {
                "name": func.__name__,
                "description": func.__doc__.strip(),
                "parameters": parameters
            }
        }

        tools_description.append(func_dict)
    return tools_description

一次function calling中,大模型第一次返回的结果如下,后续每次调用时一定要先把之前历史消息response和id加到messages中。

ChatCompletionMessage(
    content='', #content内容为空,因为模型选择调用工具(function),就不再返回自然语言文本
    refusal=None,
    role='assistant', #模型生成的回复,不是用户消息
    annotations=None,
    audio=None,
    function_call=None,
    tool_calls=[           #模型请求你调用一个函数
        ChatCompletionMessageFunctionToolCall(
            id='Call_000dcbfb@dx19aeed4d0cdb81c3321',
            function=Function(
                arguments='{"city":"上海","location":"陆家嘴"}',
                name='get_location_coordinate'
            ),
            type='function'
        )
    ],#模型调用函数的原因
    reasoning_content='用户想找上海陆家嘴附近的咖啡,首先需要获取陆家嘴的经纬度坐标,因为search_nearby_pois工具需要经纬度和关键词。可用get_location_coordinate工具,城市是上海,地点是陆家嘴,这样就能得到陆家嘴的经纬度,之后才能用这个经纬度搜索附近的咖啡。所以先调用get_location_coordinate。'
)

聚合平台:juhe.cn 

爬虫:spidertools.cn

魔搭社区:https://modelscope.cn/models

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐