153 lines
5.0 KiB
Python
153 lines
5.0 KiB
Python
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', []) |