项目5 无Function Call的结构化通用Agent

知识点:结构化Agent、Prompt工程、不支持Function Call模型适配、自定义工具

功能

  • 自己写 Prompt 约束格式:Action:xxx Action Input:xxx
  • 不用模型原生Function Call,纯提示词实现工具调用
  • 接入:计算器、时间查询、本地文档检索工具

落地:结构化Agent不靠模型,靠Prompt实现Agent。

项目拆解

核心定位:纯 Prompt 工程实现 Agent 工具调用,零模型原生函数调用能力。

  • 阶段一:项目基础协议定义(前置基建,无代码)
    • 步骤 1:定义 Agent 结构化交互协议(项目核心标准)
    • 实践:统一模型输出字段、工具调用格式、问答分支规则
    • 配套理论:无 Function Call Agent 的结构化解析原理
  • 阶段二:Prompt 工程实现(Agent 大脑规则层)
    • 步骤 2:手写完整版结构化约束 System Prompt
    • 实践:编写角色规则、工具清单、判断逻辑、正负样例、输出规约
    • 配套理论:Prompt 工程如何替代原生 Function Call 能力
  • 阶段三:代码基建(核心底座,Python)
    • 步骤 3:搭建项目基础代码结构,初始化全局配置
    • 实践:创建项目文件结构、导入依赖、定义全局常量
    • 配套理论:模块化 Agent 工程的分层设计思想
  • 阶段四:核心能力开发(工具层)
    • 步骤 4:开发 3 个自定义原生工具
    • 实践:逐一对接实现 calculator、time_query、doc_search 工具函数
    • 配套理论:自定义工具的入参、出参标准化设计规范
  • 阶段五:Agent 核心逻辑(解析 + 调度层)
    • 步骤 5:开发文本格式解析器
    • 实践:代码解析模型输出,提取 Thought / Action / Action Input
    • 配套理论:结构化输出的正则匹配解析原理
    • 步骤 6:开发工具路由调度器
    • 实践:根据解析出的 Action 名称,自动分发执行对应工具
    • 配套理论:Agent 工具调度的路由机制
  • 阶段六:Agent 闭环能力(多轮迭代)
    • 步骤 7:实现单轮 Agent 调用链路
    • 实践:用户提问→模型推理→工具调用→结果返回
    • 步骤 8:实现多轮 Agent 思考闭环
    • 实践:工具结果回传模型,支持多次连续调用工具,直至问题解决
    • 配套理论:Agent ReAct 思考迭代框架核心逻辑
  • 阶段七:测试落地
    • 步骤 9:全场景功能测试、异常兼容优化
    • 实践:测试计算、时间、文档检索、无需工具直接回答、格式异常场景

本项目属于独立可复用的工具调度模块。
整体层级关系:

  • 上层:大模型负责思考决策、输出规范格式文本
  • 中层:咱们写的解析 + 调度逻辑,拆解指令、匹配工具
  • 底层:计算器、文档查询这类具体功能函数

本项目承接模型指令,落地实际操作,是完整智能体里的执行环节。

1 项目基础协议

放弃模型原生函数调用接口,依靠约定文本格式做调用标识,程序端解析文本完成工具触发,实现等效 Agent 工具调用能力。

1.1 应答分支规则

  1. 无需调用工具:直接输出最终答案,不出现指定格式字段
  2. 需要调用工具:严格遵循固定三段式格式输出,字段顺序不可调换

1.2 标准输出格式

Thought: 思考判断内容
Action: 工具标识名
Action Input: 工具传入参数

1.3 工具名册与使用规范

工具标识 功能用途 入参要求
calculator 数学运算计算 填写完整数学表达式
time_query 获取当前系统时间 无参数,填空即可
doc_search 本地文档关键词检索 填写检索关键词

1.4 格式约束细则

  1. 字段名称固定大小写、文字不可修改删减
  2. 思考内容简洁说明调用工具的原因
  3. 参数严格匹配对应工具格式,不额外附加多余描述

2 结构化约束 System Prompt

通过提示词强制约束模型输出范式、决策逻辑与工具使用规则,替代原生 Function Call 接口,让模型按约定格式产出可解析调用指令。

2.1 完整 Prompt 文案

你是结构化智能Agent,无法使用模型原生函数调用能力,仅按既定格式完成交互作答。

可用工具列表:
1. calculator:执行数学运算,入参填写标准数学表达式
2. time_query:查询当前系统时间,无需传入参数
3. doc_search:检索本地文档内容,入参填写检索关键词

