之前探索了LLM与程序协作来去重清洗文本格式数据

https://blog.csdn.net/liliang199/article/details/159865538

这里进一步探索LLM如何以ReAct Agent的方式,统计分析融合去重后的数据。

LLM不实际运行统计分析,而是借助一专业统计工具完成,以确保分析过程的准确性。

所用示例参考和修改自网络资料。

1 Agent机制

1.1  LLM统计存在的问题

统计任务sum/count/avg/group by要求绝对精确。

LLM虽然也具备统计能力,直接让LLM处理统计任务,由可能存在多次运行不一致的问题。

而且,随着统计数据规模的扩大,由注意力漂移导致的误差累积会越来越多。

LLM的不稳定性和幻觉会影响到统计结果的准确性。

1.2 Agent ReAct机制

既然LLM不善于处理统计任务,更合理的方案是,准备各种统计工具,由LLM决定调用哪些工具。

具体为

1)LLM理解自然语言,生成工具调用如 SQL

2)由Agent调用后端同工具获得精确结果

3)再由LLM 组织自然语言回答。

Agent ReAct模式允许多轮工具调用,能处理复杂查询。

比如“对比今年和去年的销售额”需要两次调用工具。


 

2 Agent 运行

这里以代码方式示例LLM如何以ReAct Agent方式统计分析去重后数据。

2.1 环境设置

参考之前文档,LLM环境设置示例代码 如下。

import os
 
model_name = gpt_model_name # LLM名称,比如deepseek-r1, qwen3.5-8b
os.environ['OPENAI_API_KEY'] = gpt_api_key # LLM供应商提供的api key
os.environ['OPENAI_BASE_URL'] = gpt_api_url # LLM供应商提供llm访问api的url
 
from openai import OpenAI
 
client = OpenAI()

这里采用上一节“LLM与程序协作来去重清洗文本格式数据”去重处理后的数据进行测试。

name  company  date  description ext_person  ext_place   ext_amount  ext_date original_ids   index id block_key   new_id
0  张三 ABC科技 2023-05-02  张三昨天在北京签了合同,金额50000元 | 张总与北京分公司达成5万交易  张三 北京 50000 元  2024-05-21  [1, 2]   NaN   NaN   NaN   1
1  李四 XYZ有限公司  2023-06-01  李四在上海支付了1200元  李四 上海 1200 元   None  NaN   2.0   3.0   XYZ有  2

数据表名称为records_dedup,参考链接如下

https://blog.csdn.net/liliang199/article/details/159865538

2.2 工具定义

这里定义数据库查询统计工具execute_sql,统计逻辑由sql定义实现。

1)工具代码


# ================= 阶段3:查询统计 + ReAct Agent =================
# 定义工具:执行 SQL 查询(只读)
def execute_sql(query: str) -> str:
    """执行 SQL SELECT 语句,返回 JSON 字符串"""
    try:
        print(query)
        conn = sqlite3.connect("example.db")
        df_result = pd.read_sql_query(query, conn)
        conn.close()
        return df_result.to_json(orient="records")
    except Exception as e:
        return f"SQL错误: {e}"

query = "select * from records_dedup"
json_data = execute_sql(query)
for result in json.loads(json_data):
    print(result)

输出示例如下,可见该工具能实际运行sql。

select * from records_dedup
{'name': '张三', 'company': 'ABC科技', 'date': '2023-05-02', 'description': '张三昨天在北京签了合同,金额50000元 | 张总与北京分公司达成5万交易', 'ext_person': '张三', 'ext_place': '北京', 'ext_amount': '50000 元', 'ext_date': '2024-05-21', 'original_ids': '[1, 2]', 'index': None, 'id': None, 'block_key': None, 'new_id': 1}
{'name': '李四', 'company': 'XYZ有限公司', 'date': '2023-06-01', 'description': '李四在上海支付了1200元', 'ext_person': '李四', 'ext_place': '上海', 'ext_amount': '1200 元', 'ext_date': None, 'original_ids': None, 'index': 2.0, 'id': 3.0, 'block_key': 'XYZ有', 'new_id': 2}
 

2)工具描述

为方便LLM理解工具,这里定义工具描述,示例如下。

# 定义工具描述(供 LLM 理解)
tools = [
    {
        "type": "function",
        "function": {
            "name": "execute_sql",
            "description": "在 SQLite 数据库上执行只读 SQL 查询。数据库表 'records_dedup' 包含字段:id, name, company, date, description, ext_person, ext_place, ext_amount, ext_date",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "有效的 SELECT SQL 语句"}
                },
                "required": ["query"]
            }
        }
    }
]

