大模型基础35 分钟Track 1

原生 Function Calling 闭环原理 (Function Calling)

抛开框架手写工具调用闭环 - 原生 SDK 实现声明、拦截、执行与回传

ATLAS NODE #02

原生 Function Calling 闭环原理

抛开三方框架的层层封装,通过最质朴的 Python 代码和原生 SDK。手把手解构大模型与本地系统深度交互的“大循环”运行核心机制,掌握拦截、寻址、执行、喂回的核心闭环。

预计掌握时间
35 分钟
阶段等级
Track 1 · 基础
关联 · 提示工程技术全景查看完整技术地图

以下 2 项原为《提示工程技术全景》中的空白项,现作为扩展承载补充至此,对应本节点的工具调用与本地执行能力。

扩展技术承载(弱覆盖)

弱 / 泛化
#9

ART (自动推理 + 工具)

在推理过程中自动决定调用外部工具(计算器、检索器、代码执行)获取中间事实,再继续推理,把『推理』与『工具使用』无缝交织。本节点原生 Function Calling 闭环正是 ART 的工程落地:模型决定调哪个工具,我们只在执行兜底。

强覆盖 · 本节点已配专门承载
Thought: 需计算复利 → Action: calculator(rate, years) Observe: 结果 1.34x Thought: 结合检索到的政策 → 继续推理
优势
  • 把不可靠的心算/记忆替换为可验证工具结果
  • 扩展模型能力边界
局限
  • 工具声明与参数拦截需谨慎
  • 工具失败需自愈兜底
#13

PAL (程序辅助推理)

让模型生成可执行程序(如 Python)来表达推理步骤,借助解释器运行得到精确答案,常用于数学/符号计算。本节点的『本地执行』环节即 PAL 思想:模型产出代码,运行时执行并把结果喂回。

强覆盖 · 本节点已配专门承载
def solve(): return sum(range(1, 101)) # 解释器执行 → 5050
优势
  • 数值与逻辑精确
  • 可复用既有代码生态
局限
  • 代码需沙箱执行
  • 错误处理较复杂

PAL 真演练场(强覆盖)

真实沙箱执行

与上方承载卡不同,这里是 真·演练场: 选好问题后,下方的 JavaScript 会在浏览器 受限沙箱 中真实执行,结果由运行时计算得出(而非预写死的字符串)。这正是 PAL「用代码执行替代不可靠心算」的核心价值。

1. 选择推理问题
模型生成的程序(Python 展示 · 只读)
print(sum(range(1, 101)))  # => 5050

PAL 论文中模型产出的是 Python;本演练场用等价的 JavaScript 在浏览器沙箱中真实运行, 思想完全一致——把推理步骤编码为可执行的程序。

2. 沙箱代码(可编辑,回车即改)
3. 执行结果(运行时真实计算)

点击上方按钮,代码将在此真实运行

对比:人脑逐项累加极易漏掉某个数;模型也可能给出 4950、5100 这类偏差值。而 PAL 把计算交给解释器,结果 精确且可复现。 (此对比为示意,非真实模型输出。)
本地沙箱已限制全局 / 网络访问(教学级防护)
Determinism: 100%
下方为 Function Calling 主交互区——PAL 的「代码执行」正是其「本地执行」环节的工程体现

ART 真演练场(强覆盖)

推理 × 工具交织

与上方承载卡不同,这里是 真·演练场: ART 把「推理」与「工具调用」无缝交织——模型在思考中 真实决定何时调用工具, 工具(计算器 / 贷款计算) 真实执行并返回数值,模型再基于真实结果继续推理。 点「下一步」逐步展开,或「一键跑完全程」。

1. 选择任务

进度:0 / 5 步。注意交替出现的「Thought(推理)」与「Tool(真实执行)」。

2. 推理 × 工具 交织链(实时)

点击「运行下一步」开始交织推理