作答规则:
1. 简单问题无需调用工具,直接给出最终答案即可
2. 涉及计算、查时间、查文档的问题,必须严格按下方固定格式输出,不得增减内容、更改字段顺序
标准调用格式:
Thought: 简述调用工具的原因
Action: 填写对应工具标识
Action Input: 填写对应所需参数

参考示例:
示例1 调用计算
Thought: 需要计算数值结果,调用计算器工具
Action: calculator
Action Input: 12+36*2

示例2 查询时间
Thought: 需要获取当前时间,调用时间查询工具
Action: time_query
Action Input: 

示例3 文档检索
Thought: 需要查找相关文档信息,调用文档检索工具
Action: doc_search
Action Input: 项目开发规范

2.2 实操要点

  • 文案明确区分直接作答、工具调用两种场景
  • 绑定前期约定的工具名与入参规范,保证前后统一
  • 附上实操样例,降低模型格式出错概率

3 项目基础代码结构

采用分层模块化架构,拆分规则、工具、解析、调度模块,降低耦合,便于后续迭代扩展。

3.1 规划目录文件结构

agent_project/
├── main.py          # 程序入口、对话主逻辑
├── tools.py         # 计算器/时间/文档检索工具函数
└── parser.py        # 模型输出格式解析逻辑

3.2 初始化各基础文件

本次基础功能无需额外第三方库,仅使用 Python 内置模块即可开发,无需额外安装包。

简单问题:文字释义、词语理解这类属于模型自身知识库内容,不属于原生函数调用,无需动用工具,直接作答即可。

内置知识库 vs 联网搜索 边界总结
  • 内置知识库(不触发 invoke,低消耗)

    • 模型训练时录入的存量知识,本地直接推理作答适用:词语释义、常识典故、基础理论、历史固定内容、文本分析、常规问答
  • 联网搜索 / 外部工具(触发 invoke,额外耗资源)

    • 向外请求外部数据、实时内容、本地文件、运算服务适用:实时时间、最新资讯、动态数据、数学计算、文档查询、当下变动信息
  • 核心分界

    • 信息固定不变→用内置知识
    • 信息实时可变 / 外部独有→走调用检索

4 自定义工具函数

工具单独封装成独立单元,统一输入输出形式,和调度逻辑拆分,改动互不影响。

4.1 明确文件与整体思路

  • 操作文件:tools.py
  • 整体规划:分别编写三个工具方法,最后用字典建立工具名和方法的对应关系

4.2 逐个梳理工具开发要点

  • 计算器工具
    • 接收参数:数学表达式字符串
    • 核心逻辑:解析表达式算出结果
    • 容错处理:捕获计算出错的情况,返回异常提示
  • 时间查询工具
    • 接收参数:无实际有效参数
    • 核心逻辑:读取系统当前时间,规范日期时间展示格式
    • 返回内容:格式化后的时间文本
  • 本地文档检索工具
    • 接收参数:检索关键词
    • 核心逻辑:内置模拟文档数据,根据关键词匹配内容
    • 判定分支:匹配到则返回对应内容,无匹配则给出未查询到提示

4.3 构建工具映射关系

定义映射容器,把之前约定的 action 标识名,一一对应绑定到各自工具方法,后续调度时直接通过名称就能调用对应功能。

# 工具映射表
TOOL_MAP = {
    "calculator": compute,
    "time_query": get_time,
    "doc_search": search_document
}

5 文本格式解析器

拆分模型返回文本,提取 Thought、Action、Input 三段内容,为后续工具调度提供有效数据。

5.1 核心思路

  1. 按固定字段标识切割文本
  2. 分别取出思考内容、工具名、入参
  3. 校验字段完整性,异常则判定无需调用工具

5.2 关键操作要点

  1. 匹配Thought:、Action:、Action Input:前缀拆分内容
  2. 去除首尾多余空格换行,规整数据
  3. 解析成功返回三段数据,解析失败返回空标识

6 工具路由调度器

承接解析后的指令,匹配对应工具函数,完成调用并返回执行结果。

执行步骤

  1. 调用解析器,拆分出思考内容、工具标识、传入参数
  2. 判断工具标识是否为空,无调用指令则仅返回思考信息
  3. 检索工具映射表,校验当前工具是否合法可用
  4. 匹配到对应工具函数,传入参数执行运算查询
  5. 汇总思考过程与工具执行结果,统一对外输出

