掘金 人工智能 08月07日
Claude code课程:工具的使用-3.利用工具强制生成 JSON
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了如何利用 Claude 的工具功能来强制其生成结构化的 JSON 响应。核心思想是定义一个描述所需 JSON 结构的工具,然后指示 Claude 使用该工具。Claude 会将此视为调用工具,并按照工具定义的 JSON Schema 返回数据。这种方法能够确保输出的标准化和精确性,适用于实体提取、情感分析、数据汇总等多种场景。文章通过情感分析和实体提取的示例,详细展示了如何实现这一技巧,并强调了使用 `tool_choice` 参数可以更有效地强制 Claude 使用特定工具。最后,文章提供了一个使用 `wikipedia` 包和 Claude 进行维基百科文章分类的复杂示例。

💡 **强制 JSON 输出的核心在于定义工具:** 通过为 Claude 提供一个明确定义了所需 JSON 结构(包括属性、类型和必填项)的工具,可以引导其生成结构化响应。Claude 在尝试调用该工具时,会按照工具的 `input_schema` 返回数据。

🚀 **“欺骗” Claude 使用工具以获取结构化数据:** 即使不实际执行底层工具函数,也可以利用 Claude 期望使用工具时的格式化输出。关键在于指示 Claude 使用一个工具,并提取其响应中符合预期的结构化数据。

🎯 **`tool_choice` 参数增强控制力:** 使用 `tool_choice` 参数并指定特定的工具名称,可以更直接地强制 Claude 使用该工具,确保其输出符合预设的 JSON 格式,比仅通过提示词指令更可靠。

📊 **多领域应用示例:** 该技巧不仅限于简单的文本分析,还可以应用于更复杂的任务,如从维基百科文章中提取主题、摘要、关键词和分类信息,生成结构化的分类报告。

📝 **实践练习与扩展:** 文章通过一个翻译练习,展示了如何将此技巧应用于多语言翻译场景,生成包含不同语言翻译的结构化 JSON,进一步验证了该方法的可行性和灵活性。

文章翻译自官网github.com/anthropics/…

利用工具强制生成 JSON

学习目标

利用工具的一种更有趣的方式是强制 Claude 输出 JSON 等结构化内容。在很多场景下,我们可能希望从 Claude 那里获得标准化的 JSON 响应,例如:提取实体、汇总数据、分析情感等。

一种方法是直接要求 Claude 返回 JSON,但这可能需要额外的工作来从 Claude 返回的长字符串中提取 JSON,或者确保 JSON 符合我们想要的确切格式。

好消息是,每当 Claude 想要使用工具时,它已经会按照我们定义工具时指定的完美结构化格式进行响应。

在上一节课中,我们给了 Claude 一个计算器工具。当它想要使用这个工具时,会返回类似这样的内容:

json

{    'operand1': 1984135,     'operand2': 9343116,     'operation': 'multiply'}

这看起来和 JSON 非常相似!

如果我们希望 Claude 生成结构化的 JSON,我们可以利用这一点。我们要做的就是定义一个描述特定 JSON 结构的工具,然后告诉 Claude 这个工具。就这样。Claude 会做出响应,它认为自己在 “调用工具”,但实际上我们只关心它给出的结构化响应。

概念概述

这与我们在上一节课中所做的有何不同?以下是上一节课的工作流程图:

在上一节课中,我们给了 Claude 使用工具的权限,当 Claude 想要调用它时,我们实际上会调用底层的工具函数。

在本节课中,我们将通过告诉 Claude 某个特定工具来 “欺骗” 它,但我们不需要实际调用底层的工具函数。我们使用该工具来强制生成特定结构的响应,如下图所示:

情感分析

让我们从一个简单的例子开始。假设我们希望 Claude 分析某些文本中的情感,并返回具有以下格式的 JSON 对象:

json

{  "negative_score": 0.6,  "neutral_score": 0.3,  "positive_score": 0.1}

我们只需要定义一个使用 JSON Schema 来捕获这种结构的工具。以下是一个可能的实现:

python

运行

tools = [    {        "name": "print_sentiment_scores",        "description": "Prints the sentiment scores of a given text.",        "input_schema": {            "type": "object",            "properties": {                "positive_score": {"type": "number", "description": "The positive sentiment score, ranging from 0.0 to 1.0."},                "negative_score": {"type": "number", "description": "The negative sentiment score, ranging from 0.0 to 1.0."},                "neutral_score": {"type": "number", "description": "The neutral sentiment score, ranging from 0.0 to 1.0."}            },            "required": ["positive_score", "negative_score", "neutral_score"]        }    }]

