本示例来自官方文档 Build a custom SQL agent - Docs by LangChain
主要问题出在了Implement human-in-the-loop review 一节。问题不是逻辑处理部分,而是输出逻辑。主要症状是没有如预期般打印出INTERRUPTED:开头的日志。
请先观察大模型调用的输出逻辑:
import json
config = {"configurable": {"thread_id": "1"}}
question = "Which genre on average has the longest tracks?"
for step in agent.stream(
{"messages": [{"role": "user", "content": question}]},
config,
stream_mode="values",
):
if "messages" in step:
step["messages"][-1].pretty_print()
elif "__interrupt__" in step:
action = step["__interrupt__"][0]
print("INTERRUPTED:")
for request in action.value:
print(json.dumps(request, indent=2))
else:
pass
看起来没啥问题,但是呢,代码中第16行,就是没有正常打印INTERRUPTED。
排查过程
一开始就怀疑是否自己示例代码抄错了,由于这部分示例是基于前几步的代码改造来的,于是就认真的检查了一下代码,还真给检查出来两点:
- 新方法
run_query_tool_with_interrupt构造了新的 node,用来代替之前的sql_db_querynode。这一步本来不会有什么问题,因为对象会被覆盖,为了保险一些,还是把它注掉了。 - 当检查构建graph的步骤时发现,由
check_query到run_query的边没有了。但是官方文档并没有给出删除的指示。
除了这些其他都一模一样了,于是就信心满满的继续运行。
还是出意外了,依然没有打印出预期中的INTERRUPTED。
到这里基本进入死胡同了,唯一可以怀疑的就是,是否是这种中断写法有问题。
刚好文档中给出了中断参考链接 Interrupts - Docs by LangChain
排查中断写法
这种怀疑确实有些大胆,但是呢,死马当活马医吧。
于是就硬着头皮看吧,看了老半天,并且把其中的几个示例跑了一遍。
还好,这几个示例没有问题,都正常跑了。
最后,得出结论:中断的写法没问题。
最后的办法
实在没办法了,还是调试看看具体是怎么输出的吧。这么一调试,终于发现了其中的猫腻。原来是在输出的判断逻辑上。
原因
当 step对象有 message 时,不一定有 __interrupt__,而发生了中断时,有__interrupt__时,仍然有 message。就导致了,发生中断时,走不到elif分支。
不仅如此,而且还会导致 message 重复打印,比如下面这样,可以看到 id是相同的,都是 6c6a5c6169654fcabe9de3467aea8540
================================== Ai Message ==================================
Tool Calls:
sql_db_query (chatcmpl-tool-6c6a5c6169654fcabe9de3467aea8540)
Call ID: chatcmpl-tool-6c6a5c6169654fcabe9de3467aea8540
Args:
query: SELECT Genre.Name, AVG(Track.Milliseconds) AS AvgLength FROM Track JOIN Genre ON Track.GenreId = Genre.GenreId GROUP BY Genre.GenreId ORDER BY AvgLength DESC LIMIT 5;
================================== Ai Message ==================================
Tool Calls:
sql_db_query (chatcmpl-tool-6c6a5c6169654fcabe9de3467aea8540)
Call ID: chatcmpl-tool-6c6a5c6169654fcabe9de3467aea8540
Args:
query: SELECT Genre.Name, AVG(Track.Milliseconds) AS AvgLength FROM Track JOIN Genre ON Track.GenreId = Genre.GenreId GROUP BY Genre.GenreId ORDER BY AvgLength DESC LIMIT 5;
解决
找到了原因,解决就很好办,调整一下判断的先后顺序即可。代码就不贴出来了。
解决了这个问题后,从中断恢复的示例也正常执行了。到此,这个Custom SQL agent示例就跑通了。