首次提交by MimoCode

This commit is contained in:
tang1219
2026-06-15 14:50:15 +08:00
parent 584b31da50
commit ead13f863c
166 changed files with 14908 additions and 1 deletions
View File
+43
View File
@@ -0,0 +1,43 @@
from abc import ABC, abstractmethod
from typing import Optional
from pydantic import BaseModel
class VisionResult(BaseModel):
generic_name: Optional[str] = None
brand_name: Optional[str] = None
manufacturer: Optional[str] = None
specification: Optional[str] = None
class DateResult(BaseModel):
production_date: Optional[str] = None
expiry_date: Optional[str] = None
class LeafletResult(BaseModel):
indications: str
adult_dose: str
child_dose: Optional[str] = None
contraindications: str
notes: Optional[str] = None
class VisionProvider(ABC):
@abstractmethod
async def recognize_medicine(self, image_bytes: bytes) -> VisionResult:
pass
@abstractmethod
async def recognize_dates(self, image_bytes: bytes) -> DateResult:
pass
class TextProvider(ABC):
@abstractmethod
async def summarize_leaflet(self, text: str) -> LeafletResult:
pass
@abstractmethod
async def natural_language_search(self, query: str, medicines: list) -> list:
pass
+34
View File
@@ -0,0 +1,34 @@
from typing import Optional
from app.ai.base import VisionProvider, TextProvider
class AIManager:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if self._initialized:
return
self.vision_providers: dict[str, VisionProvider] = {}
self.text_providers: dict[str, TextProvider] = {}
self._initialized = True
def register_vision_provider(self, name: str, provider: VisionProvider):
self.vision_providers[name] = provider
def register_text_provider(self, name: str, provider: TextProvider):
self.text_providers[name] = provider
def get_vision_provider(self, name: str) -> Optional[VisionProvider]:
return self.vision_providers.get(name)
def get_text_provider(self, name: str) -> Optional[TextProvider]:
return self.text_providers.get(name)
ai_manager = AIManager()
+153
View File
@@ -0,0 +1,153 @@
import base64
import json
from openai import AsyncOpenAI
from app.ai.base import VisionProvider, TextProvider, VisionResult, DateResult, LeafletResult
from app.config import settings
class OpenAIVisionProvider(VisionProvider):
def __init__(self):
self.client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY)
self.model = settings.OPENAI_MODEL
async def recognize_medicine(self, image_bytes: bytes) -> VisionResult:
base64_image = base64.b64encode(image_bytes).decode('utf-8')
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": """请识别这张药品包装图片中的信息,返回JSON格式:
{
"generic_name": "通用名称",
"brand_name": "商品名称",
"manufacturer": "生产厂家",
"specification": "规格"
}
只提取图片中真实出现的内容,不要猜测。"""
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}"
}
}
]
}
],
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
return VisionResult(**result)
async def recognize_dates(self, image_bytes: bytes) -> DateResult:
base64_image = base64.b64encode(image_bytes).decode('utf-8')
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": """请识别这张图片中的日期信息,返回JSON格式:
{
"production_date": "生产日期(YYYY-MM-DD格式,如果无法识别则为null)",
"expiry_date": "有效期/过期日期(YYYY-MM-DD格式,如果无法识别则为null)"
}
只提取图片中真实出现的日期,不要猜测或推理。"""
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}"
}
}
]
}
],
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
return DateResult(**result)
class OpenAITextProvider(TextProvider):
def __init__(self):
self.client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY)
self.model = "gpt-4"
async def summarize_leaflet(self, text: str) -> LeafletResult:
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{
"role": "system",
"content": "你是一个医疗信息提取助手。请从药品说明书中提取关键信息。"
},
{
"role": "user",
"content": f"""请从以下药品说明书中提取关键信息,返回JSON格式:
{{
"indications": "适应症",
"adult_dose": "成人用法用量",
"child_dose": "儿童用法用量(如果没有则为null)",
"contraindications": "禁忌",
"notes": "注意事项(如果有)"
}}
说明书内容:
{text}"""
}
],
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
return LeafletResult(**result)
async def natural_language_search(self, query: str, medicines: list) -> list:
medicines_text = "\n".join([
f"- {m['name']}: {m.get('indications', '')}"
for m in medicines
])
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{
"role": "system",
"content": "你是一个药品搜索助手。根据用户描述的症状,从药品列表中找出可能适用的药品。"
},
{
"role": "user",
"content": f"""用户描述:{query}
可用药品列表:
{medicines_text}
请返回JSON格式的搜索结果:
{{
"results": [
{{
"medicine_id": 药品ID,
"name": "药品名称",
"reason": "匹配原因"
}}
]
}}"""
}
],
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
return result.get('results', [])