现在我们可以告诉 Claude 这个工具,并明确告诉 Claude 使用它,以确保它确实会使用。我们应该会得到一个响应,表明 Claude 想要使用一个工具。工具使用响应应该包含我们想要的确切格式的所有数据。

python

运行

from anthropic import Anthropicfrom dotenv import load_dotenvimport jsonload_dotenv()client = Anthropic()tweet = "I'm a HUGE hater of pickles.  I actually despise pickles.  They are garbage."query = f"""<text>{tweet}</text>Only use the print_sentiment_scores tool."""response = client.messages.create(    model="claude-3-sonnet-20240229",    max_tokens=4096,    tools=tools,    messages=[{"role": "user", "content": query}])response

plaintext

ToolsBetaMessage(id='msg_01BhF4TkK8vDM6z5m4FNGRnB', content=[TextBlock(text='Here is the sentiment analysis for the given text:', type='text'), ToolUseBlock(id='toolu_01Mt1an3KHEz5RduZRUUuTWz', input={'positive_score': 0.0, 'negative_score': 0.791, 'neutral_score': 0.209}, name='print_sentiment_scores', type='tool_use')], model='claude-3-sonnet-20240229', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=374, output_tokens=112))

让我们看看我们从 Claude 那里得到的响应。我们将重要部分加粗:

plaintext

ToolsBetaMessage(id='msg_01BhF4TkK8vDM6z5m4FNGRnB', content=[TextBlock(text='Here is the sentiment analysis for the given text:', type='text'), ToolUseBlock(id='toolu_01Mt1an3KHEz5RduZRUUuTWz', input={'positive_score': 0.0, 'negative_score': 0.791, 'neutral_score': 0.209}, name='print_sentiment_scores', type='tool_use')], model='claude-3-sonnet-20240229', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=374, output_tokens=112))

Claude “认为” 它在调用一个将使用这种情感分析数据的工具,但实际上我们只是要提取这些数据并将其转换为 JSON:

python

运行

import jsonjson_sentiment = Nonefor content in response.content:    if content.type == "tool_use" and content.name == "print_sentiment_scores":        json_sentiment = content.input        breakif json_sentiment:    print("Sentiment Analysis (JSON):")    print(json.dumps(json_sentiment, indent=2))else:    print("No sentiment analysis found in the response.")

plaintext

Sentiment Analysis (JSON):{  "positive_score": 0.0,  "negative_score": 0.791,  "neutral_score": 0.209}

它奏效了!现在让我们把它变成一个可重用的函数,该函数接收一条推文或一篇文章,然后以 JSON 格式打印或返回情感分析结果。

python

运行

def analyze_sentiment(content):    query = f"""    <text>    {content}    </text>    Only use the print_sentiment_scores tool.    """    response = client.messages.create(        model="claude-3-sonnet-20240229",        max_tokens=4096,        tools=tools,        messages=[{"role": "user", "content": query}]    )    json_sentiment = None    for content in response.content:        if content.type == "tool_use" and content.name == "print_sentiment_scores":            json_sentiment = content.input            break    if json_sentiment:        print("Sentiment Analysis (JSON):")        print(json.dumps(json_sentiment, indent=2))    else:        print("No sentiment analysis found in the response.")

python

运行

analyze_sentiment("OMG I absolutely love taking bubble baths soooo much!!!!")

plaintext

Sentiment Analysis (JSON):{  "positive_score": 0.8,  "negative_score": 0.0,  "neutral_score": 0.2}

python

运行

analyze_sentiment("Honestly I have no opinion on taking baths")

plaintext

Sentiment Analysis (JSON):{  "positive_score": 0.056,  "negative_score": 0.065,  "neutral_score": 0.879}

使用 tool_choice 强制工具使用

目前,我们通过提示来 “强制” Claude 使用我们的 print_sentiment_scores 工具。在我们的提示中,我们写道 “Only use the print_sentiment_scores tool.(只使用 print_sentiment_scores 工具。)”,这通常是有效的,但有一种更好的方法!我们实际上可以使用 tool_choice 参数强制 Claude 使用特定的工具:

python

运行

tool_choice={"type": "tool", "name": "print_sentiment_scores"}

上面的代码告诉 Claude,它必须通过调用 print_sentiment_scores 工具来响应。让我们更新我们的函数以使用它:

python

运行

