|
@@ -0,0 +1,395 @@
|
|
|
+## ask_agent()
|
|
|
+
|
|
|
+ask_agent()用来代替原来的ask() API,它采用LLM Agent技术,支持混合对话,即数据库查询和自由对话模式。
|
|
|
+
|
|
|
+### API端点一览
|
|
|
+
|
|
|
+|API端点|方法|功能描述|
|
|
|
+|-|-|-|
|
|
|
+|`/api/v0/ask_agent`|POST|智能问答查询(支持数据库查询和自由对话)|
|
|
|
+
|
|
|
+### 1.1.成功生成SQL并执行查询
|
|
|
+
|
|
|
+POST [http://localhost:8084/api/v0/ask_agent](http://localhost:8084/api/v0/ask_agent)
|
|
|
+
|
|
|
+```JSON
|
|
|
+{
|
|
|
+ "question": "请按照收入给每个高速服务区进行排名?返回收入最多的前三名服务区?"
|
|
|
+}
|
|
|
+
|
|
|
+#正常生成SQL,并完成查询的返回结果
|
|
|
+
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "agent_version": "langgraph_v1",
|
|
|
+ "classification_info": {
|
|
|
+ "confidence": 0.9,
|
|
|
+ "method": "rule_based_strong_business",
|
|
|
+ "reason": "强业务特征 - 业务实体: ['核心业务实体:服务区', '支付业务:收入'], 查询意图: ['排名'], SQL: []"
|
|
|
+ },
|
|
|
+ "context_used": false,
|
|
|
+ "conversation_id": "conv_1751199617_5d37a647",
|
|
|
+ "conversation_message": "创建新对话",
|
|
|
+ "conversation_status": "new",
|
|
|
+ "execution_path": [
|
|
|
+ "start",
|
|
|
+ "classify",
|
|
|
+ "agent_sql_generation",
|
|
|
+ "agent_sql_execution",
|
|
|
+ "format_response"
|
|
|
+ ],
|
|
|
+ "from_cache": false,
|
|
|
+ "is_guest_user": true,
|
|
|
+ "records": {
|
|
|
+ "columns": [
|
|
|
+ "服务区名称",
|
|
|
+ "总收入"
|
|
|
+ ],
|
|
|
+ "is_limited": false,
|
|
|
+ "row_count": 3,
|
|
|
+ "rows": [
|
|
|
+ {
|
|
|
+ "总收入": "7024226.1500",
|
|
|
+ "服务区名称": "庐山服务区"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "总收入": "6929288.3300",
|
|
|
+ "服务区名称": "三清山服务区"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "总收入": "6848435.6700",
|
|
|
+ "服务区名称": "南城服务区"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "total_row_count": 3
|
|
|
+ },
|
|
|
+ "response": "根据收入排名,前三名高速服务区依次为:庐山服务区(702.42万元)、三清山服务区(692.93万元)、南城服务区(684.84万元)。",
|
|
|
+ "routing_mode_source": "config",
|
|
|
+ "routing_mode_used": "hybrid",
|
|
|
+ "session_id": null,
|
|
|
+ "sql": "SELECT service_name AS 服务区名称, SUM(pay_sum) AS 总收入 \nFROM bss_business_day_data \nWHERE delete_ts IS NULL \nGROUP BY service_name \nORDER BY 总收入 DESC NULLS LAST \nLIMIT 3;",
|
|
|
+ "summary": "根据收入排名,前三名高速服务区依次为:庐山服务区(702.42万元)、三清山服务区(692.93万元)、南城服务区(684.84万元)。",
|
|
|
+ "timestamp": "2025-06-29T20:20:56.806141",
|
|
|
+ "type": "DATABASE",
|
|
|
+ "user_id": "guest"
|
|
|
+ },
|
|
|
+ "message": "操作成功",
|
|
|
+ "success": true
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+前端UI应关注的参数:
|
|
|
+
|
|
|
+1."response": 它将代替原来的summary,会查询的结果进行总结。
|
|
|
+
|
|
|
+2."sql":执行查询SQL.
|
|
|
+
|
|
|
+3."data.records":查询返回的数据,包括表头(data.records.columns)和数据行(data.records.rows)
|
|
|
+
|
|
|
+### 1.2.未成功生成SQL
|
|
|
+
|
|
|
+POST [http://localhost:8084/api/v0/ask_agent](http://localhost:8084/api/v0/ask_agent)
|
|
|
+
|
|
|
+```JSON
|
|
|
+{
|
|
|
+ "question": "请问每个高速公路服务区的管理经理是谁?"
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+# 返回结果
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "agent_version": "langgraph_v1",
|
|
|
+ "classification_info": {
|
|
|
+ "confidence": 0.82,
|
|
|
+ "method": "rule_based_medium_business",
|
|
|
+ "reason": "中等业务特征 - 业务实体: ['核心业务实体:服务区', '核心业务实体:高速公路']"
|
|
|
+ },
|
|
|
+ "context_used": false,
|
|
|
+ "conversation_id": "conv_1751201276_e59f0a07",
|
|
|
+ "conversation_message": "创建新对话",
|
|
|
+ "conversation_status": "new",
|
|
|
+ "execution_path": [
|
|
|
+ "start",
|
|
|
+ "classify",
|
|
|
+ "agent_sql_generation",
|
|
|
+ "format_response"
|
|
|
+ ],
|
|
|
+ "from_cache": false,
|
|
|
+ "is_guest_user": true,
|
|
|
+ "response": "当前提供的上下文信息不足以生成查询服务区对应管理经理的SQL语句。原因如下:\n\n1. 在服务区管理公司表(bss_company)中虽然存在created_by/updated_by字段,但这些字段仅记录数据操作者(系统用户),而非实际的管理经理人员信息。\n\n2. 现有表结构中缺失以下关键实体:\n - 员工/人员信息表(存储经理姓名等个人信息)\n - 公司与人员的组织架构表(关联公司ID与员工ID)\n\n3. 当前表间关系仅能查询到服务区所属的管理公司名称(通过bss_service_area.company_id关联bss_company.id),但无法获取具体管理人员的姓名。\n\n需要补充以下信息才能继续:\n- 存储人员信息的表结构(特别是管理岗位人员)\n- 公司与人员的关联关系表结构 请尝试提问其它问题。",
|
|
|
+ "routing_mode_source": "config",
|
|
|
+ "routing_mode_used": "hybrid",
|
|
|
+ "session_id": null,
|
|
|
+ "timestamp": "2025-06-29T20:48:21.351324",
|
|
|
+ "type": "DATABASE",
|
|
|
+ "user_id": "guest"
|
|
|
+ },
|
|
|
+ "message": "操作成功",
|
|
|
+ "success": true
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+前端UI应关注的参数:
|
|
|
+
|
|
|
+1.没有返回"sql"和"data.records"。
|
|
|
+
|
|
|
+2."response":当没有返回"sql"和"data.records"的时候,response会返回未能生成SQL的原因,可以返回给客户端。
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### 1.3.在Post中传递路由参数
|
|
|
+
|
|
|
+当Post 参数中,没有包含routing_mode 参数时,默认会采用服务配置文件app_config.py中"QUESTION_ROUTING_MODE "参数,它的默认值是"hybrid"。
|
|
|
+
|
|
|
+这个routing_mode 参数的含义是,手工调整查询数据库或自由对话的路由。例如,当我们在切换话题时,如果LLM不能及时的从数据库查询/自由对话模式路由到正确模式,那么需要通过传递这个参数来强迫系统查询数据库,或自由对话。它为对话提供一个兜底方案。
|
|
|
+
|
|
|
+我们可以在对话框的下方或者右侧合适的位置,添加两个按钮"数据查询模式"和"自由对话模式",当点击"数据查询模式时",在POST参数中自动添加"routing_mode: database_direct",当点击"自由对话模式",在POST参数中自动添加"routing_mode: chat_direct".
|
|
|
+
|
|
|
+```Markdown
|
|
|
+routing_mode (string, 可选)
|
|
|
+问题路由模式,控制AI如何处理问题
|
|
|
+有效值:
|
|
|
+- "database_direct": 直接数据库查询模式
|
|
|
+- "chat_direct": 直接聊天模式
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+举例说明:
|
|
|
+
|
|
|
+POST [http://localhost:8084/api/v0/ask_agent](http://localhost:8084/api/v0/ask_agent)
|
|
|
+
|
|
|
+下面的例子,如果不添加 "routing_mode": "database_direct",这个问题会被路由到自由查询模式,因为数据库中没有充电桩的数据。但 "routing_mode": "database_direct" 参数会强迫系统查询数据库。
|
|
|
+
|
|
|
+```JSON
|
|
|
+{
|
|
|
+ "question": "请问中国共有多少个充电桩",
|
|
|
+ "routing_mode": "database_direct"
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+# 上面的参数,返回结果:
|
|
|
+
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "agent_version": "langgraph_v1",
|
|
|
+ "classification_info": {
|
|
|
+ "confidence": 1,
|
|
|
+ "method": "direct_database",
|
|
|
+ "reason": "配置为直接数据库查询模式"
|
|
|
+ },
|
|
|
+ "context_used": false,
|
|
|
+ "conversation_id": "conv_1751202425_d04139e4",
|
|
|
+ "conversation_message": "创建新对话",
|
|
|
+ "conversation_status": "new",
|
|
|
+ "execution_path": [
|
|
|
+ "start",
|
|
|
+ "init_direct_database",
|
|
|
+ "agent_sql_generation",
|
|
|
+ "format_response"
|
|
|
+ ],
|
|
|
+ "from_cache": false,
|
|
|
+ "is_guest_user": true,
|
|
|
+ "response": "提供的上下文未包含充电桩相关数据字段,无法统计中国充电粧总数。当前数据库中服务区相关表(bss_service_area)未记录充电粧数量信息,建议确认业务表结构或补充数据来源。 请尝试提问其它问题。",
|
|
|
+ "routing_mode_source": "api",
|
|
|
+ "routing_mode_used": "database_direct",
|
|
|
+ "session_id": null,
|
|
|
+ "timestamp": "2025-06-29T21:07:26.383228",
|
|
|
+ "type": "DATABASE",
|
|
|
+ "user_id": "guest"
|
|
|
+ },
|
|
|
+ "message": "操作成功",
|
|
|
+ "success": true
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### 1.4. 关于用户认证
|
|
|
+
|
|
|
+按照项目组的约定,将来会从Post中获取token令牌,然后通过API获取user_id。在当前系统中,暂时没有提供用户验证功能。出于测试目的可以传递user_id,不同的user_id会看到属于自己的对话记录。
|
|
|
+
|
|
|
+如果没有传递user_id,那么将使用默认用户guest.
|
|
|
+
|
|
|
+POST [http://localhost:8084/api/v0/ask_agent](http://localhost:8084/api/v0/ask_agent)
|
|
|
+
|
|
|
+```JSON
|
|
|
+{
|
|
|
+ "question": "请问中国共有多少个充",
|
|
|
+ "user_id": "Paul"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### 1.5.关于上下文对话的传递(多轮对话)
|
|
|
+
|
|
|
+每个用户user_id,默认会保存最近5次的"会话"(conversation_id),每个conversation_id会保存10组对话信息。每次上下文传递默认会传递最近2次的对话上下文。
|
|
|
+
|
|
|
+这些参数在app_config.py中进行控制:
|
|
|
+
|
|
|
+```Python
|
|
|
+# app_config.py 的对话上下文配置
|
|
|
+CONVERSATION_CONTEXT_COUNT = 2 # 传递给LLM的上下文消息条数
|
|
|
+CONVERSATION_MAX_LENGTH = 10 # 单个对话最大消息数
|
|
|
+USER_MAX_CONVERSATIONS = 5 # 用户最大对话数
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+**第一轮对话**:
|
|
|
+
|
|
|
+POST [http://localhost:8084/api/v0/ask_agent](http://localhost:8084/api/v0/ask_agent)
|
|
|
+
|
|
|
+```JSON
|
|
|
+{
|
|
|
+ "question": "请问哪个服务区的档口最多?",
|
|
|
+ "user_id": "Paul"
|
|
|
+}
|
|
|
+
|
|
|
+# 请注意在下面的返回的结果中,包含了 "data.conversation_id",
|
|
|
+# "conversation_id": "conv_1751205549_308eb7c3",
|
|
|
+
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "agent_version": "langgraph_v1",
|
|
|
+ "classification_info": {
|
|
|
+ "confidence": 0.82,
|
|
|
+ "method": "rule_based_medium_business",
|
|
|
+ "reason": "中等业务特征 - 业务实体: ['核心业务实体:服务区', '核心业务实体:档口']"
|
|
|
+ },
|
|
|
+ "context_used": false,
|
|
|
+ "conversation_id": "conv_1751205549_308eb7c3",
|
|
|
+ "conversation_message": "创建新对话",
|
|
|
+ "conversation_status": "new",
|
|
|
+ "execution_path": [
|
|
|
+ "start",
|
|
|
+ "classify",
|
|
|
+ "agent_sql_generation",
|
|
|
+ "agent_sql_execution",
|
|
|
+ "format_response"
|
|
|
+ ],
|
|
|
+ "from_cache": false,
|
|
|
+ "is_guest_user": false,
|
|
|
+ "records": {
|
|
|
+ "columns": [
|
|
|
+ "服务区名称",
|
|
|
+ "档口数量"
|
|
|
+ ],
|
|
|
+ "is_limited": false,
|
|
|
+ "row_count": 1,
|
|
|
+ "rows": [
|
|
|
+ {
|
|
|
+ "服务区名称": "南城服务区",
|
|
|
+ "档口数量": 39
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "total_row_count": 1
|
|
|
+ },
|
|
|
+ "response": "南城服务区的档口数量最多,共计39个。",
|
|
|
+ "routing_mode_source": "config",
|
|
|
+ "routing_mode_used": "hybrid",
|
|
|
+ "session_id": null,
|
|
|
+ "sql": "SELECT sa.service_area_name AS 服务区名称, COUNT(b.id) AS 档口数量 FROM bss_service_area sa JOIN bss_branch b ON sa.id = b.service_area_id WHERE sa.delete_ts IS NULL AND b.delete_ts IS NULL GROUP BY sa.service_area_name ORDER BY 档口数量 DESC NULLS LAST LIMIT 1;",
|
|
|
+ "summary": "南城服务区的档口数量最多,共计39个。",
|
|
|
+ "timestamp": "2025-06-29T22:00:17.154155",
|
|
|
+ "type": "DATABASE",
|
|
|
+ "user_id": "Paul"
|
|
|
+ },
|
|
|
+ "message": "操作成功",
|
|
|
+ "success": true
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+请前端UI关注"conversation_id": "conv_1751205549_308eb7c3",当第二次或后续再次提交POST的时候,传递相同的"user_id"和"conversation_id",后台服务就会把相同的"conversation_id"作为一次会话保存。再提交问题给LLM的时候,会把前面两次对话记录传递给LLM。
|
|
|
+
|
|
|
+**第二轮对话**,注意POST参数中提供了第一轮对话中返回的"conversation_id",然后在第二个问题中延续了第一个问题中的答案。
|
|
|
+
|
|
|
+POST [http://localhost:8084/api/v0/ask_agent](http://localhost:8084/api/v0/ask_agent)
|
|
|
+
|
|
|
+```JSON
|
|
|
+{
|
|
|
+ "conversation_id": "conv_1751205549_308eb7c3",
|
|
|
+ "question": "请问这个服务区的餐饮档口有几个?",
|
|
|
+ "user_id": "Paul"
|
|
|
+}
|
|
|
+
|
|
|
+# 返回结果
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "agent_version": "langgraph_v1",
|
|
|
+ "classification_info": {
|
|
|
+ "confidence": 0.8799999999999999,
|
|
|
+ "method": "rule_based_medium_business",
|
|
|
+ "reason": "中等业务特征 - 业务实体: ['核心业务实体:服务区', '核心业务实体:档口', '经营品类:餐饮']"
|
|
|
+ },
|
|
|
+ "context_used": true,
|
|
|
+ "conversation_id": "conv_1751205549_308eb7c3",
|
|
|
+ "conversation_message": "继续已有对话",
|
|
|
+ "conversation_status": "existing",
|
|
|
+ "execution_path": [
|
|
|
+ "start",
|
|
|
+ "classify",
|
|
|
+ "agent_sql_generation",
|
|
|
+ "agent_sql_execution",
|
|
|
+ "format_response"
|
|
|
+ ],
|
|
|
+ "from_cache": false,
|
|
|
+ "is_guest_user": false,
|
|
|
+ "records": {
|
|
|
+ "columns": [
|
|
|
+ "餐饮档口数量"
|
|
|
+ ],
|
|
|
+ "is_limited": false,
|
|
|
+ "row_count": 1,
|
|
|
+ "rows": [
|
|
|
+ {
|
|
|
+ "餐饮档口数量": 6
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "total_row_count": 1
|
|
|
+ },
|
|
|
+ "response": "该服务区餐饮档口数量为6个。",
|
|
|
+ "routing_mode_source": "config",
|
|
|
+ "routing_mode_used": "hybrid",
|
|
|
+ "session_id": null,
|
|
|
+ "sql": "SELECT COUNT(*) AS 餐饮档口数量 FROM bss_branch b JOIN bss_service_area sa ON b.service_area_id = sa.id WHERE sa.service_area_name = '南城服务区' AND b.classify = '餐饮' AND b.delete_ts IS NULL;",
|
|
|
+ "summary": "该服务区餐饮档口数量为6个。",
|
|
|
+ "timestamp": "2025-06-29T22:09:25.494134",
|
|
|
+ "type": "DATABASE",
|
|
|
+ "user_id": "Paul"
|
|
|
+ },
|
|
|
+ "message": "操作成功",
|
|
|
+ "success": true
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+在debug的时候,可以注意上面的返回参数中"conversation_message"和 "conversation_status"在第一轮对话和第二轮对话中的值是不同的:
|
|
|
+
|
|
|
+第一轮对话:
|
|
|
+
|
|
|
+ `"conversation_message": "创建新对话",`
|
|
|
+ `"conversation_status": "new",`
|
|
|
+
|
|
|
+第二轮对话:
|
|
|
+
|
|
|
+ `"conversation_message": "继续已有对话",`
|
|
|
+ ` "conversation_status": "existing",`
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|