效果图




分析流程


代码实现
废话少说,直接上代码
python
from langchain_core.language_models.llms import BaseLLM
from langchain_core.outputs import Generation, LLMResult
from pydantic.v1 import Field, validator
from typing import Any, Dict, List, Optional, AsyncIterator
import requests
import os
class DeepSeekLLM(BaseLLM):
api_key: str = Field(alias="api_key")
model: str = "deepseek-chat"
temperature: float = 0.7
max_tokens: int = 1000
# 必须实现的抽象方法
def _generate(
self,
prompts: List[str],
stop: Optional[List[str]] = None,
**kwargs: Any,
) -> LLMResult:
print("_generate:")
generations = []
for prompt in prompts:
response = self._call_api(prompt)
generations.append([Generation(text=response)])
return LLMResult(generations=generations)
async def _agenerate(
self,
prompts: List[str],
stop: Optional[List[str]] = None,
**kwargs: Any,
) -> LLMResult:
# 异步实现(可选)
return self._generate(prompts, stop, **kwargs)
def _call_api(self, prompt: str) -> str:
try:
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
payload = {
"messages": [{"role": "user", "content": prompt}],
"model": self.model,
"temperature": self.temperature,
"max_tokens": self.max_tokens
}
#将输入 输出都保存到文件中
import json
# 添加一个分隔符 没有 json.txt 就创建
with open("json.txt", "a", encoding="utf-8") as f:
json.dump(payload, f, ensure_ascii=False, indent=4)
response = requests.post(
"https://api.deepseek.com/v1/chat/completions",
headers=headers,
json=payload,
timeout=30
)
with open("json.txt", "a", encoding="utf-8") as f:
json.dump(response.json(), f, ensure_ascii=False, indent=4)
# 增加响应内容验证
if not response.text.strip():
raise ValueError("API返回空响应")
try:
data = response.json()
except json.JSONDecodeError:
# 尝试提取可能的JSON片段
import re
json_match = re.search(r'```json\n({.*?})\n```', response.text, re.DOTALL)
if json_match:
data = json.loads(json_match.group(1))
else:
raise ValueError(f"无法解析API响应: {response.text[:200]}...")
# 验证关键字段
if not data.get("choices") or not isinstance(data["choices"], list):
raise ValueError("API返回格式异常,缺少choices字段")
content = data["choices"][0]["message"]["content"]
# 清理响应内容(去除可能的Markdown标记)
if content.startswith("```json"):
content = content[7:-3].strip()
return content
except requests.exceptions.RequestException as e:
raise ValueError(f"API请求失败: {str(e)}")
except Exception as e:
raise ValueError(f"处理API响应时出错: {str(e)}")
@property
def _llm_type(self) -> str:
return "deepseek"
car_diagnosis_app.py 代码
python
import streamlit as st
from langchain.chains import SequentialChain, TransformChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate
from langchain.chains.llm import LLMChain
from deepseek_llm import DeepSeekLLM
import json
import datetime
import random
import os
# 初始化DeepSeek LLM
api_key = os.getenv("DEEPSEEK_API_KEY", st.secrets.get("DEEPSEEK_API_KEY", "your-api-key"))
llm = DeepSeekLLM(api_key=api_key, temperature=0.3, max_tokens=1500)
# 模拟车辆数据库
VEHICLE_DB = {
"京A12345": {
"brand": "Toyota",
"model": "Camry",
"year": 2020,
"mileage": 45000,
"last_service": "2023-12-15",
"insurance_expiry": "2024-09-30",
"next_maintenance": "2024-06-30"
},
"沪B67890": {
"brand": "Honda",
"model": "CR-V",
"year": 2019,
"mileage": 60000,
"last_service": "2023-11-20",
"insurance_expiry": "2024-08-15",
"next_maintenance": "2024-05-20"
}
}
# 模拟维修店数据库
REPAIR_SHOPS = [
{"id": 1, "name": "诚信汽修", "distance": "1.2km", "rating": 4.8,
"services": ["机油更换", "刹车维修", "发动机诊断"], "price_level": "$$"},
{"id": 2, "name": "途虎养车工场店", "distance": "2.3km", "rating": 4.7,
"services": ["轮胎更换", "保养套餐", "电子诊断"], "price_level": "$$$"},
{"id": 3, "name": "小李快修", "distance": "0.8km", "rating": 4.5,
"services": ["快速保养", "玻璃水加注", "简单维修"], "price_level": "$"}
]
# 模拟知识库音频
SOUND_LIBRARY = {
"哒哒声": "https://example.com/sounds/ticking.mp3",
"嗡嗡声": "https://example.com/sounds/humming.mp3",
"吱吱声": "https://example.com/sounds/squeaking.mp3",
"咔嗒声": "https://example.com/sounds/clicking.mp3"
}
# 1. 信息提取链
def extract_vehicle_info(inputs: dict) -> dict:
license_plate = inputs["license_plate"]
vehicle_info = VEHICLE_DB.get(license_plate, {})
if not vehicle_info:
st.error(f"未找到车牌号 {license_plate} 的车辆信息")
return {"vehicle_info": "未知车辆"}
# 检查服务提醒
today = datetime.date.today()
reminders = []
insurance_expiry = datetime.datetime.strptime(vehicle_info["insurance_expiry"], "%Y-%m-%d").date()
if (insurance_expiry - today).days < 30:
reminders.append({"type": "insurance", "message": "您的车辆保险即将到期"})
next_maintenance = datetime.datetime.strptime(vehicle_info["next_maintenance"], "%Y-%m-%d").date()
if (next_maintenance - today).days < 30:
reminders.append({"type": "maintenance", "message": "您的车辆即将需要保养"})
return {
"vehicle_info": json.dumps(vehicle_info),
"reminders": json.dumps(reminders)
}
info_extraction_chain = TransformChain(
input_variables=["license_plate"],
output_variables=["vehicle_info", "reminders"],
transform=extract_vehicle_info
)
# 2. 诊断链
def setup_diagnosis_chain():
diagnosis_template = """
您是一名专业的汽车维修技师,正在帮助车主诊断车辆问题。请根据以下信息进行诊断:
车辆信息:{vehicle_info}
用户描述的症状:{symptoms}
请根据您的专业知识:
1. 分析可能的故障原因
2. 生成最多3个关键问题来进一步明确问题
3. 对于异响类问题,请建议用户试听哪种声音样本
输出格式:
{{
"analysis": "对问题的初步分析",
"questions": ["问题1", "问题2", "问题3"],
"sound_suggestion": "建议试听的声音类型"
}}
"""
prompt = PromptTemplate(
template=diagnosis_template,
input_variables=["vehicle_info", "symptoms"]
)
return LLMChain(llm=llm, prompt=prompt, output_key="diagnosis_result")
# 3. 维修决策链
def setup_repair_decision_chain():
decision_template = """
基于以下诊断信息:
{diagnosis_result}
请判断:
1. 车主是否能够自行修复问题?(是/否)
2. 如果可自行修复,提供详细的步骤指导
3. 如果需要专业维修,推荐维修项目
输出格式:
{{
"self_repairable": true/false,
"repair_steps": ["步骤1", "步骤2", ...],
"recommended_services": ["服务1", "服务2", ...]
}}
"""
prompt = PromptTemplate(
template=decision_template,
input_variables=["diagnosis_result"]
)
return LLMChain(llm=llm, prompt=prompt, output_key="repair_decision")
# 4. 门店推荐链
def recommend_shops(inputs: dict) -> dict:
decision = json.loads(inputs["repair_decision"])
if decision.get("self_repairable", False):
return {"shop_recommendations": json.dumps([])}
# 根据位置和推荐服务筛选门店
location = inputs["location"]
recommended_services = decision.get("recommended_services", [])
filtered_shops = []
for shop in REPAIR_SHOPS:
# 简单匹配服务(实际应用中应有更复杂的匹配逻辑)
if any(service in shop["services"] for service in recommended_services):
shop["match_score"] = random.uniform(0.7, 1.0) # 模拟匹配度计算
filtered_shops.append(shop)
# 按距离和评分排序
filtered_shops.sort(key=lambda x: (x["distance"], -x["rating"]))
return {"shop_recommendations": json.dumps(filtered_shops[:3])}
shop_recommendation_chain = TransformChain(
input_variables=["repair_decision", "location"],
output_variables=["shop_recommendations"],
transform=recommend_shops
)
# 完整工作流
def create_full_workflow():
diagnosis_chain = setup_diagnosis_chain()
repair_decision_chain = setup_repair_decision_chain()
return SequentialChain(
chains=[
info_extraction_chain,
diagnosis_chain,
repair_decision_chain,
shop_recommendation_chain
],
input_variables=["license_plate", "symptoms", "location"],
output_variables=["vehicle_info", "reminders", "diagnosis_result",
"repair_decision", "shop_recommendations"],
verbose=True
)
# Streamlit UI
def main():
st.set_page_config(page_title="智能汽车故障诊断", layout="wide")
st.title("🚗 智能汽车故障诊断助手")
# 初始化session状态
if "diagnosis_stage" not in st.session_state:
st.session_state.diagnosis_stage = "initial"
st.session_state.memory = ConversationBufferMemory()
st.session_state.workflow = create_full_workflow()
st.session_state.answers = {}
st.session_state.current_questions = []
# 侧边栏 - 车辆信息输入
with st.sidebar:
st.header("车辆信息")
license_plate = st.text_input("车牌号", "京A12345")
location = st.text_input("当前位置", "北京市海淀区")
st.header("车辆状态")
if license_plate in VEHICLE_DB:
vehicle = VEHICLE_DB[license_plate]
st.write(f"品牌: {vehicle['brand']}")
st.write(f"型号: {vehicle['model']}")
st.write(f"里程: {vehicle['mileage']}公里")
st.write(f"上次保养: {vehicle['last_service']}")
# 服务提醒
today = datetime.date.today()
insurance_expiry = datetime.datetime.strptime(vehicle["insurance_expiry"], "%Y-%m-%d").date()
if (insurance_expiry - today).days < 30:
st.warning(f"⏰ 保险将于 {vehicle['insurance_expiry']} 到期")
next_maintenance = datetime.datetime.strptime(vehicle["next_maintenance"], "%Y-%m-%d").date()
if (next_maintenance - today).days < 30:
st.warning(f"🔧 下次保养时间: {vehicle['next_maintenance']}")
# 主界面 - 诊断流程
if st.session_state.diagnosis_stage == "initial":
st.subheader("请描述您的车辆问题")
symptoms = st.text_area("例如:冷启动时有哒哒异响,仪表盘机油灯闪烁", height=150)
if st.button("开始诊断"):
if not symptoms.strip():
st.error("请输入车辆问题描述")
return
st.session_state.symptoms = symptoms
st.session_state.license_plate = license_plate
st.session_state.location = location
st.session_state.diagnosis_stage = "processing"
st.rerun()
# 诊断处理中
elif st.session_state.diagnosis_stage == "processing":
with st.spinner("正在分析您的车辆问题..."):
try:
# 执行工作流
result = st.session_state.workflow({
"license_plate": st.session_state.license_plate,
"symptoms": st.session_state.symptoms,
"location": st.session_state.location
})
# 解析结果
diagnosis_result = json.loads(result["diagnosis_result"])
repair_decision = json.loads(result["repair_decision"])
shop_recommendations = json.loads(result["shop_recommendations"])
# 存储结果
st.session_state.diagnosis_result = diagnosis_result
st.session_state.repair_decision = repair_decision
st.session_state.shop_recommendations = shop_recommendations
st.session_state.current_questions = diagnosis_result.get("questions", [])
# 如果有声音建议,准备音频
sound_type = diagnosis_result.get("sound_suggestion")
if sound_type and sound_type in SOUND_LIBRARY:
st.session_state.sound_url = SOUND_LIBRARY[sound_type]
else:
st.session_state.sound_url = None
st.session_state.diagnosis_stage = "show_results"
st.rerun()
except Exception as e:
st.error(f"诊断过程中发生错误: {str(e)}")
st.session_state.diagnosis_stage = "initial"
# 显示诊断结果
elif st.session_state.diagnosis_stage == "show_results":
st.subheader("诊断结果")
# 显示初步分析
st.markdown(f"**问题分析:** {st.session_state.diagnosis_result.get('analysis', '')}")
# 播放声音建议
if st.session_state.sound_url:
st.markdown("**声音对比:** 请听以下声音是否与您的车辆声音相似")
st.audio(st.session_state.sound_url, format='audio/mp3')
# 显示进一步问题
if st.session_state.current_questions:
st.markdown("**请回答以下问题以进一步明确问题:**")
for i, question in enumerate(st.session_state.current_questions):
st.session_state.answers[i] = st.text_input(question, key=f"q_{i}")
if st.button("提交答案并继续诊断"):
# 将答案添加到症状描述中
new_symptoms = "\n".join([f"Q: {st.session_state.current_questions[i]}\nA: {st.session_state.answers[i]}"
for i in range(len(st.session_state.current_questions))])
st.session_state.symptoms += "\n" + new_symptoms
st.session_state.diagnosis_stage = "processing"
st.rerun()
# 显示维修决策
st.divider()
decision = st.session_state.repair_decision
if decision.get("self_repairable", False):
st.success("✅ 您可以尝试自行修复此问题")
st.markdown("**修复步骤:**")
for i, step in enumerate(decision.get("repair_steps", [])):
st.markdown(f"{i+1}. {step}")
# 添加AR指导按钮
if st.button("查看AR修复指导"):
st.session_state.show_ar = True
if st.session_state.get("show_ar", False):
st.video("https://example.com/ar_repair_guide.mp4")
else:
st.warning("⚠️ 建议到专业维修店处理此问题")
st.markdown(f"**推荐维修项目:** {', '.join(decision.get('recommended_services', []))}")
# 显示推荐门店
st.subheader("推荐维修店")
if st.session_state.shop_recommendations:
cols = st.columns(len(st.session_state.shop_recommendations))
for i, shop in enumerate(st.session_state.shop_recommendations):
with cols[i]:
st.markdown(f"**{shop['name']}**")
st.caption(f"距离: {shop['distance']} | 评分: {shop['rating']}")
st.caption(f"服务: {', '.join(shop['services'][:3])}")
st.caption(f"价格: {shop['price_level']}")
if st.button("选择此门店", key=f"shop_{i}"):
st.session_state.selected_shop = shop
st.session_state.diagnosis_stage = "shop_selected"
st.rerun()
else:
st.info("未找到匹配的维修店,请尝试扩大搜索范围")
# 返回按钮
if st.button("重新诊断"):
st.session_state.diagnosis_stage = "initial"
st.rerun()
# 门店选择后
elif st.session_state.diagnosis_stage == "shop_selected":
shop = st.session_state.selected_shop
st.success(f"您已选择: {shop['name']}")
# 显示预约信息
st.subheader("预约信息")
date = st.date_input("预约日期", min_value=datetime.date.today())
time = st.time_input("预约时间", datetime.time(10, 00))
contact = st.text_input("联系电话")
# 维修项目确认
st.subheader("维修项目确认")
services = st.session_state.repair_decision.get("recommended_services", [])
selected_services = st.multiselect("请确认维修项目", services, default=services)
if st.button("确认预约"):
st.session_state.appointment = {
"shop": shop["name"],
"date": date.strftime("%Y-%m-%d"),
"time": time.strftime("%H:%M"),
"services": selected_services,
"contact": contact
}
st.session_state.diagnosis_stage = "appointment_confirmed"
st.rerun()
# 预约确认
elif st.session_state.diagnosis_stage == "appointment_confirmed":
appt = st.session_state.appointment
st.balloons()
st.success("🎉 预约成功!")
st.markdown(f"""
**维修店:** {appt['shop']}
**时间:** {appt['date']} {appt['time']}
**维修项目:** {', '.join(appt['services'])}
**联系电话:** {appt['contact']}
""")
st.info("维修店将很快联系您确认预约详情")
if st.button("返回主页"):
st.session_state.diagnosis_stage = "initial"
st.rerun()
if __name__ == "__main__":
main()
执行脚本
python
#环境比变量
export DEEPSEEK_API_KEY=your_api_key
#安装依赖
pip install streamlit langchain requests
#运行应用
streamlit run car_diagnosis_app.py
#访问应用
http://localhost:8501