惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News | PayPal Newsroom
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
SecWiki News
SecWiki News
Know Your Adversary
Know Your Adversary
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
NISL@THU
NISL@THU
WordPress大学
WordPress大学
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Threat Research - Cisco Blogs
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
Security Archives - TechRepublic
Security Archives - TechRepublic
有赞技术团队
有赞技术团队
L
LINUX DO - 热门话题
Hacker News: Ask HN
Hacker News: Ask HN
V
V2EX
G
GRAHAM CLULEY
TaoSecurity Blog
TaoSecurity Blog
Hugging Face - Blog
Hugging Face - Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
F
Fortinet All Blogs
博客园 - 叶小钗
博客园 - 三生石上(FineUI控件)
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
Latest news
Latest news
The Hacker News
The Hacker News
aimingoo的专栏
aimingoo的专栏
T
Troy Hunt's Blog
S
Schneier on Security
I
Intezer
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
Apple Machine Learning Research
Apple Machine Learning Research
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threatpost
爱范儿
爱范儿
The Register - Security
The Register - Security
S
SegmentFault 最新的问题
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
宝玉的分享
宝玉的分享
Recent Commits to openclaw:main
Recent Commits to openclaw:main
美团技术团队
B
Blog RSS Feed

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12) Snack JSONPath 项目架构分析 Claude Code Buddy 小析:一个非核心功能,如何体现产品的细节完成度 AI新时代下的图床管理方案-Cloudflare图床+MCP+Skills方案指南 化繁为简:顺丰速运App如何通过 HarmonyOS SDK实现专业级空间测量 从零实现富文本编辑器#13-React非编辑节点的内容渲染 AI开发-python-langchain框架(3-23-OpenAI Functions风格Tool Calling智能助手) .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut PbootCMS 网站内容数量多导致访问慢?这些实用优化方案帮你提速! - 家兴网络技术工作室 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 网站漏洞怎么发现并修复?一篇实用指南(附完整流程) - 家兴网络技术工作室 开了 TUN 模式还是直连?90% 的人都踩过这个坑 Github日报|2026年04月12日 - AI一族 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术 Etsy 把 1000 个 MySQL 分片迁进 Vitess:425TB 数据背后的真正问题不是性能,而是运维规模 MicroPython LVGL基础知识和概念:底层渲染与性能优化 - FreakStudio 数据库草图算法 Python 潮流周刊#146:CPython 引入 Rust 的进展 - 豌豆花下猫 最小生成树 - mofei1116 红日靶场七:从外网入口、容器逃逸到 AD 接管的完整利用链复盘 - YouDiscovered1t 分享四款开源且实用的 Kafka 管理工具 - 追逐时光者 vLLM 权重加载机制全解析:从挑战到理想架构 LCT 学习笔记 - ACehomoxue Avalonia UI 12.0.0 正式发布:架构演进和性能飞跃 - 张善友 当 AI Agent 把调用链拉长,延迟开始成为一门生意 conhost.exe 无法显示 U+2717 - 145a 太秀了,我把自己蒸馏成了 Skill!已开源 - 程序员鱼皮 ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑 基于 Ghostty 带有分割标签页和为 Claude 编程设计的通知终端 - BugShare AI 焊死入口:教育的“操作系统级”重塑 - 郝hai 初级Java开发工程师使用sql脚本编写代码的过程是简单而且不糊涂 - CoderOilStation Claude Code通关手册(六):MCP协议完全指南 - 暮色之狐 边框灯光环绕动画特效实现指南 - Newbe36524 开源:子木蒸馏版的 SEO 审计工具 seo-audit-skill v1.0 我所理解的Python元模型 【从0到1构建一个ClaudeAgent】规划与协调-TodoWrite - 程序员Seven Claude 和 Codex 在审计 Skill 上性能差异探究 - ACai_sec AScript如何实现中文脚本引擎 - rockey627 【渗透测试】HTB Season10 Garfield 全过程wp - dynasty_chenzi Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革 树状数组正确性证明 - AC-wyr 你的 AI 焦虑,可能比 AI 本身更危险——ATM 机没有消灭银行柜员,但恐慌消灭了你的判断力 - 我没有三颗心脏 一个拉胯的分库分表方案有多绝望?整个部门都在救火! - 冰河团队 动态规划入门必学之走方格问题 - Ofnoname PostgREST 与 PostgreSQL 角色权限配置全解析(生产级实践) - SheepDog1998 使用 UEFI 图形输出协议 GOP 在屏幕上显示图像的方法 - 阿源- Claude Code通关手册(五):组建你的AI专家团队,子代理系统 - 暮色之狐 一个程序员到架构师的催婚路之感悟(整整10年后的催婚相亲感悟) - MisterLip 用 Agent Skill 自动生成工作周报 - 赵康
张高兴的 Hailo-10 开发指南:(二)使用 LangChain 搭建本地大模型 RAG 问答应用
张高兴 · 2026-06-07 · via 博客园_首页

