> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Hola-Dermat：护肤AI助手
- URL: https://www.hubwiz.com/blog/hola-dermat-skincare-ai-assistant/
- Published: 2026-02-08T10:06:27.000Z
- Updated: 2026-02-08T10:06:27.000Z
- Description: Hola-Dermat是一个个性化的护肤方案助手，它不只是向你推荐产品，而是真正理解你独特的皮肤需求、环境因素和偏好，为你量身定制早晚护肤流程。
- Author: admin
- Tags: AGENT, HOLA-DERMAT, #Import 2026-10-14 14:41

"找到合适的护肤产品就像大海捞针……只不过大海是整个互联网，而你甚至不确定针长什么样。"

如果你曾经在护肤品货架前站过，盯着一排排承诺能改变你皮肤的产品……你就知道这种挣扎。我需要透明质酸吗？视黄醇呢？这是SPF 30还是50？最重要的是，这对*我的*肤质、在*我的*气候下、配合*我的*生活方式真的有效吗？

这就是**Hola-Dermat**——一个个性化的护肤方案助手，它不只是向你推荐产品，而是真正理解你独特的皮肤需求、环境因素和偏好，为你量身定制早晚护肤流程。

但关键是：这不只是另一个聊天机器人。Hola-Dermat利用**Qdrant的ACORN算法**、**Perplexity网络搜索**和**CrewAI的智能体工作流**等前沿技术，解决了困扰推荐系统多年的问题：可怕的"零结果"问题。

出发！！

让我带你了解我们是如何构建这个系统的，为什么它很重要，以及这些技术如何协同工作创造出真正强大的东西……

## 1、问题：为什么护肤推荐系统是失败的

想象一下：你是一名住在印度海得拉巴的软件工程师。你的皮肤是混合干性，有点容易长痘。你每天花18小时盯着屏幕（是的，我看到你在点头……），很少晒太阳，你现在用的洗面奶让你的皮肤感觉像撒哈拉沙漠。