标准格式测试输入文本

Thought: 查询项目相关说明
Action: doc_search
Action Input: 项目介绍

核心代码

from parser import parser
from tools import TOOL_MAP

if __name__ == '__main__':

    test_input = """Thought: 查询项目相关说明
    Action: doc_search
    Action Input: 项目介绍"""

    # 调用解析器,拆分出思考内容、工具标识、传入参数
    input_parser = parser(test_input)
    print(input_parser)

    res = ''
    # 判断工具标识是否为空,无调用指令则仅返回思考信息
    if input_parser['action'] == '':
        res += f"thought: {input_parser['thought']}"
    else:
        # 检索工具映射表,校验当前工具是否合法可用
        if input_parser['action'] not in TOOL_MAP:
            print('当前工具不可用')
        else:
            # 匹配到对应工具函数,传入参数执行运算查询
            tool = TOOL_MAP[input_parser['action']]
            # 汇总思考过程与工具执行结果,统一对外输出
            output = tool(input_parser['action_input'])
            res += f"thought: {input_parser['thought']}\n output: {output}"

    print(res)

运行结果

{'thought': '查询项目相关说明', 'action': 'doc_search', 'action_input': '项目介绍'}
thought: 查询项目相关说明
 output: 这是一个无FunctionCall的结构化Agent

7 单轮 Agent 调用链路

完整流转:用户提问 → 模型推理生成结构化指令 → 解析拆解指令 → 路由匹配工具执行 → 整合结果反馈。

执行步骤

  1. 定义接收用户问题的入口
  2. 编写模拟模型逻辑,依据问题内容,产出规范格式的思考、动作、参数文本
  3. 调用原有解析模块,拆分出思考内容、工具标识、入参
  4. 走工具调度逻辑,判断是否调用工具并执行
  5. 拼接信息,输出最终答复

核心代码

(先用 mock 模拟输出,现阶段只模拟推理结果格式,暂不接入真实大模型接口)

from parser import parser
from tools import TOOL_MAP


def invoke(input):
    print(input)
    # 编写模拟模型逻辑,依据问题内容,产出规范格式的思考、动作、参数文本
    if input.find("计算") != -1:
        test_input = """Thought: 1+1结果是什么
        Action: calculator
        Action Input: 1+1"""
    elif input.find("查询") != -1:
        test_input = """Thought: 查询项目相关说明
        Action: doc_search
        Action Input: 项目介绍"""
    elif input.find("时间") != -1:
        test_input = """Thought: 现在是什么时间
        Action: time_query
        Action Input: """

    return test_input


if __name__ == '__main__':

    # 定义接收用户问题的入口
    llm_output = invoke("现在是什么时间")

    # 调用解析器,拆分出思考内容、工具标识、传入参数
    input_parser = parser(llm_output)

    res = ''
    # 判断工具标识是否为空,无调用指令则仅返回思考信息
    if input_parser['action'] == '':
        res += f"thought: {input_parser['thought']}"
    else:
        # 检索工具映射表,校验当前工具是否合法可用
        if input_parser['action'] not in TOOL_MAP:
            print('当前工具不可用')
        else:
            # 匹配到对应工具函数,传入参数执行运算查询
            tool = TOOL_MAP[input_parser['action']]
            # 汇总思考过程与工具执行结果,统一对外输出
            output = tool(input_parser['action_input'])
            res += f"thought: {input_parser['thought']}\n output: {output}"

    print(res)

8 ReAct 多轮思考闭环

8.1 ReAct

ReAct = Reason(推理思考)+ Act(行动调用工具)是 Agent 经典迭代框架,边思考边行动,循环往复解决问题。

8.2 核心运作流程

  1. 思考(Reason)
    • 模型分析问题 + 已有信息,判断是否需要调用工具、用什么工具
    • → 对应你代码里的 invoke 函数,生成结构化指令
  2. 行动(Act)
    • 解析指令 → 匹配工具 → 执行函数 → 拿到结果
    • → 对应你的 parser + 工具调度逻辑
  3. 复盘(反馈)
    • 把工具执行结果回传给模型,让模型重新判断问题是否解决
  4. 循环(迭代)
    • 未解决 → 继续思考 + 调用工具
    • 已解决 → 停止循环,给出最终答案

核心规则

工具结果回传给模型,循环判断是否继续调用工具,直到问题办结。

