news 2026/7/8 5:46:23

别再写 if/else 了:让 LLM 自己决定调用哪个函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再写 if/else 了:让 LLM 自己决定调用哪个函数

假设现在手里有 3 个函数

  • 1)获取 Pod 状态,主要通过describe 查看 k8s pod,get_pod_status

    • Pod 是否 Running
    • 有没有 CrashLoopBackOff
    • 重启了多少次
    • 最近事件里有没有探针失败、OOM、镜像拉取失败
  • 2)获取业务日志,通过调用日志平台接口获取对应日志,get_business_log

    • 接口为什么返回 500
    • 有没有空指针
    • 有没有数据库超时
    • 有没有调用下游失败
  • 3)获取操作系统监控数据,通过调用prometheus获取底层监控,get_system_metrics

    • CPU 是否打满
    • 内存是否不足
    • 磁盘是否写满
    • 网络流量是否异常
函数描述
  • 获取 Kubernetes Pod 的状态、重启次数和事件信息,用于排查 Pod 启动失败、异常重启、探针失败等问题
  • 获取业务服务日志,用于排查接口报错、代码异常、数据库超时等问题
  • 获取节点操作系统监控数据,用于排查 CPU、内存、磁盘、网络资源瓶颈问题

函数描述非常重要,LLM主要靠它来指导模型决策以及函数选取

定义三个函数
import json from datetime import datetime, timedelta def get_pod_status(namespace: str = "default", pod_name: str = ""): return { "namespace": namespace, "pod_name": pod_name, "status": "CrashLoopBackOff", "restart_count": 5, "node": "10.10.1.23", "events": [ "2026-04-20 11:42:11 Readiness probe failed: connection refused", "2026-04-20 11:42:25 Container restarted", "2026-04-20 11:42:25 Last State: Terminated, Reason: OOMKilled" ] } def get_business_log(service_name: str = "", start_time: str = "", end_time: str = ""): if not start_time: start_time = (datetime.now() - timedelta(minutes=30)).strftime("%Y-%m-%d %H:%M:%S") if not end_time: end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") return { "service_name": service_name, "time_range": { "start_time": start_time, "end_time": end_time }, "logs": [ "2026-04-20 11:41:02 [ERROR] order-service create order failed: java.lang.NullPointerException", "2026-04-20 11:41:03 [ERROR] order-service db timeout when inserting order record", "2026-04-20 11:41:04 [WARN] order-service retry failed, return HTTP 500" ] } def get_system_metrics(node_ip: str = "", start_time: str = "", end_time: str = ""): if not start_time: start_time = (datetime.now() - timedelta(minutes=30)).strftime("%Y-%m-%d %H:%M:%S") if not end_time: end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") return { "node_ip": node_ip, "time_range": { "start_time": start_time, "end_time": end_time }, "metrics": { "cpu_usage": "91%", "memory_usage": "93%", "disk_usage": "58%", "network_rx": "110MB/s", "network_tx": "76MB/s" } } TOOL_MAP = { "get_pod_status": get_pod_status, "get_business_log": get_business_log, "get_system_metrics": get_system_metrics, }
定义工具描述
tools = [ { "type": "function", "function": { "name": "get_pod_status", "description": "获取 Kubernetes Pod 的运行状态、重启次数和事件信息,用于排查 Pod 异常重启、启动失败、探针失败、CrashLoopBackOff 等问题。", "parameters": { "type": "object", "properties": { "namespace": { "type": "string", "description": "Pod 所在命名空间,默认 default" }, "pod_name": { "type": "string", "description": "Pod 名称或者工作负载名称" } }, "required": ["pod_name"] } } }, { "type": "function", "function": { "name": "get_business_log", "description": "获取业务服务日志,用于排查接口报错、HTTP 500、代码异常、数据库超时、调用下游失败等问题。", "parameters": { "type": "object", "properties": { "service_name": { "type": "string", "description": "服务名称,比如 order-service" }, "start_time": { "type": "string", "description": "查询开始时间,格式 YYYY-MM-DD HH:MM:SS" }, "end_time": { "type": "string", "description": "查询结束时间,格式 YYYY-MM-DD HH:MM:SS" } }, "required": ["service_name"] } } }, { "type": "function", "function": { "name": "get_system_metrics", "description": "获取节点操作系统监控数据,包括 CPU、内存、磁盘和网络使用情况,用于排查节点负载高、资源瓶颈、系统卡顿等问题。", "parameters": { "type": "object", "properties": { "node_ip": { "type": "string", "description": "节点 IP 地址" }, "start_time": { "type": "string", "description": "查询开始时间,格式 YYYY-MM-DD HH:MM:SS" }, "end_time": { "type": "string", "description": "查询结束时间,格式 YYYY-MM-DD HH:MM:SS" } }, "required": ["node_ip"] } } } ]
模型决定调用