上一篇博客介绍了在 Hailo-10 上实现语音识别应用,这次我们来聊聊它的另一个重要功能:本地大模型推理。当然如果只是做离线对话的话,应用场景可能比较有限,下面结合 LangChain 实现一个简单的 RAG 应用。设备仍然使用之前介绍的 ASUS UGen300。

1

环境配置

与音频模型只能在 Linux 上运行不同,本地大模型推理可以在 Windows 上实现。这里是在安装了 Raspberry Pi OS 的 Raspberry Pi 5 上实现的。

安装 HailoRT

首先要安装 HailoRT,这是 Hailo 的运行时库,包含设备驱动、Python 绑定、命令行工具。可以去 Hailo Developer Zone 注册账号下载,也可以在 ASUS 的官网下载。详细步骤见上一篇博客,张高兴的 Hailo-10 开发指南:(一)实现离线语音识别

安装 Hailo-Ollama

Hailo-Ollama 是 Hailo 官方提供的推理服务,API 和 Ollama 完全兼容,意味着能用 Ollama 的工具(LangChain、Open-WebUI 等)基本都能直接使用。在安装完 ASUS 的驱动捆绑包之后,Hailo-Ollama 也已经安装完成。如果你的 Hailo-10 是 M.2 版本的,可能需要单独安装 Hailo-Ollama,在 Hailo Developer Zone 中下载安装 Hailo Model Zoo GenAI

2

Python 环境

pip install pdfplumber langchain langchain-ollama langchain-text-splitters \
            langchain-chroma langchain-huggingface sentence-transformers \
            chromadb requests

启动 Hailo-Ollama 服务

服务启动很简单,直接在命令行中运行下面的命令,默认监听 0.0.0.0:8000

hailo-ollama

下面看看 Hailo-10 目前支持哪些模型:

curl --silent http://localhost:8000/hailo/v1/list

可以看到目前支持 deepseek_r1:1.5bllama3.2:1bqwen2.5-coder:1.5bqwen2.5:1.5bqwen2:1.5bqwen3:1.7b 这些模型。执行下面的命令拉取 qwen3:1.7b 模型:

curl --silent http://localhost:8000/api/pull \
     -H 'Content-Type: application/json' \
     -d '{ "model": "qwen3:1.7b", "stream" : true }'

拉取完成后,验证一下模型是否能正常推理:

curl --silent http://localhost:8000/api/chat \
     -H 'Content-Type: application/json' \
     -d '{"model": "qwen3:1.7b", "messages": [{"role": "user", "content": "用一句话解释什么是向量数据库"}]}'

到这里,整个链路就通了。接下来你可以通过调用接口的方式在自己的应用里使用这个模型了,也可以使用支持 Ollama 的工具,比如 Open-WebUI 来和模型进行交互。比如拉取 Open-WebUI 的 Docker 镜像:

docker run -d --net=host -e OLLAMA_BASE_URL=http://127.0.0.1:8000 -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

3

实现 RAG 应用

模型本身不会主动知道你公司内部文档里写了什么。RAG 是把文档切碎存进向量数据库,提问时先搜索最相关的段落,再把这些段落塞进 prompt 让模型回答。通过 RAG 模型可以理解一些最新的、专业的知识,而不需要模型本身学会这些知识。比如这里使用的是 ASUS UGen300 的用户手册,模型本身不可能学会里面的内容,但通过 RAG 的方式,模型就能理解用户手册里的内容了。当然你也可以替换成其他的文档完成后面的操作。

4

核心流程很简单:文档处理 → 向量化 → RAG 链路 → 问答。下面新建 rag_demo.py 文件.

1. 引用相关包