def analyze_sentiment(content):    query = f"""    <text>    {content}    </text>    Only use the print_sentiment_scores tool.    """    response = client.messages.create(        model="claude-3-sonnet-20240229",        max_tokens=4096,        tools=tools,        tool_choice={"type": "tool", "name": "print_sentiment_scores"},        messages=[{"role": "user", "content": query}]    )    json_sentiment = None    for content in response.content:        if content.type == "tool_use" and content.name == "print_sentiment_scores":            json_sentiment = content.input            break    if json_sentiment:        print("Sentiment Analysis (JSON):")        print(json.dumps(json_sentiment, indent=2))    else:        print("No sentiment analysis found in the response.")

我们将在接下来的课程中更详细地介绍 tool_choice。

实体提取示例

让我们使用相同的方法让 Claude 生成格式良好的 JSON,其中包含从文本样本中提取的人员、组织和位置等实体:

python

运行

tools = [    {        "name": "print_entities",        "description": "Prints extract named entities.",        "input_schema": {            "type": "object",            "properties": {                "entities": {                    "type": "array",                    "items": {                        "type": "object",                        "properties": {                            "name": {"type": "string", "description": "The extracted entity name."},                            "type": {"type": "string", "description": "The entity type (e.g., PERSON, ORGANIZATION, LOCATION)."},                            "context": {"type": "string", "description": "The context in which the entity appears in the text."}                        },                        "required": ["name", "type", "context"]                    }                }            },            "required": ["entities"]        }    }]text = "John works at Google in New York. He met with Sarah, the CEO of Acme Inc., last week in San Francisco."query = f"""<document>{text}</document>Use the print_entities tool."""response = client.messages.create(    model="claude-3-sonnet-20240229",    max_tokens=4096,    tools=tools,    messages=[{"role": "user", "content": query}])json_entities = Nonefor content in response.content:    if content.type == "tool_use" and content.name == "print_entities":        json_entities = content.input        breakif json_entities:    print("Extracted Entities (JSON):")    print(json.dumps(json_entities, indent=2))else:    print("No entities found in the response.")

plaintext

Extracted Entities (JSON):{  "entities": [    {      "name": "John",      "type": "PERSON",      "context": "John works at Google in New York."    },    {      "name": "Google",      "type": "ORGANIZATION",      "context": "John works at Google in New York."    },    {      "name": "New York",      "type": "LOCATION",      "context": "John works at Google in New York."    },    {      "name": "Sarah",      "type": "PERSON",      "context": "He met with Sarah, the CEO of Acme Inc., last week in San Francisco."    },    {      "name": "Acme Inc.",      "type": "ORGANIZATION",      "context": "He met with Sarah, the CEO of Acme Inc., last week in San Francisco."    },    {      "name": "San Francisco",      "type": "LOCATION",      "context": "He met with Sarah, the CEO of Acme Inc., last week in San Francisco."    }  ]}

我们使用了与之前相同的 “技巧”。我们告诉 Claude 它可以使用一个工具,以此让 Claude 返回特定的数据格式。然后我们提取 Claude 返回的格式化数据,这样就大功告成了。

请记住,在这种用例中,明确告诉 Claude 我们希望它使用某个特定的工具会很有帮助:

Use the print_entities tool.(使用 print_entities 工具。)

写在中间

我最近正在使用一个超nice的Claude code镜像

「超好用的AICODEMIRROR」

真的非常好用,填写我的码「5OTTEB」还能获得额外额度,快来试试!

维基百科摘要示例(包含更复杂的数据)

让我们尝试另一个更复杂的例子。我们将使用 Python 的wikipedia包获取完整的维基百科页面文章,并将其传递给 Claude。我们会让 Claude 生成包含以下内容的响应:

如果我们向 Claude 传递关于华特・迪士尼的维基百科文章,可能会得到这样的结果:

json

{  "subject": "华特·迪士尼",  "summary": "华特·埃利亚斯·迪士尼是美国动画师、电影制片人和企业家。他是美国动画产业的先驱,在卡通制作方面推出了多项创新。他保持着个人获得奥斯卡奖和提名次数最多的纪录。他还参与了迪士尼乐园和其他主题公园以及电视节目的开发。",  "keywords": [    "华特·迪士尼",    "动画",    "电影制片人",    "企业家",    "迪士尼乐园",    "主题公园",    "电视"  ],  "categories": [    {      "name": "娱乐",      "score": 0.9    },    {      "name": "商业",      "score": 0.7    },    {      "name": "科技",      "score": 0.6    }  ]}

下面是一个函数的示例实现,该函数接收一个维基百科页面主题,查找文章、下载内容、将其传递给 Claude,然后打印生成的 JSON 数据。我们使用相同的策略:通过定义工具来 “引导” Claude 的响应格式。