下面这段代码,是整篇文章最核心的部分。

  • 把用户问题和工具列表发给模型
  • 如果模型决定调用函数,就执行对应函数
  • 把函数执行结果再喂给模型,让它输出最终结论
import json from litellm import completion def run_agent(user_query: str): messages = [ { "role": "system", "content": ( "你是一个线上故障排查助手。" "请根据用户问题自主选择最合适的工具。" "如果一个工具不足以定位问题,可以继续调用其他工具。" "回答时要基于工具返回的事实,不要编造。" ) }, { "role": "user", "content": user_query } ] while True: response = completion( model="doubao-seed-2.0-pro", messages=messages, tools=tools, tool_choice="auto" ) message = response.choices[0].message tool_calls = message.tool_calls if not tool_calls: return message.content messages.append(message) for tool_call in tool_calls: tool_name = tool_call.function.name tool_args = json.loads(tool_call.function.arguments or "{}") print(f"[TOOL] {tool_name} args={tool_args}") tool_func = TOOL_MAP[tool_name] tool_result = tool_func(**tool_args) messages.append( { "role": "tool", "tool_call_id": tool_call.id, "name": tool_name, "content": json.dumps(tool_result, ensure_ascii=False) } )
验证结果

选取了4个不同的问题,查看llm调用函数的情况

if __name__ == "__main__": test_queries = [ "我的 order-service Pod 一直重启,帮我看看怎么回事", "下单接口一直返回 500,帮我查一下日志", "节点 10.10.1.23 最近特别卡,帮我看下是不是资源打满了", "order-service 返回 500,而且 Pod 也反复重启,帮我整体分析一下" ] for idx, query in enumerate(test_queries, start=1): print(f"\n===== 测试 {idx} =====") print(f"[USER] {query}") answer = run_agent(query) print(f"[FINAL] {answer}")
参考输出

并不是每一次运行都是同样的内容,这里只展现本次运行llm的输出

