Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1238,15 +1238,56 @@ def multi_agent_chat_advanced(query: str, file=None) -> str:
|
|
| 1238 |
|
| 1239 |
|
| 1240 |
# LangGraph 使用的節點函數(會接續你的 Crew Agent)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1241 |
def router_run(state: dict) -> dict:
|
| 1242 |
query = state["query"]
|
| 1243 |
result = router_agent.kickoff(inputs={"query": query})
|
| 1244 |
return {"query": query, "route_result": result.output}
|
| 1245 |
|
| 1246 |
-
def general_run(state: dict) -> dict:
|
| 1247 |
-
result = general_agent.kickoff(inputs={"query": state["query"]})
|
| 1248 |
-
return {"query": state["query"], "final_answer": result.output}
|
| 1249 |
-
|
| 1250 |
def docqa_run(state: dict) -> dict:
|
| 1251 |
result = document_qa_agent.kickoff(inputs={"query": state["query"]})
|
| 1252 |
return {"query": state["query"], "final_answer": result.output}
|
|
@@ -1268,13 +1309,10 @@ def build_langgraph_gpt_like():
|
|
| 1268 |
|
| 1269 |
# Router 條件邏輯:你可以換成你自己的邏輯規則
|
| 1270 |
def decide_next(state):
|
| 1271 |
-
|
| 1272 |
-
|
| 1273 |
-
|
| 1274 |
-
|
| 1275 |
-
return "DocQA"
|
| 1276 |
-
else:
|
| 1277 |
-
return "General"
|
| 1278 |
|
| 1279 |
graph.add_conditional_edges("Router", decide_next, {
|
| 1280 |
"DocQA": "DocQA",
|
|
@@ -1289,28 +1327,43 @@ def build_langgraph_gpt_like():
|
|
| 1289 |
return graph.compile()
|
| 1290 |
|
| 1291 |
# 用於 Gradio Tab 6 的主入口函數
|
| 1292 |
-
def langgraph_tab6_main(query: str,
|
| 1293 |
try:
|
| 1294 |
-
|
| 1295 |
-
|
| 1296 |
-
|
| 1297 |
-
|
| 1298 |
-
|
| 1299 |
-
|
| 1300 |
-
|
| 1301 |
-
|
| 1302 |
-
|
| 1303 |
-
|
| 1304 |
-
|
| 1305 |
-
|
| 1306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1307 |
db = FAISS.from_documents(chunks, embeddings)
|
| 1308 |
retriever = db.as_retriever()
|
| 1309 |
|
| 1310 |
-
# 建構 LangGraph
|
| 1311 |
graph = build_langgraph_doc_qa_chain(llm=llm_gpt4, retriever=retriever, memory=None, prompt=custom_prompt)
|
| 1312 |
-
result = graph(
|
|
|
|
|
|
|
|
|
|
| 1313 |
return result.get("answer", "No answer generated.")
|
|
|
|
| 1314 |
except Exception as e:
|
| 1315 |
return f"[Tab6 Error] {e}"
|
| 1316 |
|
|
@@ -1459,7 +1512,10 @@ demo = gr.TabbedInterface(
|
|
| 1459 |
),
|
| 1460 |
gr.Interface(
|
| 1461 |
fn=langgraph_tab6_main,
|
| 1462 |
-
inputs=[
|
|
|
|
|
|
|
|
|
|
| 1463 |
outputs="text",
|
| 1464 |
title="LangGraph GPT-like QA (Tab6)",
|
| 1465 |
allow_flagging="never",
|
|
|
|
| 1238 |
|
| 1239 |
|
| 1240 |
# LangGraph 使用的節點函數(會接續你的 Crew Agent)
|
| 1241 |
+
from sentence_transformers import SentenceTransformer
|
| 1242 |
+
|
| 1243 |
+
embedding_model = SentenceTransformer("all-MiniLM-L6-v2")
|
| 1244 |
+
|
| 1245 |
+
# Intent label embeddings(你可以再自訂)
|
| 1246 |
+
INTENT_LABELS = {
|
| 1247 |
+
"DocQA": ["document", "file", "paper", "cb", "proposal"],
|
| 1248 |
+
"Summarise": ["summarise", "summary", "abstract", "key points", "要點", "總結", "摘要"],
|
| 1249 |
+
"General": ["who are you", "tell me something", "what can you do", "fun fact"],
|
| 1250 |
+
}
|
| 1251 |
+
|
| 1252 |
+
def detect_intent_embedding(query, file_names=[]):
|
| 1253 |
+
query_emb = embedding_model.encode(query, normalize_embeddings=True)
|
| 1254 |
+
|
| 1255 |
+
best_label = None
|
| 1256 |
+
best_score = -1
|
| 1257 |
+
|
| 1258 |
+
# 把上傳的檔名也當成一種 prompt
|
| 1259 |
+
all_phrases = INTENT_LABELS.copy()
|
| 1260 |
+
if file_names:
|
| 1261 |
+
all_phrases["DocQA"] += [name.lower() for name in file_names]
|
| 1262 |
+
|
| 1263 |
+
for label, examples in all_phrases.items():
|
| 1264 |
+
for example in examples:
|
| 1265 |
+
example_emb = embedding_model.encode(example, normalize_embeddings=True)
|
| 1266 |
+
score = float(query_emb @ example_emb.T)
|
| 1267 |
+
if score > best_score:
|
| 1268 |
+
best_score = score
|
| 1269 |
+
best_label = label
|
| 1270 |
+
return best_label if best_label else "General"
|
| 1271 |
+
|
| 1272 |
+
def decide_next(state):
|
| 1273 |
+
query = state.get("query", "")
|
| 1274 |
+
file_names = state.get("file_names", [])
|
| 1275 |
+
label = detect_intent_embedding(query, file_names)
|
| 1276 |
+
return label
|
| 1277 |
+
|
| 1278 |
+
# General → Search fallback handoff(當 general 回應不了)
|
| 1279 |
+
def general_run(state: dict) -> dict:
|
| 1280 |
+
result = general_agent.kickoff(inputs={"query": state["query"]})
|
| 1281 |
+
output = result.output.lower()
|
| 1282 |
+
if any(x in output for x in ["i don't know", "no idea", "not sure", "can't answer", "no info"]):
|
| 1283 |
+
result = search_agent.kickoff(inputs={"query": state["query"]})
|
| 1284 |
+
return {"query": state["query"], "final_answer": result.output}
|
| 1285 |
+
|
| 1286 |
def router_run(state: dict) -> dict:
|
| 1287 |
query = state["query"]
|
| 1288 |
result = router_agent.kickoff(inputs={"query": query})
|
| 1289 |
return {"query": query, "route_result": result.output}
|
| 1290 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1291 |
def docqa_run(state: dict) -> dict:
|
| 1292 |
result = document_qa_agent.kickoff(inputs={"query": state["query"]})
|
| 1293 |
return {"query": state["query"], "final_answer": result.output}
|
|
|
|
| 1309 |
|
| 1310 |
# Router 條件邏輯:你可以換成你自己的邏輯規則
|
| 1311 |
def decide_next(state):
|
| 1312 |
+
query = state.get("query", "")
|
| 1313 |
+
file_names = state.get("file_names", [])
|
| 1314 |
+
label = detect_intent_embedding(query, file_names)
|
| 1315 |
+
return label
|
|
|
|
|
|
|
|
|
|
| 1316 |
|
| 1317 |
graph.add_conditional_edges("Router", decide_next, {
|
| 1318 |
"DocQA": "DocQA",
|
|
|
|
| 1327 |
return graph.compile()
|
| 1328 |
|
| 1329 |
# 用於 Gradio Tab 6 的主入口函數
|
| 1330 |
+
def langgraph_tab6_main(query: str, files=None) -> str:
|
| 1331 |
try:
|
| 1332 |
+
if not files:
|
| 1333 |
+
return "Please upload at least one document file."
|
| 1334 |
+
|
| 1335 |
+
all_docs = []
|
| 1336 |
+
file_names = []
|
| 1337 |
+
|
| 1338 |
+
for file in files:
|
| 1339 |
+
file_path = get_file_path(file)
|
| 1340 |
+
if not file_path:
|
| 1341 |
+
continue
|
| 1342 |
+
file_names.append(os.path.basename(file_path))
|
| 1343 |
+
if file_path.lower().endswith(".pdf"):
|
| 1344 |
+
loader = PyPDFLoader(file_path)
|
| 1345 |
+
elif file_path.lower().endswith(".docx"):
|
| 1346 |
+
loader = UnstructuredWordDocumentLoader(file_path)
|
| 1347 |
+
else:
|
| 1348 |
+
loader = TextLoader(file_path)
|
| 1349 |
+
docs = loader.load()
|
| 1350 |
+
all_docs.extend(docs)
|
| 1351 |
+
|
| 1352 |
+
if not all_docs:
|
| 1353 |
+
return "No valid documents loaded."
|
| 1354 |
+
|
| 1355 |
+
chunks = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50).split_documents(all_docs)
|
| 1356 |
db = FAISS.from_documents(chunks, embeddings)
|
| 1357 |
retriever = db.as_retriever()
|
| 1358 |
|
| 1359 |
+
# 建構 LangGraph QA Chain
|
| 1360 |
graph = build_langgraph_doc_qa_chain(llm=llm_gpt4, retriever=retriever, memory=None, prompt=custom_prompt)
|
| 1361 |
+
result = graph.invoke({
|
| 1362 |
+
"query": query,
|
| 1363 |
+
"file_names": file_names # Pass all file names for intent detection
|
| 1364 |
+
})
|
| 1365 |
return result.get("answer", "No answer generated.")
|
| 1366 |
+
|
| 1367 |
except Exception as e:
|
| 1368 |
return f"[Tab6 Error] {e}"
|
| 1369 |
|
|
|
|
| 1512 |
),
|
| 1513 |
gr.Interface(
|
| 1514 |
fn=langgraph_tab6_main,
|
| 1515 |
+
inputs=[
|
| 1516 |
+
gr.Textbox(label="Ask anything"),
|
| 1517 |
+
gr.File(label="Upload one or more files", file_types=[".pdf", ".txt", ".docx"], file_count="multiple")
|
| 1518 |
+
],
|
| 1519 |
outputs="text",
|
| 1520 |
title="LangGraph GPT-like QA (Tab6)",
|
| 1521 |
allow_flagging="never",
|