注意:如果你的电脑上没有安装wikipedia,请确保运行pip install wikipedia进行安装!

python

运行

import wikipedia# 工具定义tools = [    {        "name": "print_article_classification",        "description": "打印分类结果。",        "input_schema": {            "type": "object",            "properties": {                "subject": {                    "type": "string",                    "description": "文章的主题",                },                "summary": {                    "type": "string",                    "description": "文章的段落摘要"                },                "keywords": {                    "type": "array",                    "items": {                        "type": "string",                        "description": "文章中的关键词和主题列表"                    }                },                "categories": {                    "type": "array",                    "items": {                        "type": "object",                        "properties": {                            "name": {"type": "string", "description": "类别的名称。"},                            "score": {"type": "number", "description": "类别的分类分数,范围从0.0到1.0。"}                        },                        "required": ["name", "score"]                    }                }            },            "required": ["subject", "summary", "keywords", "categories"]        }    }]# 为给定文章主题生成json的函数def generate_json_for_article(subject):    page = wikipedia.page(subject, auto_suggest=True)    query = f"""    <document>    {page.content}    </document>    使用print_article_classification工具。示例类别包括政治、体育、科技、娱乐、商业。    """    response = client.messages.create(        model="claude-3-haiku-20240307",        max_tokens=4096,        tools=tools,        messages=[{"role": "user", "content": query}]    )    json_classification = None    for content in response.content:        if content.type == "tool_use" and content.name == "print_article_classification":            json_classification = content.input            break    if json_classification:        print("文本分类(JSON):")        print(json.dumps(json_classification, indent=2))    else:        print("在响应中未找到文本分类。")

python

运行

generate_json_for_article("杰夫·高布伦")

plaintext

文本分类(JSON):{  "subject": "杰夫·高布伦",  "summary": "杰弗里·林恩·高布伦是美国演员和音乐家,曾出演一些票房最高的电影,如《侏罗纪公园》和《独立日》。他在电影和电视领域有着漫长而成功的职业生涯,在众多电影和电视节目中担任角色。高布伦还是一位出色的爵士乐音乐家,与他的乐队The Mildred Snitzer Orchestra一起发行了多张专辑。",  "keywords": [    "演员",    "音乐家",    "《侏罗纪公园》",    "《独立日》",    "电影",    "电视",    "爵士乐"  ],  "categories": [    {      "name": "娱乐",      "score": 0.9    }  ]}

python

运行

generate_json_for_article("章鱼")

plaintext

文本分类(JSON):{  "subject": "章鱼",  "summary": "本文全面概述了章鱼,包括它们的解剖学、生理学、行为、生态学和进化历史。内容涵盖了它们复杂的神经系统、伪装和变色能力、智力以及与人类的关系等主题。",  "keywords": [    "章鱼",    "头足类",    "软体动物",    "海洋生物学",    "动物行为",    "进化"  ],  "categories": [    {      "name": "科学",      "score": 0.9    },    {      "name": "自然",      "score": 0.8    }  ]}

python

运行

generate_json_for_article("赫伯特·胡佛")

plaintext

文本分类(JSON):{  "subject": "赫伯特·胡佛",  "summary": "本文提供了美国第31任总统赫伯特·胡佛的全面传记。内容包括他的早年生活、作为采矿工程师和人道主义者的职业生涯、大萧条期间的总统任期以及他的总统卸任后的活动。",  "keywords": [    "赫伯特·胡佛",    "大萧条",    "共和党",    "美国总统",    "采矿工程师",    "比利时救济委员会",    "美国食品管理局",    "商务部长",    "斯穆特-霍利关税法",    "新政"  ],  "categories": [    {      "name": "政治",      "score": 0.9    },    {      "name": "商业",      "score": 0.7    },    {      "name": "历史",      "score": 0.8    }  ]}

练习

使用上述策略编写一个名为translate的函数,该函数接收一个单词或短语,并生成结构化的 JSON 输出,包含英文原文以及西班牙语、法语、日语和阿拉伯语的翻译。

以下是函数的工作示例:

如果我们调用:

python

运行

translate("how much does this cost")

预期输出如下:

json

{  "english": "how much does this cost",  "spanish": "¿cuánto cuesta esto?",  "french": "combien ça coûte?",  "japanese": "これはいくらですか",  "arabic": "كم تكلفة هذا؟"}

注意:如果你想打印结果,以下代码行将帮助你美观地输出:

python

运行

print(json.dumps(translations_from_claude, ensure_ascii=False, indent=2))

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Claude JSON 工具 结构化输出 API
相关文章