===== 测试 1 ===== [USER] 我的 order-service Pod 一直重启,帮我看看怎么回事 [TOOL] get_pod_status args={'pod_name': 'order-service'} [TOOL] get_business_log args={'service_name': 'order-service', 'start_time': '2026-04-20 11:30:00', 'end_time': '2026-04-20 11:45:00'} [TOOL] get_system_metrics args={'node_ip': '10.10.1.23', 'start_time': '2026-04-20 11:30:00', 'end_time': '2026-04-20 11:45:00'} [FINAL] 经过排查,order-service Pod 持续重启的原因如下: ### 根因分析 1. **Pod 进程被强制终止**:Pod 状态为 CrashLoopBackOff,累计重启5次,进程终止原因是 `OOMKilled`(内存溢出被系统Kill),进程退出后导致就 绪探针探测失败报 connection refused,触发自动重启。 2. **业务代码异常**:服务运行时存在业务报错,包括空指针异常、订单数据插入数据库超时、重试失败返回HTTP 500,这类异常会导致服务资源占用异 常升高,是引发内存溢出的诱因之一。 3. **节点资源瓶颈**:Pod 所在节点10.10.1.23在故障时间段CPU使用率达91%、内存使用率达93%,节点本身负载极高,进一步加剧了Pod的资源紧张问题 ,加速触发OOMKill。 ### 修复建议 1. 优先修复业务代码中的空指针异常、数据库超时问题,优化资源占用逻辑; 2. 调整 order-service Pod 的内存limit配置,适当调高内存上限; 3. 可将该Pod临时调度到其他低负载节点运行,同时对10.10.1.23节点进行资源扩容或者清理节点上多余的负载。
===== 测试 2 ===== [USER] 下单接口一直返回 500,帮我查一下日志 [TOOL] get_business_log args={'service_name': 'order-service'} [FINAL] 根据查询order-service服务的业务日志,下单接口返回500的原因已经定位到: 1. 订单创建逻辑触发了Java空指针异常(NullPointerException) 2. 插入订单记录到数据库时出现数据库超时问题 3. 超时重试多次失败,最终接口返回HTTP 500错误 如果需要进一步排查数据库超时的根因,可以提供数据库所在节点的IP查询系统资源负载,也可以查询order-service服务的Pod运行状态确认是否存在服务本身资源不足、异常重启等问题。
===== 测试 3 ===== [USER] 节点 10.10.1.23 最近特别卡,帮我看下是不是资源打满了 [TOOL] get_system_metrics args={'node_ip': '10.10.1.23'} [FINAL] 根据查询到的节点10.10.1.23最近30分钟的系统监控数据: 1. CPU使用率达91%,内存使用率达93%,二者均处于极高负载状态,这是导致节点卡顿的核心原因 2. 磁盘使用率为58%,处于正常水平 3. 网络入流量110MB/s、出流量76MB/s,也处于较高负载水平 当前该节点确实是CPU和内存资源几乎被打满导致运行卡顿,建议尽快排查占用资源过高的进程或Pod,进行资源扩容或者异常进程查杀。
===== 测试 4 ===== [USER] order-service 返回 500,而且 Pod 也反复重启,帮我整体分析一下 [TOOL] get_pod_status args={'pod_name': 'order-service'} [TOOL] get_business_log args={'service_name': 'order-service', 'start_time': '2026-04-20 11:30:00', 'end_time': '2026-04-20 11:45:00'} [TOOL] get_system_metrics args={'node_ip': '10.10.1.23', 'start_time': '2026-04-20 11:30:00', 'end_time': '2026-04-20 11:45:00'} [FINAL] ### 故障整体分析结论 #### 1. Pod反复重启原因 order-service的Pod当前处于`CrashLoopBackOff`状态,累计重启5次,直接原因是**内存溢出被系统杀死(OOMKilled)**,同时Pod就绪探针检测失败( 连接被拒绝),进一步触发了Pod反复重启。 所在节点`10.10.1.23`的资源水位极高:CPU使用率91%、内存使用率93%,节点资源瓶颈是触发Pod OOM的重要诱因。 #### 2. 接口返回500原因 业务日志显示故障时间段内存在两类错误: - 代码层面存在空指针异常(`java.lang.NullPointerException`) - 订单插入操作触发数据库超时,重试失败后直接返回HTTP 500错误。 #### 临时恢复建议 1. 优先调整order-service的容器内存配额,同时调度到资源充足的节点 2. 修复代码空指针问题,优化数据库插入逻辑(增加超时时间/分库分表/异步插入) 3. 对`10.10.1.23`节点进行负载清退,降低节点资源使用率。

4组问题,llm选择了不同的、它自主决策的函数来解决对应的问题。在多函数场景里,模型并不是瞎选,而是很像运维工程师排查问题一样:

  • 先判断问题在哪一层
  • 再决定调哪个工具
  • 如果证据不够,就继续补证据
  • 最后基于事实做总结

原理解析

LLM 语义匹配

LLM 在多函数场景下的选择逻辑,本质上是拿用户问题的语义去匹配函数描述的语义,LLM会关注用户当前到底在问什么,每个函数的作用、输入参数、适 用场景分别是什么

