gewei20 commited on
Commit
67add1d
·
verified ·
1 Parent(s): 8773530

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -0
app.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ import threading
4
+ from functools import wraps
5
+ import google.generativeai as genai
6
+ from flask import Flask, request, jsonify, send_from_directory
7
+ from flask_cors import CORS
8
+ from dotenv import load_dotenv
9
+ import chromadb
10
+
11
+ # 从您的核心逻辑文件中导入类
12
+ from app_chromadb import MarkdownKnowledgeBase
13
+
14
+ # --- 初始化与配置 ---
15
+ load_dotenv()
16
+ app = Flask(__name__, static_folder='.', static_url_path='')
17
+ CORS(app)
18
+
19
+ # --- API 密钥认证配置 ---
20
+ VALID_API_KEYS_STR = os.environ.get("KNOWLEDGE_BASE_API_KEYS", "")
21
+ VALID_API_KEYS = {key.strip() for key in VALID_API_KEYS_STR.split(',') if key.strip()}
22
+ if not VALID_API_KEYS:
23
+ print("⚠️ 警告: 未配置 KNOWLEDGE_BASE_API_KEYS。API 将对所有人开放!")
24
+
25
+ # --- ChromaDB, Gemini, SiliconFlow 实例配置 ---
26
+ try:
27
+ CHROMA_DATA_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "chroma_db")
28
+ COLLECTION_NAME = "markdown_knowledge_base_m3"
29
+ chroma_client = chromadb.PersistentClient(path=CHROMA_DATA_PATH)
30
+ collection = chroma_client.get_or_create_collection(name=COLLECTION_NAME)
31
+ print(f"✅ ChromaDB 客户端已连接,数据存储在 '{CHROMA_DATA_PATH}'")
32
+ except Exception as e:
33
+ chroma_client = None
34
+ collection = None
35
+ print(f"❌ 初始化 ChromaDB 失败: {e}")
36
+
37
+ try:
38
+ GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
39
+ genai.configure(api_key=GEMINI_API_KEY)
40
+ gemini_model = genai.GenerativeModel('gemini-1.5-flash')
41
+ print("✅ Gemini API 已配置。")
42
+ except Exception as e:
43
+ gemini_model = None
44
+ print(f"❌ Gemini API 配置失败: {e}")
45
+
46
+ try:
47
+ SF_API_TOKEN = os.environ.get("SILICONFLOW_API_TOKEN")
48
+ kb_instance = MarkdownKnowledgeBase(api_token=SF_API_TOKEN, chroma_collection=collection)
49
+ print("✅ SiliconFlow 与知识库实例已配置。")
50
+ except Exception as e:
51
+ kb_instance = None
52
+ print(f"❌ 知识库实例配置失败: {e}")
53
+
54
+ kb_status = { "is_building": False }
55
+
56
+ # --- API 密钥认证装饰器 ---
57
+ def require_api_key(f):
58
+ @wraps(f)
59
+ def decorated_function(*args, **kwargs):
60
+ if not VALID_API_KEYS: return f(*args, **kwargs)
61
+ api_key = request.headers.get('X-API-Key')
62
+ if api_key and api_key in VALID_API_KEYS:
63
+ return f(*args, **kwargs)
64
+ else:
65
+ return jsonify({"error": "授权失败。请提供有效'X-API-Key'请求头。"}), 403
66
+ return decorated_function
67
+
68
+ # --- 前端页面路由 ---
69
+ @app.route('/')
70
+ def serve_index():
71
+ return send_from_directory('.', 'index.html')
72
+
73
+ # --- API 端点 ---
74
+ @app.route('/status', methods=['GET'])
75
+ def get_status():
76
+ if collection:
77
+ kb_status['total_items'] = collection.count()
78
+ kb_status['is_built'] = kb_status['total_items'] > 0
79
+ if not kb_status['is_building']:
80
+ kb_status['message'] = f"知识库已就绪,共有 {kb_status['total_items']} 个条目。"
81
+ else:
82
+ kb_status['message'] = "ChromaDB 未连接。"
83
+ return jsonify(kb_status)
84
+
85
+ @app.route('/build', methods=['POST'])
86
+ @require_api_key
87
+ def build_knowledge_base():
88
+ if kb_status['is_building']:
89
+ return jsonify({"error": "知识库已在构建中,请稍后。"}), 409
90
+
91
+ if not kb_instance:
92
+ return jsonify({"error": "知识库实例未初始化,无法构建。"}), 500
93
+
94
+ data = request.get_json()
95
+ clear_existing = data.get('clear_existing', False)
96
+
97
+ build_params = {
98
+ 'folder_path': data.get('folder_path'),
99
+ 'chunk_size': data.get('chunk_size', 4096),
100
+ 'overlap': data.get('overlap', 400),
101
+ 'max_files': data.get('max_files', 500),
102
+ 'sample_mode': data.get('sample_mode', 'largest')
103
+ }
104
+
105
+ def build_in_background():
106
+ global kb_status, collection
107
+ kb_status['is_building'] = True
108
+ kb_status['message'] = "构建任务开始..."
109
+ try:
110
+ if clear_existing and chroma_client:
111
+ print(f"正在清空现有集合: {COLLECTION_NAME}")
112
+ chroma_client.delete_collection(name=COLLECTION_NAME)
113
+ collection = chroma_client.get_or_create_collection(name=COLLECTION_NAME)
114
+ kb_instance.collection = collection
115
+ print("集合已清空并重建。")
116
+
117
+ kb_instance.build_knowledge_base(**build_params)
118
+ kb_status['message'] = f"构建完成!知识库现有 {collection.count()} 个条目。"
119
+ except Exception as e:
120
+ kb_status['message'] = f"构建时出错: {e}"
121
+ print(f"Error during build: {e}")
122
+ finally:
123
+ kb_status['is_building'] = False
124
+
125
+ thread = threading.Thread(target=build_in_background)
126
+ thread.start()
127
+ return jsonify({"message": "知识库构建任务已在后台启动。"}), 202
128
+
129
+ @app.route('/search', methods=['GET'])
130
+ @require_api_key
131
+ def search_in_kb():
132
+ if not (collection and collection.count() > 0):
133
+ return jsonify({"error": "知识库为空,请先构建。"}), 400
134
+
135
+ if not kb_instance:
136
+ return jsonify({"error": "知识库实例未初始化,无法搜索。"}), 500
137
+
138
+ query = request.args.get('query')
139
+ top_k = request.args.get('top_k', default=5, type=int)
140
+
141
+ if not query:
142
+ return jsonify({"error": "必须提供 'query' 参数"}), 400
143
+
144
+ try:
145
+ results = kb_instance.search(query, top_k=top_k)
146
+ return jsonify(results)
147
+ except Exception as e:
148
+ return jsonify({"error": f"搜索时发生错误: {e}"}), 500
149
+
150
+ @app.route('/summarize', methods=['POST'])
151
+ @require_api_key
152
+ def summarize_results():
153
+ if not gemini_model:
154
+ return jsonify({"error": "Gemini API 未配置或初始化失败。"}), 500
155
+
156
+ data = request.get_json()
157
+ query = data.get('query')
158
+ search_results = data.get('results')
159
+
160
+ if not query or not search_results:
161
+ return jsonify({"error": "必须提供查询和搜索结果。"}), 400
162
+
163
+ context = "\n\n---\n\n".join([item['content'] for item in search_results])
164
+ prompt = f"""
165
+ 根据以下本地知识库中搜索到的内容,请用清晰、简洁的中文直接回答用户的问题。
166
+ 如果内容不足以回答,请说明现有信息无法直接回答。
167
+
168
+ 用户问题: "{query}"
169
+
170
+ 搜索到的内容:
171
+ ---
172
+ {context}
173
+ ---
174
+
175
+ 你的回答:
176
+ """
177
+
178
+ try:
179
+ print(f"正在向 Gemini 模型 '{gemini_model.model_name}' 发送请求...")
180
+ response = gemini_model.generate_content(prompt)
181
+ summary = response.text
182
+ return jsonify({"summary": summary})
183
+ except Exception as e:
184
+ print(f"调用 Gemini API 时出错: {e}")
185
+ return jsonify({"error": f"调用 AI 服务时出错: {e}"}), 500
186
+
187
+
188
+ if __name__ == '__main__':
189
+ print("知识库后端服务 (最终版) 启动...")
190
+ print("✅ 服务已启动!请在浏览器中打开 http://127.0.0.1:5000")
191
+ # 使用生产级服务器时应移除 debug=False
192
+ app.run(host='0.0.0.0', port=5000, debug=False)