Function Calling 原理
Function Calling 让大模型连接真实世界/业务系统,三步流程(把 AI 当人看): ```text 1. LLM 读工具清单(schema) → 判断"要不要调、调哪个" (≈人读API文档+读需求) 2. 生成调用参数(arguments JSON) (≈人写调用代码) 3. 外层程序真正执行函数 → 结果回填 → LLM 整合回答 (≈人运行程序+整理结论) ``` 注意:蒸馏版/部分本地模型不支持原生 func call,但可用自定义 prompt 实现(见 tpf 双引擎)。 典型应用:RAG 检索触发、结构化入库、调用外部 API/启动服务等。
OpenAI 标准 tools 定义(四要素 + strict): ```python tools = [{ "type": "function", "function": { "name": "get_location_coordinate", "description": "根据地点描述获取经纬度", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "地点名,如 五道口"}, "region": {"type": "string", "description": "所在城市"} }, "required": ["location"], "additionalProperties": False # strict 模式必须显式给出 }, "strict": True } }] resp = client.chat.completions.create(model="gpt-4o", messages=..., tools=tools) for tc in resp.choices[0].message.tool_calls: args = json.loads(tc.function.arguments) # LLM 生成的参数 result = dispatch(tc.function.name, args) # 真正执行 messages.append({"role":"tool","tool_call_id":tc.id,"content":json.dumps(result)}) ``` 回填用 `role="tool"` + `tool_call_id`,再发起第二次请求让模型整合。
func call 对模型的三大能力要求: 1. **问题理解与行动规划**(思维链):先想清楚分几步、每步用哪个工具 2. **结构化输出**:arguments 必须是合法 JSON,字段符合 schema 3. **上下文理解**:工具返回结果要能读懂并整合进最终回答 链式场景(查坐标→搜周边POI)依赖模型自主传递中间结果——这正是 Agent 的雏形。
注册与调用(tpf FuncCall)
tpf 的注册机制——`register_function(*, name, description, parameters, function)` 维护**双映射**: - `_function_infos`:喂给 prompt 的工具清单(OpenAI 格式) - `_function_mappings`:name → 真实函数对象,执行时 `json.loads(arguments)` 后调用 ```python from tpf.llm import MyChat chat = MyChat(env_file="env.txt") func_dcn = { "name": "dcn_start_service", "description": "启动指定服务器的dcn服务", "parameters": {"type": "object", "properties": {"host": {"type": "string", "description": "主机名"}}}, } chat.register_function(**func_dcn, function=lambda host: start(host)) print(chat.tool_list()) # 查看已注册清单 print(chat.prompt_system()) # 查看注入 prompt 的系统提示(含工具列表+输出格式+示例) ```
`prompt_system()` 是"自定义 func call"的核心模板(适用于不支持原生 tools 的模型): ```text 你可以调用以下内部函数: {函数名}({参数}): {描述} ... #输出格式: {"name": 函数名, "arguments": {参数dict}, "description": 调用描述} #示例 {若干 ask/answer 示例} ``` 用户侧 `prompt_user()` 还会附加 5 条"输出前思考"检查清单(是否需要函数、参数是否齐全、能否直接回答等)。
链式注册两个函数完成复合任务: ```python from tpf.llm import FuncCall fc = FuncCall() fc.register_function(name="get_location_coordinate", description="获取地点经纬度", parameters={"type":"object","properties":{ "location":{"type":"string"},"region":{"type":"string"}}, "required":["location"]}, function=get_coordinate) fc.register_function(name="search_nearby_pois", description="搜索周边兴趣点", parameters={"type":"object","properties":{ "keyword":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"}}, "required":["keyword","latitude","longitude"]}, function=search_pois) res = fc.chat("五道口附近的咖啡馆", model_list=["gpt-4o", "DeepSeek-R1-14B-Q8:latest"], func_index=0, answer_index=0) ``` `func_index`:负责"判断+调函数"的模型下标;`answer_index`:负责"整合结果作答"的模型下标(None 时默认等于 func_index,可直接返回函数结果)。
tpf 双引擎与常用工具
`FuncCall.chat()` / `MyChat.*_func_call()` 的统一两段式: ```text 路径A(在线/原生tools):_request() 走 OpenAI tools 协议 create(tools=...) → tool_calls → _call() 执行 → role=tool 回填 → 整合 路径B(本地ollama模型):prompt_system() + prompt_user() 注入工具清单 → chat() 对话 → 模型输出 ```json``` 工具调用 → tools_call() 逐个执行 ``` 第二段"整合作答"提示词核心: ```text #用户输入:{query} #内部函数调用返回的结果:{func_call_res} 请结合用户输入的问题以及内部函数调用结果回答, - 简洁风格,不思考,不推理 #输出格式:{"query":..., "result":...} ```
本地 ollama 模型走自定义流程的配置: ```python chat.set_local_model("DeepSeek-R1-14B-Q8:latest") # 注册进本地名单 # answer 模型若是本地模型 → 一次请求内完成(消息里带 tool 结果) response = chat.func_call(query, model_list=[...], func_index=3, answer_index=2, base_url='http://localhost:11434/v1/') ``` `tools_call()` 解析失败时打印原文并退出(sys.exit)——生产使用建议改为重试。
两个高频工具模式: **sqlite 查询**(func call 的经典练习题): ```python from tpf.db import SqLite sdb = SqLite(db='vo.db') df = sdb.select("select * from user", columns=["id","name","age"]) ``` **代码执行**(给 agent 的"计算器",带安全边界): ```python from tpf.box import zx res1 = zx.check_grammar_noexec(code) # 仅语法检查 → {'status':'ok'} res2 = zx.exec_python_code(source) # subprocess+tempfile+timeout=10 → {'status':'ok','result':'(2, 2)'} ``` timeout 防死循环 + 只回传结果字符串,是"让 LLM 用工具而非自己算"的典型设计。
参考
     Function Calling 官方文档