2.3 Agent运行

这里运行基于 ReAct Agent 的自然语言查询,即统计类任务精确执行 SQL。

由LLM决定是否调用工具,在需要时由Agent运行工具程序,拿到结果后生成最终答案。

代码如下所示。

def react_agent(user_question: str, max_steps=3):
    """ReAct 循环:LLM 决定是否调用工具,拿到结果后给出最终答案"""
    messages = [
        {"role": "system", "content": "你是一个数据库查询助手。你需要根据用户问题,调用 execute_sql 工具来获取精确数据。不允许自己编造数字。若需要多步查询,请逐步调用工具。最终用自然语言回答用户。"},
        {"role": "user", "content": user_question}
    ]
    for _ in range(max_steps):
        response = client.chat.completions.create(
            model=model_name,
            messages=messages,
            tools=tools,
            tool_choice="auto",
            temperature=0
        )
        msg = response.choices[0].message
        messages.append(msg)  # 将助手消息加入历史

        # 如果没有工具调用,说明已经得出最终答案
        if not msg.tool_calls:
            return msg.content

        # 执行工具调用
        for tool_call in msg.tool_calls:
            func_name = tool_call.function.name
            args = json.loads(tool_call.function.arguments)
            if func_name == "execute_sql":
                result = execute_sql(args["query"])
                print(result)
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": result
                })
    return "达到最大步骤限制,未完成查询。"

# 测试查询
print("\n=== ReAct Agent 查询示例 ===")
question = "统计每个公司的总金额(ext_amount),按金额从高到低排序"
answer = react_agent(question)
print(f"用户问题:{question}")
print(f"最终答案:{answer}")

# 另一个需要多步的查询(演示)
question2 = "先查出张三的 ext_amount,再查出李四的,比较谁大"
answer2 = react_agent(question2)
print(f"\n用户问题:{question2}")
print(f"最终答案:{answer2}")

# 关闭连接
conn.close()

输入如下所示。

=== ReAct Agent 查询示例 ===
SELECT company, SUM(ext_amount) as total_amount FROM records_dedup GROUP BY company ORDER BY total_amount DESC
[{"company":"ABC\u79d1\u6280","total_amount":50000.0},{"company":"XYZ\u6709\u9650\u516c\u53f8","total_amount":1200.0}]
用户问题:统计每个公司的总金额(ext_amount),按金额从高到低排序
最终答案:

根据查询结果,每个公司的总金额统计如下(按金额从高到低排序):

| 公司 | 总金额 |
|------|--------|
| ABC 科技 | 50,000.00 |
| XYZ 有限公司 | 1,200.00 |

其中,ABC 科技的总金额最高,为 50,000.00 元;XYZ 有限公司的总金额为 1,200.00 元。
SELECT ext_amount FROM records_dedup WHERE name = '张三'
[{"ext_amount":"50000 \u5143"}]
SELECT ext_amount FROM records_dedup WHERE name = '李四'
[{"ext_amount":"1200 \u5143"}]

用户问题:先查出张三的 ext_amount,再查出李四的,比较谁大
最终答案:

根据查询结果:

- **张三**的 ext_amount 为 **50000 元**
- **李四**的 ext_amount 为 **1200 元**

比较结果:**张三的 ext_amount 更大**,比李四多 48800 元。

相比直接运行程序,Agent可以更有效地调用工具,而且避免了LLM不稳定和存在幻觉的问题。

3 优化注意点

Agent相比直接运行程序更高效,由于Agent依赖LLM决策,依然可能存在不稳定性问题。

这里罗列一些可能存在的问题,以及可能的优化方案。

1)LLM生成SQL可能存在语法或逻辑错误,如join缺失,需增加SQL语法校验等安全限制。

2)多轮调用可能产生冗长的中间输出,所以需要设计清晰的工具描述和终止条件。

3)提供数据库schema的详细描述(表名、字段、类型、示例值)作为 system prompt。

4)对于对常见查询类型可预置模板减少推理开销。

reference

---

LLM与程序协作来去重清洗文本格式数据

https://blog.csdn.net/liliang199/article/details/159865538

LLM如何与程序协作来结构化文本财报数据

https://blog.csdn.net/liliang199/article/details/159834630

如何使用向量库faiss和LLM判断问题是否被记录

https://blog.csdn.net/liliang199/article/details/159476341

LLM如何基于tools对同一数据不同问题进行查询

https://blog.csdn.net/liliang199/article/details/159767913

更多推荐