![](https://www.hubwiz.com/blog/content/images/2026/02/image-53.png)

哦，我的皮肤……

现在，试着找到满足以下条件的产品：

- 适合混合干性、易长痘的皮肤
- 在海得拉巴（或至少印度）有售
- 解决过度使用屏幕造成的蓝光伤害
- 提供保湿但不厚重
- 价格不要太高

传统的推荐系统要么：

1. 因为你的筛选条件"太具体"而返回零结果
2. 给你不考虑区域可用性的通用产品
3. 忽略紫外线指数、空气质量和湿度等环境因素
4. 忘记你以前尝试过什么，不断推荐同样的东西

英雄登场！

这就是**Hola-Dermat**介入的地方。它不只是一个产品搜索引擎，而是一个智能助手，理解上下文，从你的历史中学习，并适应你的环境。

## 2、解决方案：对话式AI meets 向量搜索

魔法始于简单的对话。用户不需要填写50个字段的表单（说实话，没人想填），而是可以……聊天。

```
用户："嗨，很高兴见到你。我的皮肤是混合型的，但稍微偏干，
      而且容易长痘。我来自南印度，住在特伦甘纳邦的海得拉巴。
      我是一名软件工程师，每天屏幕时间18小时，很少晒太阳。
      我现在只用普通洗面奶，一天一次，但它让我的皮肤干得要命。"

```

从这条消息中，Hola-Dermat提取出：

- 肤质：混合干性，易长痘
- 原籍地区：南印度
- 当前位置：特伦甘纳邦海得拉巴
- 职业：软件工程师
- 屏幕时间：每天18小时（高）
- 日晒：少
- 当前产品问题：洗面奶导致过度干燥

这种智能解析通过模式匹配和自然语言理解实现，允许系统构建全面的用户画像，而无需传统表单的摩擦。

LLM（我们使用的是Claude Sonnet 4.5）然后使用这个画像来：

- 理解用户的具体需求
- 只在必要时提出澄清问题
- 生成自然的、对话式的回复
- 提供个性化的推荐，而不是模板化的

## 3、为什么使用向量数据库？语义搜索革命

事情变得有趣了。传统数据库擅长精确匹配："找到所有SPF 50的产品"。但护肤是微妙的。用户可能会说"我需要适合干性敏感肌、帮助淡化黑斑的东西"，系统需要理解这意味着含有烟酰胺、α-熊果苷或维生素C等成分的产品。

好吧，这很令人困惑……

**向量数据库**通过将文本转换为捕获语义含义的高维向量（嵌入）来解决这个问题。具有相似成分、功效或用例的产品在这个向量空间中彼此接近。

在Hola-Dermat中，我们使用**Qdrant**——一个在AI社区中获得严重关注的开源向量数据库。以下是我们如何设置产品集合：

```python
def create_products_collection(client: QdrantClient) -> None:
    """使用ACORN配置创建产品集合。"""
    collection_name = settings.PRODUCTS_COLLECTION

    client.create_collection(
        collection_name=collection_name,
        vectors_config=VectorParams(
            size=EMBEDDING_DIM,  # sentence-transformers的384维
            distance=Distance.COSINE,
        ),
        optimizers_config=OptimizersConfigDiff(
            indexing_threshold=10000,
        ),
        hnsw_config=HnswConfigDiff(
            m=16,
            ef_construct=100,
        ),
    )

```

每个产品被转换为捕获其本质的嵌入：

```python
def create_product_text(product: dict) -> str:
    """从产品数据创建可搜索文本。"""
    text_parts = [
        product.get('name', ''),
        product.get('brand', ''),
        product.get('description', ''),
        product.get('product_type', ''),
        ', '.join(product.get('skin_type_compatibility', [])),
        ', '.join(product.get('key_benefits', [])),
        ', '.join(product.get('ingredients', [])),  # 成分作为列表
    ]
    return ' '.join(text_parts)
# 生成嵌入
embedding = get_embedding(product_text)  # 返回384维向量

```

当用户搜索"干性皮肤保湿的东西"时，系统：

- 将查询转换为向量
- 找到相似向量的产品（语义相似性）
- 按相关性排名- 返回最匹配的

但问题是……仅靠语义搜索还不够。你还需要**筛选**——这就是事情变得复杂的地方。

那可是个超级过滤器

## 4、零结果问题……当筛选成为你的敌人时 😈

想象一下，你正在搜索满足以下条件的产品：

- 适合混合干性皮肤
- 在印度有售
- 含有透明质酸
- 可在早上使用
- 价格低于50美元

在传统系统中，你会按顺序应用这些筛选器：

> 按肤质筛选 → 50个产品- 按地区筛选 → 20个产品- 按成分筛选 → 5个产品- 按使用方式筛选 → 2个产品- 按价格筛选 → **0个产品**

游戏结束。零结果。用户沮丧，你失去了他们。

我不想失去客户

这是因为传统的筛选使用**AND逻辑**——每个条件都必须满足。但在现实世界中，产品可能是：

- 非常适合你的肤质，但在不同地区有售（但仍可发货）
- 含有相似成分（肽而不是透明质酸，但功效相同）
- 稍微超出预算但值得额外花费

**Qdrant的ACORN算法**正好解决了这个问题。

## 5、ACORN：改变一切的算法

**ACORN**（复杂OR查询导航算法）是Qdrant对零结果问题的回答。在Qdrant v1.16中引入，ACORN通过理解何时放宽约束来智能处理复杂筛选。

以下是它在Hola-Dermat中的工作原理：

```python
def search_products(
    query_text: str,
    skin_type: Optional[str] = None,
    regions: Optional[List[str]] = None,
    usage: Optional[str] = None,
    required_ingredients: Optional[List[str]] = None,
    limit: int = 10
) -> List[Dict[str, Any]]:
    # 构建ACORN筛选条件
    filter_conditions = []

    if skin_type:
        filter_conditions.append(
            FieldCondition(
                key="skin_type_compatibility",
                match=MatchValue(value=skin_type)
            )
        )

    if regions:
        # 产品必须至少在其中一个地区有售（OR逻辑）
        filter_conditions.append(
            Filter(
                should=[  # "should"意味着至少一个必须匹配
                    FieldCondition(
                        key="regions_available",
                        match=MatchValue(value=region)
                    )
                    for region in regions
                ]
            )
        )

    if required_ingredients:
        # 至少一种成分必须匹配
        ingredient_conditions = [
            FieldCondition(
                key="ingredients",
                match=MatchValue(value=ingredient.lower().strip())
            )
            for ingredient in required_ingredients
        ]
        filter_conditions.append(
            Filter(should=ingredient_conditions)
        )

    # ACORN自动处理复杂筛选
    query_filter = Filter(must=filter_conditions) if filter_conditions else None

    # 执行混合搜索（语义 + 关键词）
    results = client.search(
        collection_name=collection_name,
        query_vector=query_embedding,
        query_filter=query_filter,  # ACORN智能处理
        limit=limit,
        score_threshold=0.3,
    )

```

ACORN的魔力在于它能够：

- **理解筛选器关系**：它知道"在印度或亚洲有售"比要求两者更灵活
- **智能放宽约束**：如果没有产品匹配所有筛选器，它可以放宽非关键条件
- **保持相关性**：即使放宽筛选器，也确保结果在语义上仍然相关
- **处理嵌套条件**：带有AND/OR逻辑的复杂筛选器不会破坏系统

在生产中，这意味着：

- **更高的转化率**：用户即使要求具体也能找到产品
- **更好的用户体验**：不再有令人沮丧的"无结果"页面
- **可扩展性**：即使数百万产品和复杂查询也能高效工作

## 6、混合搜索：语义 + 关键词 = 两全其美

等等，等等，等等！！

等等，还有更多！Hola-Dermat不仅仅依赖语义搜索。我们使用**混合搜索**——结合语义相似性和关键词匹配。

```python
# 语义搜索（理解含义）
results = client.search(
    collection_name=collection_name,
    query_vector=query_embedding,  # 基于向量
    query_filter=query_filter,
    limit=limit,
)
# 关键词搜索（精确匹配）
keyword_results = client.scroll(
    collection_name=collection_name,
    scroll_filter=Filter(
        must=[
            FieldCondition(
                key="text",
                match=MatchText(text=query_text)  # 基于文本
            )
        ] + filter_conditions
    ),
    limit=limit,
)
# 合并并去重
products = combine_results(results, keyword_results)

```

为什么两者都要？因为有时你需要精确匹配（如品牌名或特定成分名），有时你需要语义理解（如"干性皮肤的东西"匹配含有"补水"或"保湿"的产品）。

## 7、Perplexity：带来实时网络智能

Hola-Dermat变得非常智能的地方在这里。虽然Qdrant存储我们精心策划的产品数据库，但**Perplexity**带来实时网络智能。

### 7.1 为什么选择Perplexity？

- **最新信息**：天气数据、紫外线指数、空气质量——全部实时更新
- **区域产品可用性**：今天海得拉巴实际可售的产品可能不在我们的数据库中
- **最新评论和趋势**：新产品、重新配方、停产商品

以下是我们如何集成Perplexity：

```python
def research_weather_atmosphere(
    region: str,
    include_forecast: bool = True,
    days_back: int = 3,
    days_forward: int = 5
) -> Dict[str, Any]:
    """研究当前和预报的天气状况。"""
    query = f"""提供{region}的详细天气和大气状况：
    - 当前温度及过去{days_back}天的温度趋势
    - 当前紫外线指数及接下来{days_forward}天的紫外线预报
    - 当前空气质量指数（AQI）及空气质量趋势
    - 湿度水平
    - 任何影响皮肤健康的相关环境因素"""

    payload = {
        "model": "llama-3.1-sonar-large-128k-online",
        "messages": [
            {
                "role": "system",
                "content": "你是天气和环境数据专家……"
            },
            {
                "role": "user",
                "content": query
            }
        ],
        "temperature": 0.2,
        "max_tokens": 2000
    }

    response = httpx.post(PERPLEXITY_API_URL, headers=headers, json=payload)
    return response.json()

```

这些数据帮助Hola-Dermat理解：

- **防晒需求**：紫外线指数高？推荐更强的SPF
- **保湿需求**：湿度低？强调保湿产品
- **空气质量影响**：AQI高？建议屏障修复产品

### 7.2 动态产品发现

但是，等等！如果Qdrant没有匹配用户需求的产品，Perplexity会在网络上搜索区域产品，然后我们**自动将它们添加到Qdrant**：

```python
class QdrantAddProductTool(BaseTool):
    """用于向Qdrant数据库添加新产品的工具。"""

    def _run(self, product: Dict[str, Any]) -> str:
        """执行产品添加。"""
        success = add_product_to_collection(product)
        return f"成功将产品'{product.get('name')}'添加到数据库"

```

这创建了一个自我改进的系统，随着时间的推移变得更智能。

## 8、多集合架构：产品和历史

Qdrant的多集合支持非常适合Hola-Dermat。我们维护两个独立的集合：

### 8.1 产品集合

存储所有护肤产品及其嵌入、元数据和可搜索文本。

```python
payload = {
    'id': product['id'],
    'name': product['name'],
    'brand': product['brand'],
    'description': product['description'],
    'product_type': product['product_type'],
    'skin_type_compatibility': product['skin_type_compatibility'],
    'regions_available': product['regions_available'],
    'price_range': product['price_range'],
    'usage': product['usage'],
    'key_benefits': product['key_benefits'],
    'ingredients': product['ingredients'],  # 成分列表
    'text': product_text,  # 用于混合搜索
}

```

### 8.2 历史集合

跟踪用户交互、产品推荐、反馈和结果。

```python
def update_user_history(
    user_id: str,
    interaction_type: str,
    product_id: Optional[str] = None,
    product_name: Optional[str] = None,
    feedback: Optional[str] = None,
    results: Optional[str] = None,
    rating: Optional[int] = None
) -> bool:
    """在历史集合中存储用户交互。"""
    history_text = f"{interaction_type} {product_name} {feedback} {results}"
    embedding = get_embedding(history_text)

    payload = {
        "user_id": user_id,
        "timestamp": datetime.now().isoformat(),
        "interaction_type": interaction_type,
        "product_id": product_id,
        "product_name": product_name,
        "feedback": feedback,
        "results": results,
        "rating": rating,
        "text": history_text
    }

    client.upsert(
        collection_name=settings.HISTORY_COLLECTION,
        points=[PointStruct(id=point_id, vector=embedding, payload=payload)]
    )

```

### 8.3 为什么分开集合？

- **不同用例**：产品需要产品导向搜索；历史需要用户导向搜索
- **性能**：更小、专注的集合查询更快
- **可扩展性**：可以独立扩展每个集合
- **隐私**：用户历史可以有不同的保留策略

### 8.4 历史的力量

当用户返回时，Hola-Dermat搜索他们的历史：

```python
def search_user_history(
    user_id: str,
    query_text: Optional[str] = None,
    limit: int = 20
) -> List[Dict[str, Any]]:
    """检索用户的历史交互。"""
    filter_conditions = [
        FieldCondition(
            key="user_id",
            match=MatchValue(value=user_id)
        )
    ]

    if query_text:
        # 在用户历史中进行语义搜索
        query_embedding = get_embedding(query_text)
        results = client.search(
            collection_name=settings.HISTORY_COLLECTION,
            query_vector=query_embedding,
            query_filter=Filter(must=filter_conditions),
            limit=limit,
        )

```

这允许系统：

- 记住用户尝试过什么产品
- 理解什么有效什么无效\* 避免推荐他们已经拒绝的产品\* 随着时间的推移构建个性化的偏好画像

## 9、CrewAI：编排智能体工作流

现在，这就是一切汇集的地方。**CrewAI**编排整个工作流，智能决定何时使用哪个工具。

### 9.1 什么是CrewAI？

CrewAI是一个构建**智能体AI系统**的框架——能够：

- 使用多个工具
- 自主决策
- 协同解决复杂问题
- 从行动中学习

在Hola-Dermat中，我们有一个单一智能体（护肤顾问），可以访问多个工具：

```python
def create_skincare_agent() -> Agent:
    """创建主要的护肤专家智能体。"""
    return Agent(
        role='个性化护肤顾问',
        goal='理解用户需求并提供个性化推荐',
        backstory="""你是具有深入了解区域护肤实践、
        影响皮肤健康的环境因素、
        和产品配方的专家皮肤科医生……""",
        llm=llm,  # Claude Sonnet 4.5
        tools=[
            PerplexityWeatherTool(),        # 天气研究
            PerplexityProductResearchTool(), # 网络产品搜索
            PerplexitySkinAnalysisTool(),    # 环境分析
            QdrantProductSearchTool(),       # 产品数据库搜索
            QdrantHistorySearchTool(),       # 用户历史检索
            QdrantHistoryUpdateTool(),       # 历史更新
            QdrantAddProductTool()          # 添加新产品
        ]
    )

```

### 9.2 智能体工作流

当用户请求推荐时，智能体：

- **检查用户历史**："这个用户以前尝试过什么？"
- **研究天气**："海得拉巴的紫外线指数和空气质量如何？"
- **分析环境**："这些条件如何影响护肤需求？"
- **搜索产品**："找到匹配用户画像的产品"
- **如果没有结果**："搜索Perplexity寻找区域产品"
- **添加新产品**："将发现的产品存储到Qdrant"
- **生成推荐**："创建早晚护肤方案"
- **更新历史**："记录推荐了什么"

所有这些都是自主发生的。智能体决定：

- 何时使用Perplexity vs Qdrant
- 应用哪些筛选器
- 如何组合来自多个来源的信息
- 向用户提出什么问题

### 9.3 任务定义

以下是我们如何定义智能体的任务：

```python
task_description = f"""分析用户画像并创建个性化护肤推荐。
用户画像：
- 肤质：{profile_dict.get('skin_type')}
- 当前区域：{profile_dict.get('region_stay')}
- 职业：{profile_dict.get('occupation')}
- 屏幕时间：{profile_dict.get('screen_time')}
- 日晒：{profile_dict.get('sun_exposure')}
……
你的任务：
1. 检查用户历史以了解过去的产品使用情况
2. 研究{region}的天气状况
3. 分析区域因素如何影响皮肤需求
4. 搜索产品数据库寻找匹配产品
5. 如果没有找到产品，使用Perplexity研究区域产品
6. 将任何新产品添加到数据库
7. 创建全面的早晚护肤方案
8. 解释为什么选择每个产品
9. 静默更新用户历史（后台任务）
"""

```

智能体执行这个任务，沿途做出决策，并返回全面的推荐。

### 9.4 数据摄入：奠定基础

在所有这些魔法发生之前，我们需要用产品填充Qdrant。以下是我们的摄入流程：

```python
def ingest_products(products: list[dict], client) -> None:
    """将产品摄入Qdrant。"""
    collection_name = settings.PRODUCTS_COLLECTION
    points = []

    for product in products:
        # 创建可搜索文本
        product_text = create_product_text(product)

        # 使用sentence-transformers生成嵌入
        embedding = get_embedding(product_text)

        # 创建带元数据的点
        point = PointStruct(
            id=hash(product['id']) % (2**63),
            vector=embedding,
            payload={
                'id': product['id'],
                'name': product['name'],
                'brand': product['brand'],
                'description': product['description'],
                'product_type': product['product_type'],
                'skin_type_compatibility': product['skin_type_compatibility'],
                'regions_available': product['regions_available'],
                'price_range': product['price_range'],
                'usage': product['usage'],
                'key_benefits': product['key_benefits'],
                'ingredients': product['ingredients'],
                'text': product_text,  # 用于混合搜索
            }
        )
        points.append(point)

    # 批量upsert以提高效率
    client.upsert(
        collection_name=collection_name,
        points=points
    )

```

我们的产品数据结构如下：

**注意**：如果你没有任何产品也没关系，我们将在探索性网络搜索后搜索并添加新发现的产品。

```json
{
  "id": "prod_001",
  "name": "透明质酸保湿精华",
  "brand": "GlowSkin",
  "description": "含2%透明质酸的密集保湿精华……",
  "ingredients": ["hyaluronic acid", "peptides", "ceramides"],
  "product_type": "serum",
  "skin_type_compatibility": ["dry", "normal", "combination", "sensitive"],
  "regions_available": ["US", "EU", "Asia", "Global"],
  "price_range": "$$",
  "usage": "both",
  "key_benefits": ["Hydration", "Plumping", "Moisture retention"]
}

```

注意成分如何存储为**列表**而不是布尔标志。这使数据更灵活且可搜索。

## 10、Streamlit界面：让一切触手可及

所有这些强大的技术都包裹在一个干净、对话式的Streamlit界面中：

```python
def main():
    st.title("✨ Hola-Dermat")
    st.markdown("### 你的个性化护肤助手")

    # 初始化会话状态
    ChatManager.initialize_session_state()

    # 显示聊天历史
    for message in ChatManager.get_messages():
        with st.chat_message(message["role"]):
            st.write(message["content"])

    # 用户输入
    user_input = st.chat_input("在这里输入消息……")

    if user_input:
        # 智能画像提取
        ChatManager.extract_profile_info(user_input, profile)

        # 如果画像完整，触发CrewAI工作流
        if ChatManager.is_profile_collected():
            with st.spinner("正在分析你的画像……"):
                recommendations = generate_recommendations(profile, user_id)
                st.markdown("### 你的个性化护肤方案")
                st.markdown(recommendations['recommendations'])

```

界面处理：

- **智能解析**用户消息
- **渐进式画像构建**（只询问缺失的信息）
- **后台处理**（向用户隐藏智能体操作）
- **干净的输出**（过滤掉冗长的日志）

## 11、生产影响

让我们谈谈这在生产中意味着什么……

### 可扩展性

- **Qdrant的ACORN算法**高效处理数百万产品
- **多集合架构**允许独立扩展
- **混合搜索**确保即使复杂查询也能快速获得结果

### 用户体验

- **无零结果**：ACORN确保用户总能找到相关的东西
- **个性化**：历史集合构建长期用户画像
- **实时智能**：Perplexity保持推荐最新

### 业务价值

- **更高的转化率**：用户找到真正匹配他们需求的产品
- **减少流失**：个性化体验让用户参与
- **数据洞察**：历史集合提供有价值的用户行为数据

### 技术优势

- **自我改进**：新产品自动添加到数据库
- **灵活的筛选**：ACORN优雅地处理复杂查询
- **可维护**：产品和用户数据之间的清晰分离

## 12、结论：智能体AI系统的未来

Hola-Dermat代表的不仅仅是护肤助手——它是**智能体AI系统**如何解决各种领域复杂、个性化问题的蓝图。

### 关键要点

- **智能体AI**使系统能够自主决策、使用多个工具并适应用户需求
- **Qdrant的ACORN算法**解决了长期存在的零结果问题，使生产系统更可靠
- **向量数据库**实现传统数据库无法匹配的语义理解
- **混合搜索**结合了语义和关键词匹配的优点
- **多集合架构**允许可扩展、可维护的系统
- **实时网络智能**（通过Perplexity）保持推荐最新和相关
- **用户历史**创建长期个性化，随着时间的推移改进

### 大局观

驱动Hola-Dermat的技术——Qdrant、CrewAI、Perplexity和现代LLM——不仅仅是工具。它们是新一代AI系统的构建模块，这些系统：

- **理解上下文**而不是仅仅匹配关键词
- **从交互中学习**而不是静态的
- **做出智能决策**而不是遵循 rigid 规则
- **优雅地扩展**而不是在复杂性下崩溃

无论你是构建推荐系统、搜索引擎，还是任何需要理解用户意图并提供个性化结果的应用程序，这些模式都适用。

Qdrant的ACORN算法特别解决了困扰生产系统多年的问题。通过智能处理复杂筛选器并避免零结果场景，它使向量数据库真正为生产做好准备。

而CrewAI？它是将所有这些整合在一起的编排器——使智能体AI变得可访问、强大和实用。

所以下次你构建需要理解用户、智能搜索并随时间适应的东西时……记住Hola-Dermat。AI的未来不仅仅是关于更大的模型——而是关于无缝协同工作的更智能的系统。

*现在，如果你不介意的话……啊……我需要去更新我的护肤程序了。我的屏幕时间正在显现……* 😅

**想自己构建吗？**

查看[Hola-Dermat仓库](https://github.com/niranjanakella/hola-dermat?ref=hubwiz.com)并开始使用Qdrant、CrewAI和智能体AI系统进行实验。

---

原文链接：[Hola-Dermat: Personalized Skincare Agentic AI Assistant, Powered by Qdrant + Perplexity + CrewAI](https://niranjanakella.medium.com/hola-dermat-personalized-skincare-agentic-ai-assistant-powered-by-qdrant-perplexity-crewai-1c6ae2848bda?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处