<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Tool Use on niuzj</title>
    <link>https://niuzj.org/tags/tool-use/</link>
    <description>Recent content in Tool Use on niuzj</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 08 Apr 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://niuzj.org/tags/tool-use/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>让 LLM 同时返回文字和结构化数据：用假 Tool Call 做 Side Channel</title>
      <link>https://niuzj.org/posts/llm-structured-output-via-fake-tool/</link>
      <pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://niuzj.org/posts/llm-structured-output-via-fake-tool/</guid>
      <description>&lt;p&gt;最近在做 AI Agent 的时候碰到一个很实际的问题：LLM 回复用户的时候，除了正常的文字，还需要同时返回一份结构化的 JSON 给前端渲染 UI 组件。&lt;/p&gt;&#xA;&lt;p&gt;比如用户问&amp;quot;帮我分析一下这几个模板，推荐最适合的&amp;quot;，Agent 需要：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;用文字解释推荐理由（给用户看）&lt;/li&gt;&#xA;&lt;li&gt;同时返回一个结构化的推荐列表（给前端渲染可点选的卡片）&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;这两个东西必须在同一轮对话里出来。&lt;/p&gt;&#xA;&lt;h2 id=&#34;直觉方案让-llm-在文本里嵌-json&#34;&gt;直觉方案：让 LLM 在文本里嵌 JSON&lt;/h2&gt;&#xA;&lt;p&gt;最容易想到的办法是在 system prompt 里约束 LLM，让它在回复末尾加一段 JSON：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;你的回复格式：&#xA;先用自然语言回答用户，然后在末尾用 ```json 代码块输出推荐数据...&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个方案能跑，但很脆弱：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;你得写正则或者找分隔符从文本里提取 JSON&lt;/li&gt;&#xA;&lt;li&gt;LLM 有时候会把 JSON 格式搞乱，多个反引号、漏个逗号&lt;/li&gt;&#xA;&lt;li&gt;流式输出的时候更麻烦，你不知道 JSON 什么时候开始什么时候结束&lt;/li&gt;&#xA;&lt;li&gt;prompt 越复杂，LLM 越容易忘记遵守格式&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;正经方案三家-api-的-structured-output&#34;&gt;正经方案：三家 API 的 Structured Output&lt;/h2&gt;&#xA;&lt;p&gt;先看看三大 LLM 厂商怎么解决&amp;quot;让模型输出结构化数据&amp;quot;这个问题。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;OpenAI&lt;/strong&gt; 有原生的 &lt;code&gt;response_format&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl https://api.openai.com/v1/chat/completions &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -d &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;model&amp;#34;: &amp;#34;gpt-4o&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;response_format&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;#34;type&amp;#34;: &amp;#34;json_schema&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;#34;json_schema&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;name&amp;#34;: &amp;#34;recommendations&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;strict&amp;#34;: true,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#34;schema&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          &amp;#34;type&amp;#34;: &amp;#34;object&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          &amp;#34;properties&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            &amp;#34;items&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              &amp;#34;type&amp;#34;: &amp;#34;array&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              &amp;#34;items&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                &amp;#34;type&amp;#34;: &amp;#34;object&amp;#34;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                &amp;#34;properties&amp;#34;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                  &amp;#34;title&amp;#34;: {&amp;#34;type&amp;#34;: &amp;#34;string&amp;#34;},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                  &amp;#34;reason&amp;#34;: {&amp;#34;type&amp;#34;: &amp;#34;string&amp;#34;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                &amp;#34;required&amp;#34;: [&amp;#34;title&amp;#34;, &amp;#34;reason&amp;#34;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;          &amp;#34;required&amp;#34;: [&amp;#34;items&amp;#34;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;messages&amp;#34;: [{&amp;#34;role&amp;#34;: &amp;#34;user&amp;#34;, &amp;#34;content&amp;#34;: &amp;#34;推荐3个适合电商的工作流模板&amp;#34;}]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  }&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;strict: true&lt;/code&gt; 保证输出 100% 符合 schema。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