8.3 实操分步思路

  1. 定义对话上下文变量
    • 用来存放用户问题、每一轮的思考、工具执行结果,持续传给模型
  2. 用循环包裹单轮执行逻辑
    • 让 Agent 可以自动重复:思考→行动→复盘,而不是只跑一次
  3. 每轮结束拼接上下文
    • 把本轮的思考 + 工具结果追加到上下文里,给下一轮模型使用
  4. 模型根据上下文决策
    • 读取历史信息,继续生成新指令,或判断任务完成
  5. 设置终止条件
    • 当模型输出 空工具(action 为空) 时,结束循环,输出最终回答

流程图
在这里插入图片描述

8.4 核心代码

from parser import parser
from tools import TOOL_MAP


def invoke(input):
    print(input)
    # 编写模拟模型逻辑,依据问题内容,产出规范格式的思考、动作、参数文本
    if input.find("计算") != -1:
        test_input = """Thought: 1+1结果是什么
        Action: calculator
        Action Input: 1+1"""
    elif input.find("查询") != -1:
        test_input = """Thought: 查询项目相关说明
        Action: doc_search
        Action Input: 项目介绍"""
    elif input.find("时间") != -1:
        test_input = """Thought: 现在是什么时间
        Action: time_query
        Action Input: """
    else:
        test_input =  """Thought: 问题已解决
        Action:
        Action Input:"""

    return test_input


if __name__ == '__main__':
    # 对话上下文
    context = {
        "user_query":"",
        "history":[]
    }
    # 思考:义接收用户问题的入口
    llm_output = invoke("现在是什么时间")
    context['user_query'] = '现在是什么时间'

    while True:
        # 调用解析器,拆分出思考内容、工具标识、传入参数
        input_parser = parser(llm_output)

        # 当 action 为空时退出循环
        if input_parser['action'] == '':
            break

        res = ''
        # 检索工具映射表,校验当前工具是否合法可用
        if input_parser['action'] not in TOOL_MAP:
            print('当前工具不可用')
            break  # 跳出循环,结束Agent
        else:
            # 行动:匹配到对应工具函数,传入参数执行运算查询
            tool = TOOL_MAP[input_parser['action']]
            # 汇总思考过程与工具执行结果,统一对外输出
            output = tool(input_parser['action_input'])
            res += f"thought: {input_parser['thought']}\n output: {output}"


        context['history'].append(res)
        print('---')
        print(context)

        # 复盘
        # 拼接上下文:用户问题 + 历史结果
        context_str = f"用户问题:{context['user_query']},历史结果:{str(context['history'])}"
        llm_output =  invoke(context_str)

运行结果
(局部)
在这里插入图片描述

9 全场景测试 + 异常兼容优化

9.1 测试场景清单

  • 常规计算:帮我计算 1+1
  • 资料查询:帮我查询项目介绍
  • 时间查询:现在是什么时间
  • 无需工具闲聊:你好呀
  • 无效工具请求:帮我查询天气

9.2 代码优化要点

  • 问题抽取成变量,方便切换测试
  • 增加异常捕获,规避执行报错
  • 输出规整最终总结答复

9.3 验证标准

  • 合法请求正常调用工具、循环迭代后自动收尾
  • 无效工具、程序报错均可稳妥终止,不会死循环
  • 各类意图场景都能按逻辑分支正确响应

项目6 Function Call 智能工具助手

项目核心功能

  • 实现 3 个工具
    • 查天气工具
    • 查当前时间工具
    • 简单知识库检索工具
  • 自动调用工具
    • 用户问问题 → 模型自己判断要不要调用工具、调用哪个、传什么参数
  • 自定义解析器
    • 自己写解析函数,控制 Agent 什么时候停止思考、停止调用工具
  • 最后必须输出结构化 JSON
    • 不管中间调用多少次工具,最终回答强制输出标准 JSON,不能随便说话

Function Call

核心本质就是大模型主动调用外部工具。

核心逻辑

  • 模型本身知识有边界、没法实时数据、没法运算检索,就靠 Function Call 向外求助
  • 模型不再只输出文本,而是生成规范的工具调用指令,包含工具名、入参
  • 程序解析指令,执行对应工具拿到结果,再回传给模型
    模型结合工具返回内容,继续推理或是给出最终答复
  • 项目里的查天气、时间、知识库检索,全都是靠这套机制实现调度

