咨询pydantic_ai配置llama3.1实现结构化输出的问题
本地Llama3.1结合Pydantic-AI Agent无法返回指定结构化输出问题
问题概述
使用本地Llama3.1模型时,Pydantic-AI Agent可以正确调用通过tools属性注册的工具,但不会使用启用结构化输出时自动生成的final_result工具,导致无法返回指定类型的结构化输出。需要确认:
- 该问题是否由Llama3.1无法稳定返回
final_result工具调用导致 - 是否存在Pydantic-AI配置项可强制模型触发
final_result工具调用
已知Pydantic-AI提供Prompted Output模式,但文档说明默认的Tool Output模式更稳定,因此优先排查配置是否存在问题。
Agent配置代码
from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIChatModel from pydantic_ai.providers.ollama import OllamaProvider from pydantic import BaseModel, Literal, Union class SyncResult(BaseModel): message: str class AsyncResult(BaseModel): workflow_name: str class MainAgentOutput(BaseModel): mode: Literal["sync", "async"] result: Union[SyncResult, AsyncResult] main_agent = Agent( model=OpenAIChatModel( model_name="llama3.1:latest", provider=OllamaProvider( base_url="http://localhost:11434/v1", api_key="somekey" ) ), output_type=MainAgentOutput, tools=get_tools(), system_prompt=build_system_prompt(), )
系统提示内容
You are a routing agent. You can either: 1. Execute one or more synchronous tools (mode="sync") 2. Select an async workflow (mode="async") Information available to you: - Synchronous tools: Their names, parameters, and docstrings are provided separately by the system. Use those tool descriptions to understand exactly what each tool can do. - Asynchronous workflows (with routing descriptions): {workflow_descriptions} Output contract: - When you choose mode="sync": - Call the appropriate synchronous tools as needed. - Populate result as a SyncResult. - Set result.message to a clear, user-facing text answer that summarizes the tool outputs and directly answers the request. - When you choose mode="async": - Populate result as an AsyncResult. - Set result.workflow_name to exactly one of the keys listed in the async workflows. - Do not start or run the workflow yourself; you only suggest which workflow should be used. Routing rules: - Read the user request and compare it to: • The capabilities described by the synchronous tools (their names, arguments, and docstrings). • The routing_description of each async workflow above. - Choose mode="sync" when one or more synchronous tools can fully satisfy the request. - Choose mode="async" when the request clearly matches a workflow's routing_description or requires the type of processing that workflow is designed for. - Do NOT base the decision purely on whether the task seems long or short; use the described capabilities instead. - Never invent workflow names. - If both a tool and a workflow could handle the request, prefer mode="sync" unless the workflow description explicitly makes it the better fit.
测试流程与错误日志
测试提示语:How many entries are in the database
消息流程
- ModelRequest(系统提示+用户提示)
- ModelResponse(正确调用注册工具
get_number_of_entries,参数符合预期) - ModelRequest(工具调用响应返回结果42)
- ModelResponse(返回文本答案,未调用
final_result工具) - ModelRequest(返回格式错误提示:
Invalid JSON: expected ident at line 1 column 2) - ModelResponse(再次调用
get_number_of_entries工具,误解了错误信息) - ModelRequest(工具再次返回结果42)
- ModelResponse(再次返回文本答案)
最终错误
Exceeded maximum retries (1) for output validation
完整消息日志
[ ModelRequest( parts=[ SystemPromptPart( content=... ), UserPromptPart( content="How many proteins are in the database?", ), ], ), ModelResponse( parts=[ ToolCallPart( tool_name="get_number_of_entries", args='{"entry_type": ...}', ) ], model_name="llama3.1:latest", provider_name="ollama", provider_url="http://localhost:11434/v1/", provider_details={ "finish_reason": "tool_calls", }, finish_reason="tool_call", ), ModelRequest( parts=[ ToolReturnPart( tool_name="get_number_of_entries", content=42, ) ], ), ModelResponse( parts=[ TextPart( content=... ) ], provider_details={ "finish_reason": "stop", }, finish_reason="stop", ), ModelRequest( parts=[ RetryPromptPart( content=[ { "type": "json_invalid", "loc": (), "msg": "Invalid JSON: expected ident at line 1 column 2", "input": ..., "ctx": {"error": "expected ident at line 1 column 2"}, } ], tool_call_id="pyd_ai_286c9b4a75114705b6dbfc44f0f585ed", ) ], ), ModelResponse( parts=[ ToolCallPart( tool_name="get_number_of_entries", args=..., tool_call_id="call_9ono7lfu", ) ], usage=RequestUsage(input_tokens=969, output_tokens=20), model_name="llama3.1:latest", provider_name="ollama", provider_url="http://localhost:11434/v1/", provider_details={ "finish_reason": "tool_calls", }, finish_reason="tool_call", ), ModelRequest( parts=[ ToolReturnPart( tool_name="get_number_of_entries", content=42, ) ], ), ModelResponse( parts=[ TextPart( content=... ) ], finish_reason="stop", ), ]
内容的提问来源于stack exchange,提问作者C. Zeil
相关产品推荐
相关产品推荐