工具调用真实执行(结果由运行时算出)
Tool-augmented Reasoning
下方为 Function Calling 主交互区——ART 正是其「推理与工具交织」的工程落地
闭环交互核心要点
1. Tools 声明与 API 拦截:模型并不直接去连接你的数据库或执行 API,它仅根据你定义的 JSON Schema 规范匹配用户意图,生成结构化 tool_call 标识。
2. 无框架底层运行时(Runtime):本地客户端(如你的 Python 进程)需要捕获到此特殊标识,将主线程挂起,并在本地执行具体函数,获取执行返回字符串。
3. 角色注入与对话闭环:必须将结果包装为 `role="tool"` 并附上原有会话历史重新递送给 LLM。LLM 读取该真实返回后,才能融合成普通自然语言答复用户。
第一步:选择交互模拟场景
选择以下一个具体的用户需求,并在右侧体验从声明到执行的闭环
当前匹配函数的 JSON Schema 声明:
{
  "name": "get_weather",
  "description": "获取指定城市的实时天气预报",
  "parameters": {
    "type": "object",
    "properties": {
      "city": { "type": "string", "description": "城市名称,例如北京、上海" }
    },
    "required": ["city"]
  }
}
第二步:手动触发无框架 Tool-use 事件循环
观察大模型与您的本地运行时如何轮流交接主控权
1
1. API声明
2
2. 模型生成
3
3. 本地执行
4
4. 结果喂回
5
5. 最终融和
Token 消耗
0 Tokens
等待运行
工具本地执行
0 次
等待运行
单次循环耗时
0.0 秒
等待运行
运行时控制台 (Local Runtime Logs)
点击右上角“启动闭环模拟”开始捕获无框架运行轨迹
该步骤原生 Python 代码实现片段:
# 准备就绪,请选择上方场景点击启动模拟
# 您也可以切换到上方【代码蓝图】标签,阅读无框架的完整可运行底层事件循环。
无框架 Python SDK 底层闭环运行源码 (Pure-Python Blueprint)
这是一份可以直接运行的完整本地 Python 脚本。它完全抛弃了 LangChain、LlamaIndex 等高层框架包装,仅使用谷歌官方原生的 `@google/genai` 开发,完美演示了拦截与喂回闭环逻辑。
import os
from google import genai
from google.genai import types

# 1. 声明本地 Python 工具函数
def get_weather(city: str) -> str:
    """获取指定城市的实时天气预报"""
    # 真实的本地函数,执行网络请求或数据库查询
    if "北京" in city:
        return "北京明日天气:晴转多云,22°C ~ 31°C,微风。"
    return f"{city}明日天气状况良好。"

def calculate_tax(income: float, region: str) -> str:
    """计算中国不同地区的个人所得税"""
    taxable = max(0.0, income - 60000)
    tax = taxable * 0.1 - 2520
    return f"针对{region}地区,年薪 {income} 元的个税计算结果为:实际应纳税额 {tax:.2f} 元。"

# 建立工具名称到函数的物理映射,供拦截后寻址
tool_map = {
    "get_weather": get_weather,
    "calculate_tax": calculate_tax
}

# 2. 初始化原生 SDK (未封装框架)
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

def run_pure_tool_loop(user_prompt: str):
    print(f"User: {user_prompt}")
    
    # 将真实的 Python 函数作为 tools 传入,SDK 会自动解析函数签名生成 JSON Schema 声明
    # 对应的就是 [阶段 1: API Tool 声明]
    response = client.models.generate_content(
        model='gemini-2.5-flash',
        contents=user_prompt,
        config=types.GenerateContentConfig(
            tools=[get_weather, calculate_tax]
        )
    )
    
    # 模拟手写运行时事件循环 (Event Loop)
    chats = [{"role": "user", "parts": [user_prompt]}]
    
    # [阶段 2: 拦截与识别 <tool_call>]
    if response.function_calls:
        print("\n🤖 模型生成了 Tool Call 申请!")
        # 复制第一轮 assistant 响应到对话历史中
        chats.append(response.candidates[0].content)
        
        # 遍历所有的 tool_call 请求并依次在本地拦截执行
        for call in response.function_calls:
            print(f"📥 正在解析函数: {call.name}, 参数: {call.args}")
            
            # [阶段 3: 本地运行时拦截寻址执行]
            if call.name in tool_map:
                # 动态执行本地函数并安全获取返回值
                local_func = tool_map[call.name]
                execution_result = local_func(**call.args)
                print(f"🔌 本地执行结果: {execution_result}")
                
                # [阶段 4: 结果喂回上下文]
                # 构造符合规范的 'tool' 角色回复消息
                tool_response = types.Part.from_function_response(
                    name=call.name,
                    response={"result": execution_result}
                )
                
                chats.append(types.Content(
                    role="tool",
                    parts=[tool_response]
                ))
            
        # [阶段 5: 模型融合成最终回复]
        # 将携带了 Tool 执行结果的完整会话上下文,重新喂回给大模型
        final_response = client.models.generate_content(
            model='gemini-2.5-flash',
            contents=chats,
            config=types.GenerateContentConfig(
                tools=[get_weather, calculate_tax]
            )
        )
        print(f"\n💬 Final Assistant: {final_response.text}")
    else:
        print(f"💬 Direct Assistant: {response.text}")

if __name__ == "__main__":
    run_pure_tool_loop("查询北京明天的天气")
温馨提示:只要将 os.environ["GEMINI_API_KEY"] 设定为您在 AI Studio 的 API Key,并执行此脚本,您即可在终端内真实打印出以上 5 步闭环日志,感受到程序与模型深度融合的“手写”魅力!