开发步骤

  • 阶段 1:环境准备 + 基础配置
    • 安装必要依赖(langchain、langchain-openai 等)
    • 配置大模型(GPT / 通义千问 / 文心一言 都行)
  • 阶段 2:编写 3 个自定义工具
    • 写一个 “查天气” 工具(简单模拟返回)
    • 写一个 “查当前时间” 工具
    • 写一个 “知识库检索” 工具(模拟本地知识)
  • 阶段 3:学习并使用 bind_tools
    • 把工具绑定给大模型
    • 理解 Function Call 原理:模型什么时候决定调用工具
  • 阶段 4:搭建 AgentExecutor 执行器
    • 搭建 Agent 核心运行流程
    • 理解:用户提问 → 思考 → 调用工具 → 获取结果 → 再思考
  • 阶段 5:编写自定义解析器
    • 自己写 parse 函数
    • 控制:什么时候停止调用工具、进入最终回答
  • 阶段 6:强制输出 JSON 格式(Response 伪装)
    • 让模型最后必须调用 Response 工具
    • 输出固定结构 JSON,而不是自然语言
  • 阶段 7:完整测试
    • 测试各种问题:
      • 只需要查时间
      • 需要查天气
      • 需要查知识库
      • 混合问题
    • 看 Agent 能不能自动调度工具 + 最后输出 JSON

1 环境准备 & 基础配置

  1. 先引入环境变量、模型、工具相关基础模块
  2. 读取密钥接口地址,实例化大模型,温度设 0 保证推理稳定

2 自定义工具

LangChain 自定义工具的固定规则

  • 用 @tool 装饰器修饰一个函数
  • 函数名 = 工具名
  • 函数文档字符串(注释)= 给大模型看的工具说明(非常重要)
  • 函数参数 = 工具需要的入参
  • 函数返回值 = 工具执行结果

核心代码

from datetime import datetime
from langchain_core.tools import tool

@tool
def get_current_time():
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

@tool
def get_weather(city):
    return f"{city}市 晴天,25℃"

documents = {
    "项目介绍": "这是一个无FunctionCall的结构化Agent",
    "工具列表": "计算器、时间查询、文档检索",
    "使用规则": "必须按指定格式输出调用指令"
}

@tool
def knowledge_base_search(query):
    if query in documents:
        return documents[query]
    else:
        return "未找到相关文档"

3 bind_tools

什么是 bind_tools

  • 将自定义的工具 “告诉” 大模型让模型知道:这里有几个工具可以用

需要做的事情

  • 把你定义的 3 个工具放进一个列表
  • 用模型的 .bind_tools() 方法,把这个列表传进去
  • 得到一个绑定了工具的新模型对象

规则

  • 工具列表 = [工具 1, 工具 2, 工具 3]
  • 直接调用 chat_model.bind_tools(工具列表)
  • 赋值给一个新变量,比如 llm_with_tools

核心代码

tools = [get_current_time, get_weather, knowledge_base_search]
chat_model_with_tools = chat_model.bind_tools(tools)

4 AgentExecutor 执行器

  1. 必须先创建一个 Agent:create_tool_calling_agent

    • 绑定好工具的模型(chat_model_with_tools)
    • 工具列表(tools)
    • 一个 prompt 提示词模板(系统提示 + 用户输入)
  2. 创建 AgentExecutor —— Agent 的 “运行器”

    • 第一步创建的 agent
    • 工具列表tools
    • 可以加:verbose=True 看思考过程(强烈建议开)
  3. executor.invoke ()

    • 传入用户问题,即可自动调用工具

核心代码

from langchain_classic.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

chat_prompt_template = ChatPromptTemplate.from_messages([
    ("system", "你是工具助手,必须调用工具回答"),
    ("user", "{user_input}"),
    MessagesPlaceholder("agent_scratchpad")
])

agent = create_tool_calling_agent(chat_model_with_tools, tools, chat_prompt_template)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
res = executor.invoke({"user_input": "南京天气如何"})

print(res)

运行结果
在这里插入图片描述

5 自定义解析器

脱离默认 AgentExecutor 黑盒,手动解析工具调用、自主控制流程启停。