然后模型会判断:

  • 哪个函数最像解决这个问题的工具
  • 需不需要调用函数
  • 调用时参数该怎么补齐
  • 如果一个函数不够,后面还要不要继续调别的函数

function call 的关键不只是有没有函数,更关键的是怎么描述函数,函数写得像说明书,模型就选得准,函数写得像谜语人,模型就容易整活

到这里,有为老哥可能还会继续追问:所谓的语义匹配,到底匹配的是啥?

模型在决策时,通常会综合下面几个维度:

  • 问题里的关键词和上下文:

    • “Pod 重启” 更容易命中get_pod_status
    • “接口 500” 更容易命中get_business_log
    • “节点很卡” 更容易命中get_system_metrics
  • 函数 description 的场景描述,如果你把“用于排查什么问题”写得清楚,模型就更容易做出准确判断

  • 参数是否容易从当前上下文中提取,比如用户明确说了10.10.1.23等操作系统类的资源,那模型就更容易触发监控工具

  • 系统提示词里的行为约束,如果你在 system prompt 里明确要求:先查事实,再下结论;必要时可以多次调用工具,模型一般会严谨许多

function call 的效果,不是只取决于模型本身,也取决于你的工具定义和提示词设计

让模型更聪明

如果你发现模型老是选错函数,先排查下面几个点

  • 1)description 写得太短,这是最常见的问题

    • 不要只写:

      • 获取日志
      • 获取监控
    • 而要写成:

      • 获取业务服务日志,用于排查接口报错、代码异常、数据库超时等问题
      • 获取节点操作系统监控数据,用于排查 CPU、内存、磁盘、网络资源瓶颈问题
  • 2)函数边界不清晰,如果两个函数看起来都像什么都能查,模型就容易纠结,所以工具设计最好做到:

    • 每个函数负责一类证据
    • 不同函数之间边界清楚
    • 尽量不要让多个函数 description 大面积重叠
  • 3)参数名太抽象,像这种参数:idnametarget,看着万能,实际上很坑,应该改为具体指某一类资源,实在有重名,那就疯狂叠下划线pod_nameservice_namenode_ip,参数名越具体,模型越不容易理解歪。

  • 4)提示词没告诉模型:可以连续调用多个工具,如果 system prompt 没写这句话,模型有时会保守,只调一个函数就停,所以建议明确告诉它:

    • 如果一个工具不足以定位问题,可以继续调用其他工具
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/8 5:46:05

《AI生成内容正在被大规模清洗——同质化营销内容将被剔除,深度思想白皮书成为GEO时代最稀缺的优质信源》

《AI生成内容正在被大规模清洗——同质化营销内容将被剔除,深度思想白皮书成为GEO时代最稀缺的优质信源》一、一个正在发生的悖论:AI创造了内容,也在消灭内容先看一个充满讽刺意味的现象。AI大模型的训练,依赖于海量的互联网文本数…

作者头像 李华
网站建设 2026/7/8 5:45:43

孕期肛周困扰别硬扛:从根源出发的温和修护思路

怀孕之后才明白,身体上那些难以启齿的小麻烦,往往比明显的妊娠反应更磨人。孕吐、水肿还能和家人朋友倾诉,唯独肛周的那点不适,多数时候只能自己默默消化——时隐时现的小肉球,久坐后的坠胀感,排便时偶现的…

作者头像 李华
网站建设 2026/7/8 5:43:09

2026年必看:科学解读儿童自闭症,家长这样应对更有效

近年来,儿童自闭症(或称孤独症谱系障碍,ASD)的发病率在全球范围内逐年上升。据世界卫生组织(WHO)2025年最新统计,全球每160名儿童中就有1名被诊断为自闭症,而在某些国家,…

作者头像 李华
网站建设 2026/7/8 5:42:55

DVWA_CSRF

Security level: Low查看代码,没有任何验证码或者Token防御(服务器只看请求里有无有效Session Cookie, 带了就执行) 你敢相信吗,在浏览器里点开了一个搞笑的图片,你在另一个网站的密码就被人偷偷改了!今天来…

作者头像 李华