import json
import os
import requests
from typing import Any, Iterator, List, Optional
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import (
    AIMessage,
    AIMessageChunk,
    BaseMessage,
    HumanMessage,
    SystemMessage,
)
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma
import pdfplumber

2. PDF 文档处理

pdfplumber 逐页解析 PDF,构造 LangChain Document 对象。

PDF_FILES = [
    "data/UGen300-manual.pdf",
]
all_docs = []

for pdf_path in PDF_FILES:
     if not os.path.exists(pdf_path):
          print(f"  跳过(不存在):{pdf_path}")
          continue
     count = 0
     with pdfplumber.open(pdf_path) as pdf:
          for i, page in enumerate(pdf.pages):
               text = (page.extract_text() or "").strip()
               all_docs.append(Document(
                    page_content=text,
                    metadata={"source": pdf_path, "page": i + 1},
               ))
               count += 1

print(f"共加载 {len(all_docs)} 页文本")

3. 文本切分

文档切分成更小的段落,方便后续的向量化和检索。chunk_size 为每个 chunk 最大字符数,chunk_overlap 让相邻 chunk 共享边界内容,避免关键信息被截断。

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
)
chunks = splitter.split_documents(all_docs)
print(f"共生成 {len(chunks)} 个 chunk")

4. 向量化和存储

使用 HuggingFaceEmbeddings 生成文本的向量表示,并存储到 ChromaDB。all-MiniLM-L6-v2 是最常用的轻量通用 embedding 模型,速度快,在本地运行,不需要 API key,也不依赖 GPU。ChromaDB 是一个轻量的本地向量数据库,适合小规模数据的存储和检索。

CHROMA_DB_DIR    = "./chroma_db"
COLLECTION_NAME  = "hailo_docs"

embeddings = HuggingFaceEmbeddings(
    model_name="sentence-transformers/all-MiniLM-L6-v2",
    model_kwargs={"device": "cpu"},
    encode_kwargs={"normalize_embeddings": True}
)
print("Embedding 模型就绪")

db_exists = os.path.exists(CHROMA_DB_DIR) and os.listdir(CHROMA_DB_DIR)

if db_exists:
    # 数据库已存在,直接加载
    vectorstore = Chroma(
        collection_name=COLLECTION_NAME,
        embedding_function=embeddings,
        persist_directory=CHROMA_DB_DIR,
    )
    print(f"已加载现有数据库 {CHROMA_DB_DIR}")
else:
    # 首次构建
    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=embeddings,
        collection_name=COLLECTION_NAME,
        persist_directory=CHROMA_DB_DIR,
    )
    print(f"数据库构建完成,已持久化到 {CHROMA_DB_DIR}")

5. 自定义 HailoChatOllama 类

Hailo-Ollama 的底层用的是 oatpp HTTP 框架,它对请求体的 JSON 字段类型要求很严。LangChain 官方的 ChatOllama 会往请求里附加 optionskeep_alive 等额外字段,oatpp 碰到未知字段直接报类型解析错误。

解决方案是继承 BaseChatModel 自己写一个简化版:

class HailoChatOllama(BaseChatModel):
    model: str
    base_url: str
    timeout: int = 120

    @property
    def _llm_type(self) -> str:
        return "hailo-ollama"

    def _to_ollama_messages(self, messages: List[BaseMessage]) -> list:
        role_map = {"system": "system", "human": "user", "ai": "assistant"}
        return [
            {"role": role_map.get(m.type, m.type), "content": m.content}
            for m in messages
        ]

    def _generate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Any = None,
        **kwargs: Any,
    ) -> ChatResult:
        payload = {
            "model": self.model,
            "messages": self._to_ollama_messages(messages),
            "stream": False,
        }
        resp = requests.post(
            f"{self.base_url}/api/chat",
            json=payload,
            timeout=self.timeout,
        )
        resp.raise_for_status()
        content = resp.json()["message"]["content"]
        return ChatResult(
            generations=[ChatGeneration(message=AIMessage(content=content))]
        )

    def _stream(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Any = None,
        **kwargs: Any,
    ) -> Iterator[ChatGenerationChunk]:
        payload = {
            "model": self.model,
            "messages": self._to_ollama_messages(messages),
            "stream": True,
        }
        with requests.post(
            f"{self.base_url}/api/chat",
            json=payload,
            stream=True,
            timeout=self.timeout,
        ) as resp:
            resp.raise_for_status()
            for line in resp.iter_lines():
                if not line:
                    continue
                data = json.loads(line.decode("utf-8"))
                token = data.get("message", {}).get("content", "")
                if token:
                    chunk = ChatGenerationChunk(
                        message=AIMessageChunk(content=token)
                    )
                    if run_manager:
                        run_manager.on_llm_new_token(token, chunk=chunk)
                    yield chunk
                if data.get("done"):
                    break

