You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

LangGraph工具调用节点工作异常问题求助

LangGraph工具调用节点工作异常问题求助

我现在在LangGraph里做一个简单的Demo,想让LLM在需要的时候调用维基百科搜索工具,但遇到了奇怪的问题:

有时候LLM返回的结果里明明有类似<function=search-wikipedia{"query": "Palmyra"}</function>的内容,但工具调用列表Tool Calls []却是空的,而且第一次运行经常触发不了工具节点,第二次运行又能正常执行工具函数,偶尔第一次也能成功,完全摸不准规律。

下面是我拿到的LLM返回结果:

The resp: content='<function=search-wikipedia{"query": "Palmyra"}</function>' additional_kwargs={} response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 247, 'total_tokens': 264, 'completion_time': 0.061818182, 'prompt_time': 0.034164437, 'queue_time': 0.666112673, 'total_time': 0.095982619}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_3884478861', 'finish_reason': 'stop', 'logprobs': None} id='run-cd05b340-619d-4672-bdda-578f095985ad-0' usage_metadata={'input_tokens': 247, 'output_tokens': 17, 'total_tokens': 264}

同时工具调用显示:Tool Calls []

我的代码如下:

@tool("search-wikipedia", args_schema=SearchWikiInput)
def get_res(query: str) -> str:
    
    print(bcolors.WARNING + f'Using the wikipedia tool:...\n' + bcolors.ENDC)
    search_query = model.invoke([SystemMessage(content=f'''Convert the user queryinto search keyword in wikipedia,
    if the query is a simple greeting, return an empty response only, else return only and only the keyword: {query}''')]).content
    if search_query == "":
        return {'wiki_content': ''}
    pages = search_for_page(query)
    content = get_wiki_content(pages)
    if not content:
        return "No results found."
    
    formatted_results = []
    for page in content:
        formatted_results.append(
        f"Title: {page.title}\n"
        f"Summary: {page.summary[:800]}...\n"
        f"URL: {page.url}\n"
        )
    results = '\n\n'.join(formatted_results)
    print(bcolors.WARNING + f'Returned Results from wikipedia' + bcolors.ENDC)
    return {'wiki_content': results}

tools = [get_res]

tools_dict = {tool.name: tool for tool in tools}
model = ChatGroq(model_name=model_name, temperature=0.1).bind_tools(tools)
def tools_node(state: State):
    """Tool call node that executes the tools based on the plan."""
    outputs = []
    print('Hello from the tools node')
    for tool_call in state['messages'][-1].tool_calls:
        tool_results = tools_dict[tool_call['name']].invoke(tool_call['args'])
        outputs.append(
            ToolMessage(
                content=json.dumps(tool_results),
                name=tool_call['name'],
                tool_call_id=tool_call['id']
            )
        )
    return {'messages': outputs}
def agent_node(state: State):
    """Agent call node that uses the LLM with tools to answer the user query."""
    agent_prompt = 'You are a helpful assistant, use the external tools only when needed'
    system_prompt = SystemMessage(content=agent_prompt)
    response = model.invoke([system_prompt] + state["messages"])
    print(f'The resp: {response}')
    return {"messages": [response]}
def should_continue(state:State):
    messages = state["messages"]
    last_message = messages[-1]
    print(f'Tool Calls {last_message.content}')
    if last_message.tool_calls:
        return 'continue'
    else:
        return 'END'

workflow = StateGraph(State)
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tools_node)
workflow.add_edge(START,"agent")
workflow.add_edge('tools',"agent")

workflow.add_conditional_edges("agent",should_continue,{'continue':'tools','END':END})
graph = workflow.compile()

问题分析与解决建议

我之前也遇到过类似的情况,核心问题是LLM返回的工具调用格式和LangGraph期望的标准格式不匹配

  1. 格式不匹配是核心原因
    从返回结果看,你的LLM用了自定义的<function>标签格式返回工具调用,但LangChain的bind_tools默认期望模型返回符合标准结构的tool_calls字段,所以last_message.tool_calls才会是空的。至于偶尔第二次成功,可能是因为上下文里保留了之前的成功调用示例,模型自动调整了输出格式。

  2. 具体修复方案

方案一:强制模型使用标准工具调用格式

修改你的agent_prompt,明确告诉模型必须用LangChain的标准工具调用格式,不能用自定义标签。比如:

agent_prompt = '''你是一个乐于助人的助手,只有在需要外部信息时才使用提供的工具。
当你需要调用工具时,必须使用以下标准格式返回,不要使用任何自定义标签:
{"tool_calls": [{"name": "工具名称", "args": {"参数名": "参数值"}}]}
'''

同时,bind_tools已经会自动给模型注入工具的描述信息,不用手动在prompt里重复说明工具,保持prompt简洁明确即可。

方案二:手动解析自定义格式(如果模型顽固不化)

如果模型还是坚持输出<function>标签,可以在agent_node里手动解析这个格式,把它转换成LangChain能识别的tool_calls结构:

import re
import json
from langchain_core.messages import ToolCall

def agent_node(state: State):
    """Agent call node that uses the LLM with tools to answer the user query."""
    agent_prompt = 'You are a helpful assistant, use the external tools only when needed'
    system_prompt = SystemMessage(content=agent_prompt)
    response = model.invoke([system_prompt] + state["messages"])
    print(f'The resp: {response}')
    
    # 手动解析自定义的<function>标签格式
    content = response.content
    tool_call_match = re.search(r'<function=(\w+)\{(.+)\}</function>', content)
    if tool_call_match and not response.tool_calls:
        tool_name = tool_call_match.group(1)
        try:
            tool_args = json.loads(tool_call_match.group(2))
            # 构造标准的ToolCall对象
            response.tool_calls = [
                ToolCall(name=tool_name, args=tool_args, id="1")
            ]
        except json.JSONDecodeError:
            # 解析失败就忽略,不触发工具调用
            pass
    return {"messages": [response]}

额外优化建议

  • 你的get_res工具内部又调用了一次模型来转换查询关键词,这会额外消耗token,还可能引入错误。建议把这个关键词转换逻辑移到agent_node里,让模型在生成工具调用时直接传入正确的搜索关键词。
  • 确保SearchWikiInput的schema定义正确,和工具的query参数类型完全匹配,避免模型生成的参数不兼容。
  1. 验证修复效果
    修改后,第一次运行时观察last_message.tool_calls是否有内容,如果有,就会正常进入tools_node执行工具了。

希望这些方案能帮到你!

备注:内容来源于stack exchange,提问作者MDD

火山引擎 最新活动