阶段4(下):LangChain 1.x、提示词与注入防护、模型侧(路由/vLLM/LoRA)、AI 编程工具与验证、Dify/Coze 专题
This commit is contained in:
1 parent
caaec830b3
commit
7a32d2c9a6
38 files changed
+3059
-67
No files matched your search
@@ -0,0 +1,103 @@
|
||||
---
|
||||
id: ai.langchain.lcel-structured-agent
|
||||
title: LangChain 1.x:模型和消息、LCEL、结构化输出、create_agent 和中间件
|
||||
type: lesson
|
||||
domain: ai
|
||||
stack: [langchain]
|
||||
tags: [LangChain, LCEL, init_chat_model, 结构化输出, create_agent, middleware, 重试, 备用模型]
|
||||
level: intermediate
|
||||
prerequisites: [ai.function-calling.tool-loop]
|
||||
est_hours: 3
|
||||
related: [ai.langgraph.state-graph, ai.langchain.scenario.ticket-triage, ai.langchain.interview]
|
||||
week: [6, 11]
|
||||
order: 1
|
||||
---
|
||||
|
||||
# LangChain 1.x:模型和消息、LCEL、结构化输出、create_agent 和中间件
|
||||
|
||||
> LangChain 在 2025 年发布 1.0,API 和 0.x 有较大变化(例如 Agent 统一为 `create_agent`,基于 LangGraph 构建,并引入中间件)。网上大量教程是 0.x 写法(`AgentExecutor`、`initialize_agent`、`LLMChain`),不要照抄。本课基于 langchain 1.4 编写,以官方文档为准:https://docs.langchain.com/oss/python/langchain/overview
|
||||
|
||||
## 解释说明
|
||||
|
||||
### 核心组件
|
||||
|
||||
| 组件 | 说明 |
|
||||
| --- | --- |
|
||||
| 聊天模型 | 统一接口调用不同提供方:`init_chat_model("提供方:模型名")`;方法 `invoke` / `stream` / `batch` 及异步版本 |
|
||||
| 消息 | `SystemMessage`、`HumanMessage`、`AIMessage`(可带 `tool_calls`)、`ToolMessage` |
|
||||
| 提示词模板 | `ChatPromptTemplate.from_messages([...])`,用 `{变量}` 占位 |
|
||||
| 输出解析器 | `StrOutputParser`、`PydanticOutputParser`;支持原生结构化输出的模型优先用 `model.with_structured_output(Schema)` |
|
||||
| 工具 | `@tool` 装饰的函数,文档字符串就是工具描述 |
|
||||
| Agent | `create_agent(model, tools, system_prompt=..., middleware=[...], response_format=...)` |
|
||||
|
||||
### LCEL(LangChain 表达式语言)
|
||||
|
||||
用 `|` 把组件串成链:`prompt | model | parser`。每个组件都是 Runnable,统一支持 `invoke` / `stream` / `batch` / 异步,并且可以加:
|
||||
|
||||
- `.with_retry(stop_after_attempt=3)`:失败重试;
|
||||
- `.with_fallbacks([备用链])`:失败时切换到备用(例如另一个模型提供方);
|
||||
- `RunnableParallel`:并行执行多个分支。
|
||||
|
||||
### create_agent 和中间件
|
||||
|
||||
`create_agent` 实现了标准的工具调用循环(就是第 10 周手写的那个循环),返回一个 LangGraph 图,所以也支持检查点、流式、interrupt。1.x 的**中间件**可以在模型调用 / 工具调用前后插入逻辑,内置的有:调用次数限制、人工审批(HumanInTheLoop)、历史摘要(Summarization)、备用模型(ModelFallback)、PII 检测、工具重试等(名称和参数以官方文档为准)。
|
||||
|
||||
**什么时候用 LangChain,什么时候直接用 SDK?** 需要切换多个模型提供方、需要现成的检索 / 工具 / 中间件生态时用 LangChain;逻辑简单、追求最少依赖和完全可控时直接用厂商 SDK。面试时要能说出理由。
|
||||
|
||||
## 案例
|
||||
|
||||
工单分类服务:用 `PydanticOutputParser`(或 `with_structured_output`)输出 `{category, urgent}`;模型偶尔返回不合法格式,`with_retry` 重试一次;主模型提供方超时时 `with_fallbacks` 切换到备用提供方;批量历史工单用 `batch` 并发处理。
|
||||
|
||||
## 代码示例
|
||||
|
||||
完整可运行版本(假模型离线运行):[examples/lc-basics/lc_basics.py](examples/lc-basics/lc_basics.py)。
|
||||
|
||||
```python
|
||||
from langchain.agents import create_agent
|
||||
from langchain.agents.middleware import ModelCallLimitMiddleware
|
||||
from langchain_core.output_parsers import PydanticOutputParser
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.tools import tool
|
||||
|
||||
|
||||
class Triage(BaseModel):
|
||||
category: str = Field(pattern="^(refund|bug|question)$")
|
||||
urgent: bool
|
||||
|
||||
|
||||
parser = PydanticOutputParser(pydantic_object=Triage)
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "对工单分类。{format_instructions}"),
|
||||
("human", "{ticket}"),
|
||||
]).partial(format_instructions=parser.get_format_instructions())
|
||||
|
||||
chain = (prompt | model | parser).with_retry(stop_after_attempt=2).with_fallbacks([prompt | backup_model | parser])
|
||||
print(chain.invoke({"ticket": "我要退款,很急"})) # category='refund' urgent=True
|
||||
|
||||
|
||||
@tool
|
||||
def get_order_status(order_id: str) -> str:
|
||||
"""查询订单的物流状态。order_id 形如 A1001。"""
|
||||
...
|
||||
|
||||
|
||||
agent = create_agent(model, tools=[get_order_status], system_prompt="你是售后客服。",
|
||||
middleware=[ModelCallLimitMiddleware(run_limit=5)])
|
||||
out = agent.invoke({"messages": [{"role": "user", "content": "A1001 到哪了"}]})
|
||||
```
|
||||
|
||||
## 易错点
|
||||
|
||||
- 照抄 0.x 教程:`AgentExecutor`、`LLMChain` 等在 1.x 中已移到 `langchain-classic` 或不再推荐。
|
||||
- 各模型提供方是独立的包(`langchain-openai`、`langchain-anthropic` 等),版本要和 `langchain-core` 匹配。
|
||||
- 结构化输出只靠提示词约束,不做校验。
|
||||
- `with_fallbacks` 的备用链输入输出格式和主链不一致。
|
||||
- 工具的文档字符串写得太简单,模型不知道何时调用。
|
||||
- 过度封装:简单的一次调用也套很多层链,调试困难。
|
||||
|
||||
## 面试常问
|
||||
|
||||
- LangChain 1.x 和 0.x 有什么变化?
|
||||
- LCEL 是什么?有什么好处?
|
||||
- 怎么保证结构化输出可靠?
|
||||
- 什么时候不用 LangChain?
|
||||
@@ -0,0 +1,101 @@
|
||||
# requires: langchain, langchain_core, pydantic
|
||||
"""LangChain 1.x 基础:LCEL 链、结构化输出解析 + 重试 + 备用模型、create_agent 工具调用 + 中间件。
|
||||
|
||||
用假模型离线运行(GenericFakeChatModel 按顺序返回预设消息)。换成真实模型:
|
||||
from langchain.chat_models import init_chat_model
|
||||
model = init_chat_model("openai:gpt-4.1-mini") # 模型名和提供方写法以官方文档为准
|
||||
基于 langchain 1.4 / langchain-core 1.7 编写,以官方文档为准:https://docs.langchain.com/oss/python/langchain/overview
|
||||
运行:python lc_basics.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from langchain.agents import create_agent
|
||||
from langchain.agents.middleware import ModelCallLimitMiddleware
|
||||
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
|
||||
from langchain_core.messages import AIMessage
|
||||
from langchain_core.output_parsers import PydanticOutputParser, StrOutputParser
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.runnables import RunnableLambda
|
||||
from langchain_core.tools import tool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
def fake(*texts_or_msgs) -> GenericFakeChatModel:
|
||||
msgs = [m if isinstance(m, AIMessage) else AIMessage(content=m) for m in texts_or_msgs]
|
||||
return GenericFakeChatModel(messages=iter(msgs))
|
||||
|
||||
|
||||
# ---------- 1. LCEL:提示词 | 模型 | 解析器 ----------
|
||||
def demo_lcel() -> str:
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "你是客服,用一句话回答。"),
|
||||
("human", "{question}"),
|
||||
])
|
||||
chain = prompt | fake("您好,开发票请在订单详情页申请。") | StrOutputParser()
|
||||
return chain.invoke({"question": "怎么开发票"})
|
||||
|
||||
|
||||
# ---------- 2. 结构化输出:解析 + 失败重试 + 备用模型 ----------
|
||||
class Triage(BaseModel):
|
||||
category: str = Field(pattern="^(refund|bug|question)$")
|
||||
urgent: bool
|
||||
|
||||
|
||||
def demo_structured() -> Triage:
|
||||
parser = PydanticOutputParser(pydantic_object=Triage)
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "对工单分类。{format_instructions}"),
|
||||
("human", "{ticket}"),
|
||||
]).partial(format_instructions=parser.get_format_instructions())
|
||||
|
||||
# 主模型第一次返回了不合法的 JSON,第二次正确:with_retry 对解析失败重试
|
||||
primary = prompt | fake("类别是退款", '{"category": "refund", "urgent": true}') | parser
|
||||
robust = primary.with_retry(stop_after_attempt=2)
|
||||
|
||||
# 主链彻底失败时切换到备用链(真实项目中是另一个模型 / 提供方)
|
||||
def broken(_):
|
||||
raise TimeoutError("主模型超时")
|
||||
backup = prompt | fake('{"category": "question", "urgent": false}') | parser
|
||||
with_fallback = RunnableLambda(broken).with_fallbacks([backup])
|
||||
assert with_fallback.invoke({"ticket": "怎么改地址"}).category == "question"
|
||||
|
||||
return robust.invoke({"ticket": "我要退款,很急"})
|
||||
|
||||
|
||||
# ---------- 3. create_agent:工具调用 + 调用次数限制 ----------
|
||||
class ToolCallingFake(GenericFakeChatModel):
|
||||
"""假模型不支持 bind_tools,这里直接返回自身;真实模型会把工具定义发给接口。"""
|
||||
def bind_tools(self, tools, **kwargs):
|
||||
return self
|
||||
|
||||
|
||||
@tool
|
||||
def get_order_status(order_id: str) -> str:
|
||||
"""查询订单的物流状态。order_id 形如 A1001。"""
|
||||
return {"A1001": "已发货,预计明天送达"}.get(order_id, "订单不存在")
|
||||
|
||||
|
||||
def demo_agent() -> list:
|
||||
model = ToolCallingFake(messages=iter([
|
||||
AIMessage(content="", tool_calls=[{"name": "get_order_status", "args": {"order_id": "A1001"}, "id": "c1"}]),
|
||||
AIMessage(content="您的订单 A1001 已发货,预计明天送达。"),
|
||||
]))
|
||||
agent = create_agent(
|
||||
model,
|
||||
tools=[get_order_status],
|
||||
system_prompt="你是售后客服,查询订单要用工具。",
|
||||
middleware=[ModelCallLimitMiddleware(run_limit=5)], # 防止无限循环
|
||||
)
|
||||
out = agent.invoke({"messages": [{"role": "user", "content": "A1001 到哪了"}]})
|
||||
return out["messages"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("LCEL:", demo_lcel())
|
||||
t = demo_structured()
|
||||
print("结构化输出:", t)
|
||||
assert t.category == "refund" and t.urgent
|
||||
msgs = demo_agent()
|
||||
for m in msgs:
|
||||
print(f"{type(m).__name__:12s}", m.content or getattr(m, "tool_calls", ""))
|
||||
assert [type(m).__name__ for m in msgs] == ["HumanMessage", "AIMessage", "ToolMessage", "AIMessage"]
|
||||
@@ -4,12 +4,39 @@ title: LangChain
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [langchain]
|
||||
tags: []
|
||||
tags: [LangChain, LCEL, 结构化输出, Agent]
|
||||
week: [6, 11]
|
||||
---
|
||||
|
||||
# LangChain
|
||||
|
||||
模型与消息、LCEL、工具、结构化输出。
|
||||
大模型应用框架。BOSS直聘 2026-10-23 的调研中约 9 个岗位提到 LangChain / LangGraph / AutoGen / LlamaIndex。注意 1.x 和 0.x 差异很大,学习时以官方文档为准。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
## 学习路线
|
||||
|
||||
| 顺序 | 课程 | 级别 | 预计 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | [LangChain 1.x:模型和消息、LCEL、结构化输出、create_agent 和中间件](01-lcel-structured-agent.md) | 进阶 | 3 小时 |
|
||||
|
||||
## 企业场景
|
||||
|
||||
- [工单自动分类服务:结构化输出、批处理、重试降级和人工复核](scenarios/ticket-triage.md)
|
||||
|
||||
## 面试题
|
||||
|
||||
- [LangChain 面试题](interview.md)
|
||||
|
||||
## 示例代码
|
||||
|
||||
- [examples/lc-basics](examples/lc-basics/):LCEL 链、`PydanticOutputParser` + `with_retry` + `with_fallbacks`、`create_agent` 工具调用 + `ModelCallLimitMiddleware`(假模型离线运行)
|
||||
|
||||
## 在 16 周计划中
|
||||
|
||||
- [第 6 周:LangChain 对照实现](../../../plan/week06-rag-2.md)、[第 11 周:LangGraph 多 Agent](../../../plan/week11-agent-m2-langgraph.md)
|
||||
|
||||
## 官方资料
|
||||
|
||||
以官方文档为准:
|
||||
|
||||
- https://docs.langchain.com/oss/python/langchain/overview
|
||||
- 1.0 迁移说明:https://docs.langchain.com/oss/python/migrate/langchain-v1
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
id: ai.langchain.interview
|
||||
title: LangChain 面试题
|
||||
type: interview
|
||||
domain: ai
|
||||
stack: [langchain]
|
||||
tags: [LangChain, LCEL, 结构化输出, 面试]
|
||||
level: intermediate
|
||||
related: [ai.langchain.lcel-structured-agent]
|
||||
week: [6, 11, 16]
|
||||
---
|
||||
|
||||
# LangChain 面试题
|
||||
|
||||
## 基础
|
||||
|
||||
### 【基础】LangChain 是什么?主要组件有哪些?
|
||||
|
||||
**参考答案**:构建大模型应用的框架,提供统一的模型接口、消息、提示词模板、输出解析、工具、检索组件、Agent。1.x 中 Agent 统一为 `create_agent`(基于 LangGraph),并通过中间件扩展。
|
||||
|
||||
**追问**:LangChain、LangGraph、LangSmith 的关系?→ LangChain 提供组件和高层 Agent;LangGraph 是底层的有状态编排框架;LangSmith 是追踪、评测和监控平台。
|
||||
|
||||
### 【基础】LCEL 是什么?
|
||||
|
||||
**参考答案**:LangChain 表达式语言,用 `|` 把 Runnable 组件组合成链,统一支持同步 / 异步、流式、批处理,以及重试、备用、并行等能力。
|
||||
|
||||
**追问**:`batch` 和循环调用 `invoke` 有什么区别?→ `batch` 会并发执行(可以用 `max_concurrency` 控制并发数),速度更快;循环是串行的。
|
||||
|
||||
### 【基础】怎么让模型输出结构化数据?
|
||||
|
||||
**参考答案**:优先使用模型原生的结构化输出(`with_structured_output(Schema)`,底层用工具调用或 JSON Schema 模式);不支持时用提示词 + `PydanticOutputParser`。无论哪种都要校验,失败时重试或降级。
|
||||
|
||||
**追问**:Schema 设计要注意什么?→ 用枚举限制取值、字段加描述、可选字段明确、不要嵌套太深;字段名和描述模型都会读。
|
||||
|
||||
## 进阶
|
||||
|
||||
### 【进阶】LangChain 1.x 和 0.x 有什么主要变化?
|
||||
|
||||
**参考答案**:Agent 统一为 `create_agent`,基于 LangGraph 构建,支持检查点、流式和人工介入;引入中间件机制(调用限制、摘要、人工审批、备用模型等);旧的 `AgentExecutor`、`LLMChain` 等移到 `langchain-classic`;核心包更精简。具体以官方迁移文档为准。
|
||||
|
||||
**追问**:项目里遇到过版本问题吗?→ 按实际经历讲(例如教程是 0.x 写法、包版本不匹配),以及怎么解决(锁定版本、读迁移文档、写测试)。
|
||||
|
||||
### 【进阶】怎么做模型的重试和降级?
|
||||
|
||||
**参考答案**:Runnable 的 `with_retry` 处理临时错误;`with_fallbacks` 在主链失败后切换到备用模型或提供方;1.x 的 Agent 也可以使用备用模型中间件。降级的最后一层通常是“返回友好提示 / 转人工”。
|
||||
|
||||
**追问**:备用模型效果差一些怎么办?→ 评测备用模型在关键用例上的表现,可以只对部分功能降级;记录降级发生的比例并告警。
|
||||
|
||||
## 场景题
|
||||
|
||||
### 【场景题】做一个工单自动分类服务,你会怎么设计?
|
||||
|
||||
**参考答案**:结构化输出(枚举类别、格式校验)+ 重试 + 备用提供方;低置信度人工复核和抽检;异步执行不阻塞工单创建;历史数据批处理控制并发、断点续跑;记录模型和提示词版本;评测集验证准确率。详见[工单分类场景](scenarios/ticket-triage.md)。
|
||||
|
||||
**追问**:怎么衡量分类效果?→ 用人工标注的样本计算准确率和各类别的召回率,特别关注高风险类别(投诉)的召回。
|
||||
|
||||
### 【场景题】团队里有人说“LangChain 太重了,不如直接调 SDK”,你怎么看?
|
||||
|
||||
**参考答案**:各有适用场景。简单调用、单一模型提供方、追求可控性,直接用 SDK;需要多提供方切换、检索 / 工具 / Agent 生态、中间件和 LangGraph 编排能力时,LangChain 能节省大量工作。关键是封装要适度,并锁定版本、写测试应对版本变化。
|
||||
|
||||
**追问**:如果用 SDK,怎么保留切换模型的能力?→ 自己定义一层薄的模型接口(或使用 OpenAI 兼容接口),业务代码不直接依赖某个厂商的 SDK。
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
id: ai.langchain.scenario.ticket-triage
|
||||
title: 工单自动分类服务:结构化输出、批处理、重试降级和人工复核
|
||||
type: scenario
|
||||
domain: ai
|
||||
stack: [langchain, fastapi, postgresql]
|
||||
tags: [结构化输出, 批处理, 重试, 降级, 人工复核, 成本]
|
||||
level: intermediate
|
||||
prerequisites: [ai.langchain.lcel-structured-agent]
|
||||
est_hours: 2.5
|
||||
related: [ai.langchain.interview, ai.llm-serving.index]
|
||||
week: [6, 11]
|
||||
---
|
||||
|
||||
# 工单自动分类服务:结构化输出、批处理、重试降级和人工复核
|
||||
|
||||
## 业务背景与问题
|
||||
|
||||
客服每天收到约 5000 张工单,人工分派到退款、技术、物流、投诉等 8 个队列,平均分派耗时 20 分钟。希望用大模型自动分类、判断紧急程度、提取订单号。试运行问题:
|
||||
|
||||
1. 模型偶尔输出“类别:退款”这样的自然语言,解析失败;
|
||||
2. 模型给出了不存在的类别“售后”;
|
||||
3. 高峰期模型接口限流,工单堆积;
|
||||
4. 历史 50 万张工单要回填分类,逐条调用太慢、太贵;
|
||||
5. 分类错误的工单被分到错误的队列,没人发现。
|
||||
|
||||
## 约束
|
||||
|
||||
- 分派延迟目标:1 分钟内。
|
||||
- 分类结果要可追溯(模型版本、提示词版本)。
|
||||
|
||||
## 方案设计
|
||||
|
||||
1. **结构化输出**:用 Schema 定义输出(类别枚举、紧急程度、订单号格式),优先使用模型的原生结构化输出(`with_structured_output`),并用 pydantic 校验;校验失败重试一次,仍失败进入“待人工分派”队列。
|
||||
2. **置信度和人工复核**:输出中包含置信度(或用规则判断:订单号缺失、类别为“其他”),低置信度进入人工复核;每天抽检 2% 自动分类的工单。
|
||||
3. **重试和降级**:限流 / 超时用指数退避重试;主提供方持续失败时切换备用提供方;都失败时进入人工队列,不阻塞工单创建。
|
||||
4. **批处理**:历史回填走离线任务,用 `batch` 控制并发(或厂商的批处理接口,通常更便宜,以厂商文档为准),按天分片、断点续跑。
|
||||
5. **可追溯**:每条结果记录模型名、提示词版本、原始输出、耗时。
|
||||
|
||||
## 关键代码
|
||||
|
||||
```python
|
||||
class Triage(BaseModel):
|
||||
category: Literal["refund", "tech", "logistics", "complaint", "invoice", "account", "presale", "other"]
|
||||
urgency: Literal["low", "normal", "high"]
|
||||
order_id: str | None = Field(default=None, pattern=r"^A\d{4,}$")
|
||||
confidence: float = Field(ge=0, le=1)
|
||||
|
||||
|
||||
classifier = (
|
||||
prompt | primary_model.with_structured_output(Triage)
|
||||
).with_retry(stop_after_attempt=2).with_fallbacks([prompt | backup_model.with_structured_output(Triage)])
|
||||
|
||||
|
||||
async def triage(ticket: Ticket) -> Triage | None:
|
||||
try:
|
||||
result = await classifier.ainvoke({"title": ticket.title, "body": ticket.body[:2000]})
|
||||
except Exception:
|
||||
await queue.put_manual(ticket.id, reason="模型失败")
|
||||
return None
|
||||
if result.confidence < 0.7 or result.category == "other":
|
||||
await queue.put_review(ticket.id, result)
|
||||
await save_result(ticket.id, result, model=MODEL_NAME, prompt_version=PROMPT_VERSION)
|
||||
return result
|
||||
|
||||
|
||||
# 历史回填:控制并发
|
||||
results = await classifier.abatch(inputs, config={"max_concurrency": 8}, return_exceptions=True)
|
||||
```
|
||||
|
||||
基础写法的可运行示例:[examples/lc-basics](../examples/lc-basics/)。
|
||||
|
||||
## 取舍
|
||||
|
||||
| 选择 | 优点 | 缺点 |
|
||||
| --- | --- | --- |
|
||||
| **大模型分类(本方案)** | 新增类别只改提示词和 Schema;能同时提取信息 | 有成本和延迟;需要评测和复核 |
|
||||
| 传统文本分类模型 | 便宜、快 | 需要标注数据训练,类别变化要重新训练 |
|
||||
| 关键词规则 | 简单可解释 | 覆盖率低 |
|
||||
| 规则 + 大模型 | 明确的用规则(便宜),模糊的用模型 | 两套逻辑要维护 |
|
||||
|
||||
## 易错点
|
||||
|
||||
- 让模型自由输出类别名,再用字符串匹配。
|
||||
- 模型失败时阻塞工单创建:分类应该是异步的,失败可降级为人工。
|
||||
- 回填时并发过高触发限流,或者没有断点续跑,失败后从头再来。
|
||||
- 模型自报的置信度并不可靠,要和评测集的实际准确率对照校准。
|
||||
- 换模型或改提示词后没有跑评测集。
|
||||
|
||||
## 面试怎么讲
|
||||
|
||||
> “工单分类我用结构化输出,类别是枚举、订单号有格式校验,失败重试一次,再失败进人工队列,不阻塞工单创建。主提供方限流时自动切换备用提供方。低置信度和‘其他’类进入人工复核,每天再抽检 2%。历史回填用批处理控制并发、断点续跑。每条结果记录模型和提示词版本,改动前后跑评测集对比准确率。”
|
||||
Reference in new issue
Block a user