只传 modelmessages 两个字段,完全避开 oatpp 的字段校验问题。

6. RAG 链的组装

这里搭建了一个最基本的 RAG 链路,根据用户提问先用检索器从 ChromaDB 取最相关的 3 个 chunk,把它们拼成一个上下文,再把上下文和问题一起塞进 prompt 让模型回答。

# 创建检索器,相似度搜索,返回 top_k 最相关 chunk
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

llm = HailoChatOllama(
    model="qwen3:1.7b",
    base_url="http://localhost:8000"
)

# 问答函数
def stream_answer(question: str):
    docs = retriever.invoke(question)
    context = "\n\n---\n\n".join(d.page_content for d in docs)
    prompt = f"""
你是一个专业助手,只根据下面提供的参考文档尽可能简短的回答问题。如果文档中没有相关信息,请直接说"文档中未找到相关内容"。

文档内容:
{context}

问题:{question}
"""
    
    for chunk in llm.stream([HumanMessage(content=prompt)]):
        print(chunk.content, end="", flush=True)
    print()

stream_answer("How to install HailoRT on Ubuntu?")

5

LoRA 微调

除了 RAG 可以使模型获取外部知识之外,LoRA 微调可以让模型学会一些特定的技能。比如你想让模型更懂你公司的业务术语,或者学会一些特定的操作流程,这时候就可以通过 LoRA 微调来实现。LoRA 微调的原理是只训练模型中一小部分参数(通常是权重矩阵的低秩分解),而保持其他参数冻结不变。这样可以大幅降低微调所需的计算资源和时间,同时还能让模型学会新的技能。

我一开始的设想是类似之前做视觉模型一样(将自定义模型编译为 Hailo NPU 的 .hef 模型),先任选一个通用基座进行微调,然后导出 ONNX 模型,最后将 ONNX 模型编译成 HEF 模型在 Hailo-10 上部署。结果发现目前不支持用户自定义模型,只能使用官方提供的模型。这也是目前 Hailo-10 在大模型推理方面的一个限制。

不过 Hailo 官方在 Hailo Dataflow Compiler 用户手册的第 4.8 章和第 6 章提供了一种特殊的微调方案,仅支持 Qwen2-1.5B-Instruct 这个模型。需要将微调后的 LoRA adapter 权重挂载到预优化的 HAR 文件中,然后量化编译为 HEF 模型。这个过程需要的硬件配置较高,需要一块 32GB 显存的 GPU 和 128GB 的内存。下面根据官方文档简单的介绍一下这个流程,这里我并没有进行测试。

1. 微调

在微调时,Hailo DFC 有一些硬性约束,不满足就编译报错。这意味着不能用常见的 r=8r=16 配置,秩必须是 32。这个约束来自 Hailo 硬件对 LoRA 矩阵乘法的专用实现。

参数 要求
lora_alpha 必须等于 64
r(秩) 必须等于 32
target_modules 只能是 FFN 的 gate_projdown_projup_proj
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig
from trl import SFTConfig, SFTTrainer

# 1. 基础配置
MODEL_ID = "Qwen/Qwen2-1.5B-Instruct"
OUTPUT_DIR = "./lora_adapter_news"

# 2. 加载模型与分词器,关闭 KV cache 节省显存
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype="auto", device_map="auto")
model.config.use_cache = False
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)

# 3. 准备数据集 (AG News)
dataset = load_dataset("fancyzhx/ag_news", split="test[:1000]") # 取前1000条快速演示

def format_data(example):
    # 转换为 Qwen 对话格式
    messages = [
        {"role": "system", "content": "Classify news into: world, sports, business, sci/tech."},
        {"role": "user", "content": example["text"]}
    ]
    prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    labels = ["world", "sports", "business", "sci/tech"]
    return {"text": prompt + labels[example["label"]]}

