作为一名长期奋战在AI应用开发一线的工程师,我深知将大模型从"玩具"变成"工具"的挑战。本章将分享我在实际项目中积累的提示工程、函数调用和RAG应用的核心经验,这些方法已经帮助多个企业级项目成功落地。
记得我第一次调用大模型时,简单写了句"写个Python排序函数",结果得到的代码简直惨不忍睹——没有注释、边界条件处理缺失、甚至用了危险的eval()。这让我意识到:大模型的输出质量与提示质量直接相关。
典型问题场景分析:
关键认知:提示工程不是"魔法咒语",而是明确的人机协作协议。好的提示应该像PRD文档一样清晰具体。
基于数十个项目的实践,我总结出CRIS+框架(比原书CRIS新增Schema要求),这是保证提示有效性的方法论基础:
python复制# 不良示例
"优化这段代码"
# 良好示例
"将以下Python代码的时间复杂度从O(n²)优化到O(n log n),仅使用标准库"
python复制# 不良示例
"你是程序员"
# 良好示例
"你是5年经验的前端工程师,精通React和TypeScript,熟悉ESLint规范"
python复制# 不良示例
"分析用户数据"
# 良好示例
"""
输入:JSON数组(每个元素含user_id、age、gender)
输出:JSON对象(含age均值、男女比例)
"""
python复制"禁止使用eval/exec、os.system等危险函数;仅使用Python 3.9+标准库"
python复制"必须输出valid JSON,符合以下Schema:
{'code': int, 'msg': string, 'data': {'result': string}}"
在实际项目中,我开发了以下提示生成器,确保团队保持统一的提示标准:
python复制def build_prompt(role, instruction, input_data, output_schema, safety_rules):
"""
生成符合CRIS+原则的提示
:param role: 模型角色
:param instruction: 具体指令
:param input_data: 输入数据(字符串/JSON)
:param output_schema: 输出Schema描述
:param safety_rules: 安全规则列表
:return: 完整提示字符串
"""
safety_text = "- " + "\n- ".join(safety_rules) if safety_rules else "无"
return f"""
你是{role}。
# 指令
{instruction}
# 输入数据
{input_data}
# 输出要求
1. 严格按照以下Schema输出JSON格式:
{output_schema}
2. 字段类型必须匹配,必填项不可缺失
3. 仅返回JSON,不要添加额外解释
# 安全规则
{safety_text}
"""
# 使用示例
prompt = build_prompt(
role="数据分析师",
instruction="从用户评论中提取产品缺陷(最多5个),按出现频率排序",
input_data='{"comments": ["电池不耐用", "屏幕易碎", "电池不耐用", "系统卡顿"]}',
output_schema='''{
"defects": [{"name": string, "count": int}], # 缺陷列表,按count降序
"total": int # 缺陷总数
}''',
safety_rules=["仅提取客观缺陷,不添加主观评价", "缺陷名称统一为名词短语"]
)
实战技巧:
在电商客服项目中,我们曾遇到模型虚构天气信息的尴尬。函数调用技术从根本上解决了这个问题:
四大核心价值:
以查询天气为例,典型交互流程如下:
json复制{"name": "get_weather", "parameters": {"city": "北京", "date": "2023-11-20"}}
异常处理机制:
以下是基于Qwen-7B-Chat的完整实现:
python复制from transformers import AutoModelForCausalLM, AutoTokenizer
import json
# 1. 模型加载
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat", trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen-7B-Chat",
trust_remote_code=True,
device_map="auto",
torch_dtype="auto"
).eval()
# 2. 业务函数定义
def get_weather(city: str, date: str = None) -> dict:
"""获取天气(模拟实现)"""
mock_data = {
"北京": {"2023-11-20": {"temp": 8, "desc": "晴", "wind": "3级"}},
"上海": {"2023-11-20": {"temp": 12, "desc": "多云", "wind": "2级"}}
}
date = date or "2023-11-20"
return mock_data.get(city, {}).get(date, {"error": "无数据"})
# 3. 系统提示设计
system_prompt = """
你是智能助手,可调用以下函数:
1. get_weather(city: str, date: str = None)
- 功能:查询城市天气
- 调用条件:用户询问天气时
响应规则:
- 需要调用函数时,返回JSON:{"name":"函数名","parameters":{"参数":"值"}}
- 参数缺失时,询问用户补充
"""
# 4. 交互处理
def process_query(user_query):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_query}
]
# 首次生成
inputs = tokenizer.apply_chat_template(messages, return_tensors="pt").to("cuda")
outputs = model.generate(inputs, max_new_tokens=200)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
try:
# 解析函数调用
call_info = json.loads(response)
if call_info["name"] == "get_weather":
weather = get_weather(**call_info["parameters"])
# 二次生成
messages.append({"role": "assistant", "content": response})
messages.append({"role": "user", "content": f"函数结果:{weather}"})
final_output = model.generate(
tokenizer.apply_chat_template(messages, return_tensors="pt").to("cuda"),
max_new_tokens=200
)
return tokenizer.decode(final_output[0], skip_special_tokens=True)
except:
return response # 直接返回自然语言回复
避坑指南:
在金融知识库项目中,我们测试了三种方案:
| 方案 | 知识更新时间 | 专业术语准确率 | 合规风险 |
|---|---|---|---|
| 纯模型 | 训练截止日 | 68% | 高 |
| 微调 | 标注日 | 82% | 中 |
| RAG | 实时 | 95% | 低 |
RAG的胜利在于:将知识检索与文本生成解耦。
经过多个项目迭代,我们的稳定架构如下:
code复制[文档来源]
↓
[文档加载] → PDF/Word/HTML解析
↓
[文本分块] → 语义分块(非固定长度)
↓
[向量化] → BGE-large-zh中文模型
↓
[向量存储] → Milvus集群
↓
[查询流程] → 混合检索(向量+BM25)→ 结果重排 → 提示注入
关键组件选型建议:
| 组件 | 推荐方案 | 适用场景 | 性能指标 |
|---|---|---|---|
| 文本分块 | SemanticChunker | 技术文档 | 保持语义完整 |
| 向量模型 | bge-large-zh | 中文场景 | 768维,召回率92% |
| 向量库 | Milvus | 生产环境 | 支持亿级数据 |
| 检索策略 | HybridSearch | 复杂查询 | 准确率提升25% |
python复制from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
# 1. 文档处理
loader = PyPDFLoader("企业规范.pdf")
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。"]
)
docs = text_splitter.split_documents(loader.load())
# 2. 向量化
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh")
db = Chroma.from_documents(docs, embeddings, persist_directory="./chroma_db")
# 3. 检索增强
retriever = db.as_retriever(search_type="mmr", search_kwargs={"k": 3})
def rag_query(question):
docs = retriever.get_relevant_documents(question)
context = "\n\n".join(d.page_content for d in docs)
prompt = f"""基于以下上下文回答问题:
{context}
问题:{question}
要求:
1. 严格基于上下文
2. 未提及的内容回答"不清楚"
"""
return llm(prompt)
性能优化技巧:
我们在医疗项目中建立了四级校验:
python复制class DiagnosisOutput(BaseModel):
disease: str = Field(pattern=r"^[A-Za-z\u4e00-\u9fa5]+$")
confidence: float = Field(ge=0, le=1)
treatments: List[str] = Field(min_items=1)
python复制def validate_treatment(disease: str, treatments: list):
known_patterns = {
"感冒": ["休息", "喝水"],
"骨折": ["固定", "静养"]
}
return all(t in known_patterns.get(disease, []) for t in treatments)
python复制sensitive_words = ["自杀", "暴力"]
if any(word in output for word in sensitive_words):
raise ContentSafetyError
python复制if confidence < 0.7 or disease in high_risk_list:
send_to_human_review(output)
我们部署的监控看板包含:
| 指标 | 计算方式 | 报警阈值 |
|---|---|---|
| 格式错误率 | 格式错误次数/总调用 | >5% |
| 幻觉率 | 无依据陈述/总陈述 | >3% |
| 平均响应时间 | 总耗时/调用次数 | >3s |
| 函数调用成功率 | 成功次数/调用次数 | <95% |
当检测到异常时,系统自动切换:
根据我们的压力测试结果(基于AWS c5.2xlarge):
| 场景 | LangChain QPS | LlamaIndex QPS | 自研实现 QPS |
|---|---|---|---|
| 简单问答 | 32 | 45 | 68 |
| RAG检索 | 28 | 52 | 60 |
| 函数调用 | 25 | 38 | 72 |
选型决策树:
我们项目中采用的解耦设计:
code复制[业务逻辑] ←→ [适配层] ←→ [AI框架]
这样当需要替换LangChain时,只需重写适配层。实际案例中,我们将核心服务从LangChain迁移到自研实现,只花了2人日。
code复制[前端]
↑↓ HTTP
[API服务] ←→ [向量库] ←→ [文档库]
↑↓ ↑
[LLM] [SQL数据库]
python复制class EnterpriseAssistant:
def __init__(self):
self.llm = load_qwen()
self.vector_db = init_milvus()
self.sql_conn = init_postgres()
def handle_query(self, user_input):
# 意图识别
intent = classify_intent(user_input)
if intent == "policy_query":
return self.rag_query(user_input)
elif intent == "data_query":
return self.sql_query(user_input)
else:
return self.llm(user_input)
def rag_query(self, question):
# 检索增强流程
docs = retrieve_docs(question)
prompt = build_rag_prompt(question, docs)
return generate_with_validation(prompt)
def sql_query(self, query):
# 语义转SQL
sql = self.llm(f"将问题转为SQL:{query}")
if not validate_sql(sql):
raise SQLInjectionError
return execute_sql(sql)
案例1:未做输出校验
案例2:过度依赖框架
案例3:检索质量差
在电商客服项目中,这些优化使单机QPS从12提升到35,同时将延迟从2.3s降至1.4s。