1 line
15 KiB
JSON
1 line
15 KiB
JSON
{"repo": "brkddd/lawagent2", "files": {"main.py": {"5ed78d8305568e99a4cdcfb97397749b4e42fa33": {"t": 1785674063.362636, "n": 2725, "h": "b2e2352bb1661261", "c": "\"\"\"\r\nSpoonOS 法律公正助手 - 主程序\r\n支持法律检索、回答与区块链存证。\r\n\"\"\"\r\nimport asyncio\r\nimport os\r\nfrom flask import Flask, request, jsonify\r\nfrom flask_cors import CORS\r\n\r\nfrom dotenv import load_dotenv\r\n\r\n# 1. 最先加载 .env,确保后续代码能读取环境变量\r\nload_dotenv()\r\napp = Flask(__name__)\r\nCORS(app) # 解决跨域问题\r\n\r\n# 2. 显式获取配置(必须在创建 LLM 之前)\r\napi_key = os.getenv(\"OPENAI_API_KEY\")\r\napi_base = os.getenv(\"OPENAI_BASE_URL\", \"https://api.deepseek.com\")\r\nmodel_name = os.getenv(\"MODEL_NAME\", \"deepseek-chat\")\r\n\r\nif not api_key:\r\n raise ValueError(\r\n \"请在 .env 中配置 OPENAI_API_KEY。当前未读取到有效的 API Key。\"\r\n )\r\n\r\n# 3. 显式初始化 DeepSeek LLM,使用 OpenAILike 避免模型名校验\r\nfrom llama_index.llms.openai_like import OpenAILike\r\n\r\nllm = OpenAILike(\r\n api_key=api_key,\r\n api_base=api_base,\r\n model=model_name,\r\n is_chat_model=True,\r\n)\r\n\r\n# 4. 配置全局 Settings\r\nfrom llama_index.core import Settings\r\nfrom llama_index.embeddings.huggingface import HuggingFaceEmbedding\r\n\r\nSettings.llm = llm\r\nSettings.embed_model = HuggingFaceEmbedding(model_name=\"BAAI/bge-small-zh-v1.5\")\r\n\r\n# 5. 引入技能(需在 Settings 配置之后)\r\nfrom llama_index.core.agent.workflow import ReActAgent\r\nfrom llama_index.core.agent.workflow import AgentStream\r\nfrom llama_index.core.memory import ChatMemoryBuffer\r\nfrom llama_index.core.workflow import Context\r\n\r\nfrom skills.legal_skill import search_laws\r\nfrom skills.notary_skill import notarize_on_chain\r\n\r\n# 6. 系统提示词:SpoonOS 法律公正助手\r\nSYSTEM_PROMPT = \"\"\"你是 SpoonOS 法律公正助手。\r\n\r\n工作流程要求:\r\n1. 先使用 search_laws 工具检索相关法律条文,再基于检索结果回答用户问题。\r\n2. 回答用户的法律相关问题。\r\n3. 如果用户明确要求对某段内容进行存证,则调用 notarize_on_chain 工具完成区块链存证。\r\n\"\"\"\r\n\r\n# 7. 构建 ReActAgent,显式传入 llm\r\nagent = ReActAgent(\r\n tools=[search_laws, notarize_on_chain],\r\n llm=llm,\r\n system_prompt=SYSTEM_PROMPT,\r\n verbose=True,\r\n)\r\n\r\n# 8. 创建上下文与记忆\r\nctx = Context(agent)\r\nmemory = ChatMemoryBuffer.from_defaults(llm=llm)\r\n\r\n\r\n# --- 3. API 路由 ---\r\n# --- 修改后的 API 路由 ---\r\n@app.route('/api/chat', methods=['POST'])\r\nasync def chat():\r\n user_input = request.json.get('message')\r\n if not user_input:\r\n return jsonify({\"error\": \"No message\"}), 400\r\n\r\n try:\r\n # 使用 agent.run 获取执行句柄\r\n handler = agent.run(user_input, memory=memory, ctx=ctx)\r\n \r\n # 这种方式会自动处理 ReAct 的思考过程,最终结果会直接包含在响应对象中\r\n response = await handler \r\n \r\n # response 对象通常包含最终的 content,过滤掉中间的推理步骤\r\n return jsonify({\"response\": str(response)})\r\n except Exception as e:\r\n return jsonify({\"error\": str(e)}), 500\r\n# 原有的上传逻辑可保留在这里...\r\n@app.route('/api/upload-evidence', methods=['POST'])\r\ndef upload_evidence():\r\n # ... 你之前的 Flask 存证逻辑 ...\r\n return jsonify({\"success\": True, \"tx_hash\": \"0x...\", \"evidence_hash\": \"...\"})\r\n\r\nif __name__ == \"__main__\":\r\n app.run(port=5000, debug=True)"}, "7df29157692e4f7f5bc1e09e626e53b87128d311": {"t": 1785766531.9956021, "n": 2725, "h": "b2e2352bb1661261", "c": "\"\"\"\r\nSpoonOS 法律公正助手 - 主程序\r\n支持法律检索、回答与区块链存证。\r\n\"\"\"\r\nimport asyncio\r\nimport os\r\nfrom flask import Flask, request, jsonify\r\nfrom flask_cors import CORS\r\n\r\nfrom dotenv import load_dotenv\r\n\r\n# 1. 最先加载 .env,确保后续代码能读取环境变量\r\nload_dotenv()\r\napp = Flask(__name__)\r\nCORS(app) # 解决跨域问题\r\n\r\n# 2. 显式获取配置(必须在创建 LLM 之前)\r\napi_key = os.getenv(\"OPENAI_API_KEY\")\r\napi_base = os.getenv(\"OPENAI_BASE_URL\", \"https://api.deepseek.com\")\r\nmodel_name = os.getenv(\"MODEL_NAME\", \"deepseek-chat\")\r\n\r\nif not api_key:\r\n raise ValueError(\r\n \"请在 .env 中配置 OPENAI_API_KEY。当前未读取到有效的 API Key。\"\r\n )\r\n\r\n# 3. 显式初始化 DeepSeek LLM,使用 OpenAILike 避免模型名校验\r\nfrom llama_index.llms.openai_like import OpenAILike\r\n\r\nllm = OpenAILike(\r\n api_key=api_key,\r\n api_base=api_base,\r\n model=model_name,\r\n is_chat_model=True,\r\n)\r\n\r\n# 4. 配置全局 Settings\r\nfrom llama_index.core import Settings\r\nfrom llama_index.embeddings.huggingface import HuggingFaceEmbedding\r\n\r\nSettings.llm = llm\r\nSettings.embed_model = HuggingFaceEmbedding(model_name=\"BAAI/bge-small-zh-v1.5\")\r\n\r\n# 5. 引入技能(需在 Settings 配置之后)\r\nfrom llama_index.core.agent.workflow import ReActAgent\r\nfrom llama_index.core.agent.workflow import AgentStream\r\nfrom llama_index.core.memory import ChatMemoryBuffer\r\nfrom llama_index.core.workflow import Context\r\n\r\nfrom skills.legal_skill import search_laws\r\nfrom skills.notary_skill import notarize_on_chain\r\n\r\n# 6. 系统提示词:SpoonOS 法律公正助手\r\nSYSTEM_PROMPT = \"\"\"你是 SpoonOS 法律公正助手。\r\n\r\n工作流程要求:\r\n1. 先使用 search_laws 工具检索相关法律条文,再基于检索结果回答用户问题。\r\n2. 回答用户的法律相关问题。\r\n3. 如果用户明确要求对某段内容进行存证,则调用 notarize_on_chain 工具完成区块链存证。\r\n\"\"\"\r\n\r\n# 7. 构建 ReActAgent,显式传入 llm\r\nagent = ReActAgent(\r\n tools=[search_laws, notarize_on_chain],\r\n llm=llm,\r\n system_prompt=SYSTEM_PROMPT,\r\n verbose=True,\r\n)\r\n\r\n# 8. 创建上下文与记忆\r\nctx = Context(agent)\r\nmemory = ChatMemoryBuffer.from_defaults(llm=llm)\r\n\r\n\r\n# --- 3. API 路由 ---\r\n# --- 修改后的 API 路由 ---\r\n@app.route('/api/chat', methods=['POST'])\r\nasync def chat():\r\n user_input = request.json.get('message')\r\n if not user_input:\r\n return jsonify({\"error\": \"No message\"}), 400\r\n\r\n try:\r\n # 使用 agent.run 获取执行句柄\r\n handler = agent.run(user_input, memory=memory, ctx=ctx)\r\n \r\n # 这种方式会自动处理 ReAct 的思考过程,最终结果会直接包含在响应对象中\r\n response = await handler \r\n \r\n # response 对象通常包含最终的 content,过滤掉中间的推理步骤\r\n return jsonify({\"response\": str(response)})\r\n except Exception as e:\r\n return jsonify({\"error\": str(e)}), 500\r\n# 原有的上传逻辑可保留在这里...\r\n@app.route('/api/upload-evidence', methods=['POST'])\r\ndef upload_evidence():\r\n # ... 你之前的 Flask 存证逻辑 ...\r\n return jsonify({\"success\": True, \"tx_hash\": \"0x...\", \"evidence_hash\": \"...\"})\r\n\r\nif __name__ == \"__main__\":\r\n app.run(port=5000, debug=True)"}}, "app.py": {"5ed78d8305568e99a4cdcfb97397749b4e42fa33": {"t": 1785674064.3075614, "n": 6709, "h": "92a5ecad9374af53", "c": "\"\"\"\r\nSpoonOS 法律公正助手 + 区块链证据存证 后端\r\n\"\"\"\r\nimport os\r\nimport json\r\nimport asyncio\r\nfrom flask import Flask, request, jsonify\r\nfrom flask_cors import CORS\r\nfrom dotenv import load_dotenv\r\n\r\n# ================== 1. 环境变量 & Flask ==================\r\nload_dotenv()\r\n\r\napp = Flask(__name__)\r\nCORS(app)\r\n\r\n# ================== 2. 区块链证据上传工具(spoon_ai) ==================\r\nupload_tool = None\r\n\r\ndef init_upload_agent():\r\n global upload_tool\r\n try:\r\n from examples.user_evidence_upload_agent import UserEvidenceUploadTool\r\n upload_tool = UserEvidenceUploadTool()\r\n print(\"✅ 证据上传工具初始化成功\")\r\n return True\r\n except Exception as e:\r\n print(f\"❌ 证据上传工具初始化失败: {e}\")\r\n return False\r\n\r\n\r\n@app.route('/api/upload-evidence', methods=['POST'])\r\ndef upload_evidence():\r\n \"\"\"上传证据并上链\"\"\"\r\n try:\r\n if upload_tool is None:\r\n return jsonify({'error': '证据上传工具未初始化'}), 500\r\n\r\n # 获取上传的文件\r\n if 'evidence_file' not in request.files:\r\n return jsonify({'error': '没有提供文件'}), 400\r\n\r\n file = request.files['evidence_file']\r\n if file.filename == '':\r\n return jsonify({'error': '没有选择文件'}), 400\r\n\r\n # 获取表单数据\r\n evidence_type = request.form.get('evidence_type', 'document')\r\n # 前端历史值兼容:text -> document\r\n if evidence_type == 'text':\r\n evidence_type = 'document'\r\n evidence_source = request.form.get('evidence_source', 'user_upload')\r\n user_address = request.form.get('user_address', '')\r\n description = request.form.get('description', '')\r\n\r\n # 读取文件内容\r\n file_content = file.read()\r\n if isinstance(file_content, bytes):\r\n try:\r\n file_content = file_content.decode('utf-8')\r\n except UnicodeDecodeError:\r\n # 如果不是文本文件,转换为 base64\r\n import base64\r\n file_content = base64.b64encode(file_content).decode('utf-8')\r\n evidence_type = 'binary'\r\n\r\n # 运行工具(异步)\r\n loop = asyncio.new_event_loop()\r\n asyncio.set_event_loop(loop)\r\n\r\n try:\r\n # sign_with 参数目前仅用于接口兼容;真实上链使用 .env 的 PRIVATE_KEY\r\n sign_with = user_address or \"local\"\r\n result = loop.run_until_complete(\r\n upload_tool.execute(\r\n evidence_content=file_content,\r\n evidence_type=evidence_type,\r\n source=evidence_source,\r\n sign_with=sign_with,\r\n description=description,\r\n uploader_address=user_address,\r\n file_name=file.filename,\r\n metadata={\r\n \"content_encoding\": \"utf-8\" if evidence_type != \"binary\" else \"base64\",\r\n },\r\n )\r\n )\r\n finally:\r\n loop.close()\r\n\r\n # 解析结果\r\n if isinstance(result, str):\r\n # 尝试解析 JSON 结果\r\n try:\r\n parsed_result = json.loads(result)\r\n # 兼容前端:统一输出 success/tx_hash/evidence_hash 等字段\r\n status = parsed_result.get(\"status\")\r\n success = status == \"success\"\r\n\r\n onchain = parsed_result.get(\"onchain\")\r\n onchain_dict = onchain if isinstance(onchain, dict) else {}\r\n\r\n response_body = {\r\n \"success\": bool(success),\r\n \"status\": status,\r\n \"message\": parsed_result.get(\"message\") or (\"OK\" if success else \"FAILED\"),\r\n \"error\": parsed_result.get(\"error\"),\r\n # 扁平化常用字段,便于前端展示\r\n \"tx_hash\": onchain_dict.get(\"tx_hash\"),\r\n \"evidence_hash\": onchain_dict.get(\"evidence_hash\"),\r\n \"signature\": onchain_dict.get(\"signature\"),\r\n \"timestamp\": onchain_dict.get(\"timestamp\"),\r\n \"explorer_url\": onchain_dict.get(\"explorer\"),\r\n # 保留完整细节\r\n \"result\": parsed_result,\r\n \"onchain\": onchain,\r\n }\r\n return jsonify(response_body)\r\n except json.JSONDecodeError:\r\n return jsonify({\r\n 'success': False,\r\n 'error': 'invalid_json',\r\n 'message': result,\r\n 'raw_response': result,\r\n })\r\n else:\r\n return jsonify({\r\n 'success': False,\r\n 'error': 'invalid_result_type',\r\n 'result': str(result)\r\n })\r\n\r\n except Exception as e:\r\n print(f\"上传失败: {e}\")\r\n return jsonify({'error': str(e)}), 500\r\n\r\n\r\n@app.route('/api/health', methods=['GET'])\r\ndef health():\r\n return jsonify({\r\n \"status\": \"ok\",\r\n \"upload_tool_ready\": upload_tool is not None\r\n })\r\n\r\n\r\n# ================== 3. LLM / Agent(法律助手) ==================\r\nfrom llama_index.llms.openai_like import OpenAILike\r\nfrom llama_index.core import Settings\r\nfrom llama_index.embeddings.huggingface import HuggingFaceEmbedding\r\nfrom llama_index.core.agent.workflow import ReActAgent\r\nfrom llama_index.core.memory import ChatMemoryBuffer\r\nfrom llama_index.core.workflow import Context\r\n\r\nfrom skills.legal_skill import search_laws\r\nfrom skills.notary_skill import notarize_on_chain\r\n\r\napi_key = os.getenv(\"OPENAI_API_KEY\")\r\napi_base = os.getenv(\"OPENAI_BASE_URL\", \"https://api.deepseek.com\")\r\nmodel_name = os.getenv(\"MODEL_NAME\", \"deepseek-chat\")\r\n\r\nllm = OpenAILike(\r\n api_key=api_key,\r\n api_base=api_base,\r\n model=model_name,\r\n is_chat_model=True,\r\n)\r\n\r\nSettings.llm = llm\r\nSettings.embed_model = HuggingFaceEmbedding(model_name=\"BAAI/bge-small-zh-v1.5\")\r\n\r\nSYSTEM_PROMPT = \"\"\"\r\n你是 SpoonOS 法律公正助手。\r\n\r\n流程:\r\n1. 使用 search_laws 检索法律条文\r\n2. 基于结果回答\r\n3. 若用户要求存证,调用 notarize_on_chain\r\n\"\"\"\r\n\r\nagent = ReActAgent(\r\n tools=[search_laws, notarize_on_chain],\r\n llm=llm,\r\n system_prompt=SYSTEM_PROMPT,\r\n verbose=True,\r\n)\r\n\r\nctx = Context(agent)\r\nmemory = ChatMemoryBuffer.from_defaults(llm=llm)\r\n\r\n\r\n@app.route('/api/chat', methods=['POST'])\r\nasync def chat():\r\n user_input = request.json.get(\"message\")\r\n if not user_input:\r\n return jsonify({\"error\": \"message 不能为空\"}), 400\r\n\r\n try:\r\n handler = agent.run(user_input, memory=memory, ctx=ctx)\r\n response = await handler\r\n return jsonify({\"response\": str(response)})\r\n except Exception as e:\r\n return jsonify({\"error\": str(e)}), 500\r\n\r\n\r\n# ================== 4. 启动入口 ==================\r\nif __name__ == \"__main__\":\r\n init_upload_agent()\r\n print(\"🚀 SpoonOS 法律公正助手后端启动中...\")\r\n app.run(host=\"0.0.0.0\", port=5000, debug=True)\r\n"}}}} |