formatted_dataset = dataset.map(format_data)

# 4. Hailo 兼容的 LoRA 配置
peft_config = LoraConfig(
    lora_alpha=64,            # 必须为 64
    r=32,                     # 必须为 32
    target_modules=["gate_proj", "down_proj", "up_proj"], # 仅支持 FFN 层
    task_type="CAUSAL_LM",
)

# 5. 开始训练
training_args = SFTConfig(
    dataset_text_field="text",
    max_seq_length=512,
    output_dir=OUTPUT_DIR,
    num_train_epochs=1,
    per_device_train_batch_size=4,
)

trainer = SFTTrainer(
    model=model,
    train_dataset=formatted_dataset,
    peft_config=peft_config,
    args=training_args,
)

trainer.train()
print(f"LoRA 权重已保存至: {OUTPUT_DIR}")

2. 编译 HEF 模型

将基础模型和 LoRA 权重融合,量化并编译为专用的二进制 .hef 文件。需要提前从 Hailo 官方获取 Qwen2-1.5B 的预优化文件(放在 ./models 目录下):

import os
import tensorflow as tf
from hailo_sdk_client.runner.client_runner import ClientRunner

MODEL_NAME = "qwen2_1.5b_instruct"
ADAPTER_NAME = "lora_adapter_news"

# 1. 初始化 Runner 并加载基础 HAR
runner = ClientRunner(hw_arch="hailo10h")
runner.load_har(f"./models/{MODEL_NAME}.q.har")

# 2. 挂载阶段一训练出的 LoRA 权重
runner.load_lora_weights(
    lora_weights_path="./lora_adapter_news/checkpoint-250/adapter_model.safetensors", # 请替换为实际checkpoint路径
    lora_adapter_name=ADAPTER_NAME,
)
hn_dict = runner.load_model_script(f"./models/{MODEL_NAME}.alls")

# 3. 构造量化校准数据
# 这里仅使用最基础的 Dummy Data 作为示例,实际应用中建议使用格式化后的真实文本 input_ids
import numpy as np
cache_size = hn_dict["net_params"]["cache_size"]
dummy_input = np.zeros((64, 1, cache_size)) # 64 个全零样本用于跑通编译流程
dummy_cur_pos = np.ones((64,)) * cache_size

input_dict = {
    f"{ADAPTER_NAME}/input_layer1": dummy_input,
    f"{ADAPTER_NAME}/input_layer2": dummy_cur_pos,
    f"{ADAPTER_NAME}/input_layer3": dummy_cur_pos,
    f"{ADAPTER_NAME}/input_layer4": dummy_cur_pos,
    f"{ADAPTER_NAME}/input_layer5": dummy_cur_pos,
    f"{ADAPTER_NAME}/input_layer6": dummy_cur_pos,
}

print("开始优化 LoRA 权重...")
with tf.device("/cpu:0"):
    runner.optimize(input_dict)

# 保存中间状态并重新加载以准备编译
runner.save_har(f"{MODEL_NAME}.lora.q.har", compilation_only=False)
runner = ClientRunner(hw_arch="hailo10h", har=f"{MODEL_NAME}.lora.q.har")

# 4. 替换编译脚本中的占位符
with open(f"./models/{MODEL_NAME}_compilation.alls", "r") as f:
    compile_script = f.read().replace("LORA_NAME_PLACEHOLDER", ADAPTER_NAME)

with open("compile_final.alls", "w") as f:
    f.write(compile_script)

runner.load_model_script("compile_final.alls")

# 5. 执行最终编译
print("开始编译 HEF (此过程可能需要大量内存与时间)...")
runner.compile()
runner.save_har(f"{MODEL_NAME}.lora.compiled.har", compilation_only=True)
print("编译完成!可使用 HailoRT 进行推理。")

总结

折腾完这一套之后,对端侧大模型多了一些具体的认识。虽然目前 Hailo-10 支持的模型有限,微调和编译的流程也比较繁琐。并且小模型的能力有限,RAG 能补充知识但补不了推理能力。但对于一些特定的应用场景,还是很有价值的。比如在一些对隐私敏感、网络受限的纯嵌入式场景中,根据任务结果使用本地大模型进行分析推理是非常有意义的。未来如果 Hailo 能支持用户自定义模型,或者提供更简单的微调和编译流程,相信会有更多有趣的应用出现。