执行流程

  • 步骤 1:梳理核心依赖与前置准备
    • 现有资源:已绑定工具的模型、工具列表、提示词模板,直接复用。
    • 核心对象:模型返回结果里的 tool_calls 字段,用来判断是否要调用工具。
  • 步骤 2:实现工具匹配执行逻辑
    • 定义一个独立函数,入参设计为工具名、工具参数。
    • 遍历全局工具列表,根据工具名匹配对应工具。
    • 匹配成功就执行工具并返回结果;匹配失败返回提示文本。
  • 步骤 3:构造首轮对话上下文
    • 用提示词模板组装请求内容,传入用户问题。
    • agent_scratchpad 首轮传空列表,用来存放后续交互记录。
  • 步骤 4:自定义解析 & 流程控制(核心)
    • 调用模型得到响应结果。
    • 判断响应是否存在 tool_calls:
      • 存在:提取工具名、入参,调用上面的工具执行函数拿到结果,手动终止本轮流程。
      • 不存在:直接取模型文本内容作为最终回答,流程结束。

自定义解析器的自定义体现在:自己判断工具、自己拆字段、自己找工具、自己控制终止

核心代码

def execute_action(res):
    for tool in tools:
        if tool.name == res.tool_calls[0]['name']:
            res = tool.invoke(res.tool_calls[0]['args'])
            return res
    return '未找到对应的工具,请重试'

chain  = chat_prompt_template | chat_model_with_tools
res = chain.invoke({"user_input": "我在胡言乱语啊啊啊啊啊", "agent_scratchpad":[]})
print(res)
if res.tool_calls:
    res1 = execute_action(res)
    print(res1)
else:
    # 直接提取文本内容,作为最终回复,流程结束。
    print(res.content)

运行结果

正常情况

南京市 晴天,25

异常情况

content='哈哈,听起来你是在释放压力或者玩文字游戏呢!如果你有任何问题、需要帮助,或者只是想聊点什么,我都很乐意陪你~ 😄  \n要不要试试问点有趣的、实用的,或者来点脑洞大开的问题?' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'request_id': 'c75a6e27-08dc-9e7b-8461-c769b09fd5b6', 'token_usage': {'input_tokens': 258, 'output_tokens': 53, 'total_tokens': 311, 'prompt_tokens_details': {'cached_tokens': 0}}} id='lc_run--019e64d3-f637-7820-b912-8f95b0aeb04f' tool_calls=[] invalid_tool_calls=[]
哈哈,听起来你是在释放压力或者玩文字游戏呢!如果你有任何问题、需要帮助,或者只是想聊点什么,我都很乐意陪你~ 😄  
要不要试试问点有趣的、实用的,或者来点脑洞大开的问题?

6 强制结构化输出

本节主要内容为:约束模型输出格式 + 自定义解析 JSON 内容。

提示词修改为

chat_prompt_template = ChatPromptTemplate.from_messages([
    ("system", "你是工具助手,必须调用工具回答。只输出 JSON,不要说任何多余的话!。如果是纯文本信息,那么回复的内容有type(值为text)和content(值为回答的内容)\n"
               "属性,如果是工具调用相关,那么是type(值为tool)和tool_name(要调用的工具名称)和 args(工具函数的入参)"),
    ("user", "{user_input}"),
    MessagesPlaceholder("agent_scratchpad")
])

chat_model不再需要绑定tools。在上述提示词模板下,输出如下。

content='{"type": "tool", "tool_name": "get_weather", "args": {"city": "南京"}}' additional_kwargs={} response_metadata={'finish_reason': 'stop', 'request_id': '7838f54d-ec09-9226-922e-961fc962beb1', 'token_usage': {'input_tokens': 96, 'output_tokens': 23, 'total_tokens': 119, 'prompt_tokens_details': {'cached_tokens': 0}}} id='lc_run--019e64e6-1126-7210-8933-0a5d91a01577' tool_calls=[] invalid_tool_calls=[]

对输出进行解析,修改解析器

def execute_action(res):
    for tool in tools:
        if tool.name == res['tool_name']:
            res = tool.invoke(res['args'])
            return res
    return '未找到对应的工具,请重试'


chain  = chat_prompt_template | chat_model
res = chain.invoke({"user_input": "南京天气如何", "agent_scratchpad":[]})
print(res)
content = json.loads(res.content)
if content['type'] == 'tool':
    res1 = execute_action(content)
    print(res1)
else:
    # 直接提取文本内容,作为最终回复,流程结束。
    print(content)

运行结果

南京市 晴天,25

本节目的

  • 在不使用模型原生工具调用(不 bind_tools)的前提下
  • 通过提示词强制模型输出【自定义结构化 JSON】
  • 手动解析格式、手动匹配工具、手动执行调用,最终拿到结果
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