一文讲透如何构建 Harness——六大组件全解析
本文是对腾讯云开发者社区文章《一文讲透如何构建 Harness——六大组件全解析》(作者:李伟山)的阅读总结与深度解读。原文详细拆解了 AI Agent 工程中最重要的概念之一——Harness,本文在保留核心观点的同时做了结构化的梳理。
本文是对腾讯云开发者社区文章《一文讲透如何构建 Harness——六大组件全解析》(作者:李伟山)的阅读总结与深度解读。原文详细拆解了 AI Agent 工程中最重要的概念之一——Harness,本文在保留核心观点的同时做了结构化的梳理。
本文记录了 uni-app 项目通过 HBuilderX 离线 SDK 打包 Android APK 的全过程。适用于 5.15 版本 SDK,内容包括环境准备、Gradle 工程搭建、资源编译、签名配置、APK 构建及调试安装。如果你习惯 CLI 而非 GUI 打包,这篇就是为你准备的。
这套组合的出现有一个很现实的理由:
所以很自然的,你会想让 VSCode 连进 WSL,然后在编辑器里直接和 Hermes 交互——写完代码让它审,遇到问题让它查,重构代码让它帮。
这篇文章就讲清楚这条路怎么打通,以及打通之后能怎么用。
本文是一份完整的 HyperFrames 时间线视频制作脚本 + 实际生成记录。前半部分为逐帧镜头设计和生图 Prompt,后半部分记录了用
edge-tts+Pillow+ffmpeg纯代码生成视频的全过程(含踩坑)。成品 1080p / 3m51s / 7.5MB,带 TTS 配音和硬字幕。
zh-CN-YunxiNeural 男声,rate +10%| 组件 | 数量 | 单价(估算) | 小计 | 占比 |
|---|---|---|---|---|
| GB200 Superchip (1 Grace + 2 B200) | 36 | $60K-$70K | $2.16M-$2.52M | ~72-78% |
| NVLink 5 Switch 交换盘 | 18 | $12K-$15K | $216K-$270K | ~7-9% |
| 液冷散热系统 (DLC/CDU) | 1 套 | $80K-$120K | $80K-$120K | ~3-4% |
| ConnectX-8/7 网卡 + Spectrum-X 交换机 | 36 NIC + 交换机 | 整套 ~$120K-$180K | $120K-$180K | ~4-6% |
| 定制机柜 + 电源分配 (PDU) | 1 套 | $40K-$70K | $40K-$70K | ~1.5-2.5% |
| BlueField-3 DPU | 2-4 | $3K-$5K | $12K-$20K | ~0.5% |
| 组装/集成/测试 + 原厂 margin | — | — | ~$100K-$150K | ~3-5% |
| 合计 | — | — | ~$2.7M-$3.3M | 100% |
来源说明:NVIDIA 不公开组件级定价。以上数据综合自 SemiAnalysis 深度分析(2024.03)、Morgan Stanley 研报(2024.03)、Dell/Supermicro OEM 报价泄露、ServeTheHome 和 Tom's Hardware 报道。实际价格因 OEM 厂商、采购规模、配置差异浮动 ±15%。
2023 年 6 月,OpenAI 在 GPT-4-0613 和 GPT-3.5-turbo-0613 中首次引入了 Function Calling 能力。当时很多人第一反应是:"这不就是让模型生成 JSON 吗?有什么特别的?"
两年后再回头去看,Function Calling 是 LLM 从"聊天玩具"走向"生产工具"的关键一步。本文不讲 API 怎么调用,而是聊清楚一个更根本的问题:Function Calling 到底解决了什么问题?它为什么一定会出现?
2024 年底,DeepSeek 发布了 DeepSeek-V2 和 DeepSeek-R1 系列模型,并选择完全开源。这一举动在全球 AI 社区引发了巨大反响。本文试图从多个维度分析 DeepSeek 开源战略的真正价值。
AI Infra = AI Infrastructure(AI 基础设施),指的是支撑 AI 系统从研发到落地全流程的底层技术栈。
只要你听过"大模型训练太贵了"、"推理速度不够快"、"GPU 利用率上不去"——这些问题的根都在 AI Infra 上。
AI Infra 不是单一的东西,从上到下可以拆成四个层面:
这层最重、最贵,也是普通人最容易感知的部分:
训练一个 70B 模型,单机 8 卡 H100 是不够的,需要几十上百台机器组集群,这时候 InfiniBand 的带宽决定了你"能不能卡在通信上"。
硬件买回来只是开始,怎么让几十上百台机器跑起来是另一回事:
搞过 K8s 的人都知道,加上 GPU 之后调度复杂度直接翻倍——要处理 GPU 显存碎片、MIG 分区、节点选择、拓扑亲和性。这层是 AI Infra Engineer 最常打交道的地方。
这层解决两个核心问题:怎么训得快 和 怎么跑得稳。
训练方向:
推理方向:
模型与数据管理:
模型训练完了,要部署成线上服务,这层关注的是稳定性、可观测性和自动化:
三个字:成本差。
同样的 70B 模型,AI Infra 做得好的团队,训练成本可能是别人的 1/3,推理延迟可能是别人的 1/5。这不是夸张——DeepSpeed 的 ZeRO-3 能在同样硬件上省 60% 显存,vLLM 的 PagedAttention 能提升 2-4 倍推理吞吐。
另一个角度:GPU 利用率就是钱。
H100 一小时几十块的折旧成本,利用率从 30% 提到 80%,相当于 2.6 倍的硬件收益。AI Infra 干的就是这件事。
对应的职位通常是 AI Infra Engineer / AI 基础设施工程师,日常工作包括:
如果你在用 Rust / Go / C++ 做这些方向,非常明确就是 Infra 角色。
┌─────────────────────────────────┐
│ 服务/业务层 │
│ 推理网关 · 监控 · MLOps · CI/CD│
├─────────────────────────────────┤
│ 平台软件层 │
│ PyTorch · DeepSpeed · vLLM │
│ TensorRT · Triton · MLflow │
├─────────────────────────────────┤
│ 编排层 │
│ K8s + GPU Op · Slurm · Ray │
│ 调度 · 弹性 · 混部 │
├─────────────────────────────────┤
│ 硬件层 │
│ GPU · InfiniBand · 分布式存储 │
└─────────────────────────────────┘
AI Infra = 让 AI 模型能"算得快、训得起、部署稳"的底层工程体系。
不是每个人都需要深入每一层,但理解这个分层结构,能帮你快速定位问题在哪层,以及你的工作落在哪个位置。
需要从一份 Word 简历中提取结构化信息(姓名、专业技能、工作年限),用 RAG 方案比直接写正则解析灵活得多。
| 组件 | 选型 |
|---|---|
| 文档加载 | UnstructuredLoader |
| 向量库 | Qdrant (Docker) |
| 嵌入模型 | 通义千问 text-embedding-v1 |
| LLM | DeepSeek v4 Flash |
| 编排框架 | LangChain |
| 运行环境 | Python 3.13 + uv |
rag/
├── main.py # 主流程
├── llm.py # LLM + 向量库封装
├── prompt.py # 本地 prompt 模板
├── sensenova.py # 备用 LLM 客户端
├── pyproject.toml # 依赖管理
└── uv.lock
word = UnstructuredLoader("简历.docx")
docs = word.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=50, chunk_overlap=20)
chunks = splitter.split_documents(docs)
UnstructuredLoader 支持 .docx/.pdf/.pptx 等格式,自动提取文本。分片用 50 字符 + 20 字符重叠,对简历这种短文档粒度合适。
embeddings = DashScopeEmbeddings(
dashscope_api_key=os.environ.get("dashscope"),
model="text-embedding-v1",
)
vectordb = QdrantVectorStore.from_documents(
chunks, embeddings,
collection_name="resume",
url="http://localhost:6333",
)
嵌入用通义千问的 text-embedding-v1,向量库用 Qdrant(Docker 运行),collection 名 resume。
chain = {
"context": vec_store.as_retriever() | format_docs,
"question": RunnablePassthrough(),
} | prompt | llm | StrOutputParser()
用 LangChain Expression Language 串联:检索 → 格式化上下文 → 填充 prompt → LLM 推理 → 解析输出。
chain.invoke("请输入姓名. 格式如下\n姓名: ?")
chain.invoke("总结专业技能情况. 格式如下\n专业技能: ?")
chain.invoke("根据工作年份总结工作经验. 格式如下\n工作经验: ?年")
三次调用分别提取姓名、技能、工作年限,prompt 里约束了输出格式。
姓名: XX
专业技能: Go, Python, C/C++, C#, Java, Lua
工作经验: 约16年
hub.pull("rlm/rag-prompt") 默认不允许拉取公开 prompt,需要:
from langsmith import Client as LangSmithClient
client = LangSmithClient()
prompt = client.pull_prompt("rlm/rag-prompt", dangerously_pull_public_prompt=True)
langchain-text-splitters、langchain-community、dashscope 需要在 pyproject.toml 中显式声明。
Qdrant 通过 Docker 启动,确保 localhost:6333 可访问。
import os
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_unstructured import UnstructuredLoader
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langsmith import Client as LangSmithClient
from llm import DeepSeek, QdrantVecStoreFromDocs
def clearstr(s: str) -> str:
filter_chars = ['\n', '\r', '\t', '\u3000', ' ']
for char in filter_chars:
s = s.replace(char, '')
return s
def format_docs(docs) -> str:
return "\n\n".join(clearstr(doc.page_content) for doc in docs)
def load_doc():
word = UnstructuredLoader("C:\\Users\\Administrator\\Downloads\\xxx.docx")
docs = word.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=50, chunk_overlap=20)
chunks = splitter.split_documents(docs)
vec_store = QdrantVecStoreFromDocs(chunks, collection_name="resume")
llm = DeepSeek()
client = LangSmithClient()
prompt = client.pull_prompt("rlm/rag-prompt", dangerously_pull_public_prompt=True)
chain = {
"context": vec_store.as_retriever() | format_docs,
"question": RunnablePassthrough(),
} | prompt | llm | StrOutputParser()
ret = chain.invoke("请输入姓名. 格式如下\n姓名: ?")
print(ret)
ret = chain.invoke("总结专业技能情况,内容可能包含golang、AI Agent、python、rag等.格式如下\n专业技能: ?")
print(ret)
ret = chain.invoke("根据各大公司工作过的年份总结工作经验有多少年.格式如下\n工作经验: ?年")
print(ret)
def main():
print("Hello from rag!")
load_doc()
if __name__ == "__main__":
main()
import os
from typing import List, Optional
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.documents import Document
from langchain_openai import ChatOpenAI
from langchain_qdrant import QdrantVectorStore
def DeepSeek():
return ChatOpenAI(
model="deepseek-v4-flash",
api_key=os.environ.get("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
)
def get_embeddings():
return DashScopeEmbeddings(
dashscope_api_key=os.environ.get("dashscope"),
model="text-embedding-v1",
)
def QdrantVecStoreFromDocs(docs: List[Document], collection_name: str,
qdrant_url: Optional[str] = None,
qdrant_api_key: Optional[str] = None):
if qdrant_url is None:
qdrant_url = "http://localhost:6333"
embeddings = get_embeddings()
vectordb = QdrantVectorStore.from_documents(
docs, embeddings, collection_name=collection_name,
url=qdrant_url, api_key=qdrant_api_key,
)
return vectordb
RAGPrompt = """
You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer the question.
If you don't know the answer, just say that you don't know,
don't try to make up an answer.
Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:
"""
[project]
name = "rag"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"langchain>=1.3.11",
"langchain-unstructured>=0.1.0",
"langchain-openai>=1.3.3",
"langchain-text-splitters>=0.3.0",
"langchain-community>=0.3.0",
"nltk>=3.9.4",
"python-docx>=1.2.0",
"unstructured>=0.23.1",
"langchain-qdrant>=1.1.0",
"dashscope>=1.0.0",
]
LangChain 的 LCEL 写 RAG pipeline 非常简洁,配合 Docker 运行的 Qdrant 和 DeepSeek 的 API,从文档到结构化输出只要几十行代码。这套方案不仅适用于简历解析,合同审核、知识库 QA 等场景也能直接复用。
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh |bash
. ~/.bashrc
sonar list
sonar list --stats
sonar list -c port,process,cpu,mem,uptime,state
sonar info 80
sonar kill 80 # SIGTERM
sonar kill 80 -f # SIGKILL
sonar logs 80
sonar attach 80
# 每2秒刷新,显示变化
sonar watch
# 实时资源监控
sonar watch --stats
# 更快的刷新间隔
sonar watch -i 500ms
# 端口上下线时发送桌面通知
sonar watch --notify
sonar graph
# 等待TCP连接可用
sonar wait 80
# 多个端口
sonar wait 80 6379
# 等待HTTP 200
sonar wait 80 --http
# 检查特定断点
sonar wait 80 --http=/health
# 超时
sonar wait 80 --timeout 30s
# 把 6873 端口的服务映射到3002 端口。
sonar map 6873 3002
sonar next
# 从 8000 开始
sonar next 8000
# 在范围搜索
sonar next 3000-3100
# 找 3 个连续空闲端口
sonar next -n 3
#
sonar list --host user@server
sonar watch --host user@server
blog index.md