chatgpt to production

从 ChatGPT 原型到生产系统:迁移避坑指南

原型 vs 生产:到底差在哪 在 ChatGPT 界面里跑通一个 demo,和生产环境服务千万用户,中间隔着一条鸿沟。原型阶段你不需要关心的事情,在生产中每一个都会变成事故: 维度 原型阶段 生产阶段 延迟 手动等待,几秒可接受 P99 < 3s,超时降级 可用性 失败就重试 99.9% SLA,多活容灾 成本 不关心 每月 API 账单可预测 安全 Prompt 直接硬编码 密钥管理、输入消毒、输出过滤 可观测性 print 大法 结构化日志、指标、链路追踪 一致性 每次回答不同 温度控制、缓存、确定性输出 核心认知:原型验证的是"能不能做",生产回答的是"能不能稳定做、能不能赚钱做、能不能安全做"。 API 选型:不只是选个模型 模型选择决策树 def choose_model(task: str, latency_req: float, budget: float) -> str: if task in ["简单分类", "抽取"] and latency_req < 0.5: return "gpt-4o-mini" # 快且便宜 if task in ["复杂推理", "代码生成"] and budget > 0.05: return "gpt-4o" # 能力优先 if task in ["长文档总结"] and budget < 0.02: return "gpt-4o-mini" # 上下文够长,成本可控 return "gpt-4o-mini" # 默认保守 成本估算公式 单次调用成本 = (input_tokens × input_price) + (output_tokens × output_price) 月度预算 = 日均调用量 × 平均(input_tokens + output_tokens) × 30 × 单价 # GPT-4o (2026 pricing) # Input: $2.50 / 1M tokens # Output: $10.00 / 1M tokens # 一个典型客服对话: ~500 input + ~200 output # 单次成本 ≈ $0.00325 # 日均 10000 次 → 月成本 ≈ $975 多模型路由策略 生产环境不要只用一个模型。根据请求复杂度路由到不同模型: ...

2026-06-25 · 4 min · 754 words · AI 实战派
agent memory implementation

Agent 记忆系统实现:从短期到长期

为什么 Agent 需要记忆 LLM 本身是无状态的——每次调用都是独立的。对话历史塞进 Context Window 是最简单的"记忆",但有致命缺陷:token 成本线性增长、上下文长度有上限、无法跨会话持久化。一个真正有用的 Agent 需要像人一样拥有多层次记忆系统。 四种记忆类型 记忆类型 类比人类 生命周期 存储介质 示例 工作记忆 工作台上的文件 当前任务 Redis/内存 当前对话上下文、临时变量 情景记忆 个人经历 天~月 向量数据库 “用户上周问了XX问题” 语义记忆 知识事实 长期 向量数据库 + 图数据库 “用户是Java开发者” 程序记忆 肌肉记忆 长期 规则库/技能库 “调用API的固定流程” 架构设计 ┌──────────────────────────────────────────────────┐ │ Agent 记忆系统 │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 工作记忆 │ │ 情景记忆 │ │ 语义记忆 │ │ │ │ (Redis) │ │(Qdrant) │ │(Qdrant) │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ │ └──────────────┼──────────────┘ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 记忆管理器 │ │ │ │ (Memory Mgr) │ │ │ └──────┬───────┘ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 程序记忆 │ │ │ │ (规则库) │ │ │ └──────────────┘ │ └──────────────────────────────────────────────────┘ 核心实现 1. 记忆数据结构 from dataclasses import dataclass, field from datetime import datetime from enum import Enum import uuid class MemoryType(Enum): WORKING = "working" EPISODIC = "episodic" SEMANTIC = "semantic" PROCEDURAL = "procedural" @dataclass class Memory: id: str = field(default_factory=lambda: str(uuid.uuid4())) type: MemoryType = MemoryType.EPISODIC content: str = "" embedding: list[float] = field(default_factory=list) metadata: dict = field(default_factory=dict) importance: float = 0.5 # 0-1, 影响遗忘策略 created_at: datetime = field(default_factory=datetime.now) last_accessed: datetime = field(default_factory=datetime.now) access_count: int = 0 decay_factor: float = 1.0 # 随时间衰减 2. 记忆写入 import redis import json from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct class MemoryStore: def __init__(self): self.redis = redis.Redis(host='localhost', port=6379, db=0) self.qdrant = QdrantClient(host='localhost', port=6333) self._init_collections() def _init_collections(self): """初始化向量集合""" for collection in ["episodic", "semantic"]: if not self.qdrant.collection_exists(collection): self.qdrant.create_collection( collection_name=collection, vectors_config=VectorParams(size=1024, distance=Distance.COSINE) ) async def write(self, memory: Memory): """写入记忆""" if memory.type == MemoryType.WORKING: # 工作记忆写入 Redis,设置过期时间 key = f"working:{memory.id}" self.redis.setex( key, 3600, # 1小时过期 json.dumps({ "content": memory.content, "metadata": memory.metadata }) ) elif memory.type in (MemoryType.EPISODIC, MemoryType.SEMANTIC): # 情景/语义记忆写入向量库 self.qdrant.upsert( collection_name=memory.type.value, points=[PointStruct( id=memory.id, vector=memory.embedding, payload={ "content": memory.content, "metadata": memory.metadata, "importance": memory.importance, "created_at": memory.created_at.isoformat(), "last_accessed": memory.last_accessed.isoformat(), "access_count": memory.access_count } )] ) async def search(self, query_vector, memory_type: MemoryType, top_k=5): """检索记忆""" if memory_type == MemoryType.WORKING: # 工作记忆:返回所有未过期的 keys = self.redis.keys("working:*") results = [] for key in keys: data = json.loads(self.redis.get(key)) results.append(data) return results # 向量检索 hits = self.qdrant.search( collection_name=memory_type.value, query_vector=query_vector, limit=top_k, score_threshold=0.7 ) return [hit.payload for hit in hits] 3. 记忆检索与排序 class MemoryRetriever: """记忆检索器:综合相关性、重要性、时效性""" def __init__(self, store: MemoryStore): self.store = store async def retrieve(self, query: str, query_vector: list, context: dict = None) -> list[dict]: # 并行检索三种记忆 import asyncio tasks = [ self.store.search(query_vector, MemoryType.WORKING), self.store.search(query_vector, MemoryType.EPISODIC, top_k=5), self.store.search(query_vector, MemoryType.SEMANTIC, top_k=5), ] working, episodic, semantic = await asyncio.gather(*tasks) # 融合排序 all_memories = [] for mem in episodic + semantic: score = self._compute_score(mem, query_vector) all_memories.append((mem, score)) all_memories.sort(key=lambda x: x[1], reverse=True) # 返回 top-K + 工作记忆 return { "working": working, "long_term": [m[0] for m in all_memories[:5]] } def _compute_score(self, mem, query_vector): """综合评分 = 相关性 × 时效性 × 重要性 × 访问频率""" import numpy as np from datetime import datetime, timedelta # 相关性(向量相似度,假设payload中存了) relevance = mem.get("score", 0.5) # 时效性衰减 created = datetime.fromisoformat(mem["created_at"]) days_old = (datetime.now() - created).days recency = np.exp(-days_old / 30) # 30天半衰期 # 重要性 importance = mem.get("importance", 0.5) # 访问频率(越常访问越重要) access_factor = np.log1p(mem.get("access_count", 0)) return relevance * (0.3 + 0.4 * recency) * importance * (1 + 0.1 * access_factor) 4. 遗忘机制 class ForgettingManager: """遗忘机制:模拟人类遗忘曲线""" async def decay_memories(self): """定期执行,降低旧记忆的重要性""" from datetime import datetime, timedelta cutoff = datetime.now() - timedelta(days=7) # 在向量库中批量更新 self.store.qdrant.set_payload( collection_name="episodic", payload={"decay_factor": 0.8}, # 每次衰减20% filters={ "must": [ {"field": "last_accessed", "range": {"lt": cutoff.isoformat()}} ] } ) async def cleanup_memories(self): """删除重要性低于阈值的记忆""" self.store.qdrant.delete( collection_name="episodic", points_selector={ "filter": { "must": [ {"field": "importance", "range": {"lt": 0.1}}, {"field": "decay_factor", "range": {"lt": 0.3}} ] } } ) 实战建议 重要性评分策略:不要让 Agent 自己打分(LLM 评分不稳定)。用规则:用户明确要求记住的 → 1.0,任务关键信息 → 0.8,普通对话 → 0.3。 ...

2026-06-24 · 3 min · 617 words · AI 实战派
ai observability guide

AI 可观测性实践:让你的 Agent 透明可见

为什么 AI 应用需要可观测性 传统应用监控关注 CPU、内存、QPS。AI 应用需要关注:LLM 调用了几次?每次延迟多少?Token 消耗多少?检索结果是否相关?Agent 的推理链路是什么?没有可观测性,AI 应用就是黑盒,出问题只能猜。 三支柱模型 支柱 传统应用 AI 应用 工具 Tracing 请求链路 LLM 调用链 + Agent 推理步骤 + 检索过程 LangSmith/Langfuse Logging 日志 Prompt/Response/中间状态 结构化日志 Metrics QPS/延迟 Token消耗/检索召回率/幻觉率 Prometheus+Grafana 关键指标定义 from dataclasses import dataclass from typing import Optional @dataclass class LLMCallMetrics: # 延迟指标 time_to_first_token: float # 首 token 延迟(ms) total_latency: float # 总延迟(ms) # 成本指标 prompt_tokens: int completion_tokens: int total_cost: float # 质量指标 model_name: str temperature: float finish_reason: str # 检索指标(RAG场景) retrieval_count: Optional[int] = None retrieval_relevance: Optional[float] = None context_utilization: Optional[float] = None @dataclass class AgentTrace: trace_id: str spans: list[dict] # 每个步骤一个 span # span 结构: name, start, end, input, output, metadata Langfuse 集成实现 from langfuse import Langfuse from langfuse.decorators import langfuse_context, observe import functools, time langfuse = Langfuse( public_key="pk-lf-xxx", secret_key="sk-lf-xxx", host="https://your-langfuse.com" ) def trace_llm(name="llm_call"): def decorator(func): @functools.wraps(func) async def wrapper(*args, **kwargs): start = time.time() span = langfuse_context.start_span(name=name, input=kwargs) try: result = await func(*args, **kwargs) ttft = time.time() - start span.end( output=result, metadata={"ttft_ms": ttft * 1000, "model": kwargs.get("model", "unknown")} ) langfuse_context.update_current_observation( usage={ "prompt_tokens": result.get("usage", {}).get("prompt_tokens", 0), "completion_tokens": result.get("usage", {}).get("completion_tokens", 0), "total_tokens": result.get("usage", {}).get("total_tokens", 0), }, metadata={"latency_ms": (time.time() - start) * 1000} ) return result except Exception as e: span.end(level="ERROR", status_message=str(e)) raise return wrapper return decorator @observe(name="rag_pipeline") async def rag_pipeline(query: str): docs = await retrieve(query) answer = await generate(query, docs) return answer @observe(name="retrieve") async def retrieve(query: str): results = await vector_search(query) return {"query": query, "docs": results} @trace_llm(name="generate") async def generate(query: str, docs: list): resp = await llm_client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": f"Q: {query}\nContext: {docs}"}] ) return resp 工具对比 工具 部署方式 核心优势 适用场景 价格 LangSmith SaaS LangChain 深度集成 LangChain 用户 $39/月起 Langfuse 自部署/SaaS 开源,框架无关 通用 AI 应用 免费/自部署 Phoenix 自部署 Arize 出品,评估强 需要评估体系 开源 OpenTelemetry 自部署 标准化,通用 已有 OTel 体系 免费 选型建议:不用 LangChain 选 Langfuse(开源、框架无关),用 LangChain 选 LangSmith(集成最好),需要深度评估选 Phoenix。 ...

2026-06-24 · 2 min · 333 words · AI 实战派
llm streaming implementation

LLM 流式输出实现:SSE 与 WebSocket

为什么需要流式输出 LLM 生成完整回答可能需要 5-15 秒。用户盯着空白屏幕等待体验极差。流式输出让用户像看打字机一样实时看到生成内容,首 token 延迟降到 200-500ms,体感速度提升 10 倍。 SSE vs WebSocket 对比 维度 SSE (Server-Sent Events) WebSocket 协议 HTTP/1.1 或 HTTP/2 独立协议(ws://) 方向 服务器到客户端(单向) 双向 自动重连 浏览器内置 需手动实现 代理兼容性 好(就是HTTP) 差(需特殊配置) 适合场景 LLM 流式输出(推荐) 实时对话/多Agent通信 实现复杂度 低 中 结论:LLM 流式输出首选 SSE,需要双向通信才用 WebSocket。 SSE 后端实现(FastAPI) from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse import httpx, json, asyncio app = FastAPI() LLM_API_URL = "https://api.openai.com/v1/chat/completions" @app.post("/api/chat/stream") async def chat_stream(request: Request): body = await request.json() async def event_generator(): headers = { "Authorization": f"Bearer {body['api_key']}", "Content-Type": "application/json" } payload = { "model": body["model"], "messages": body["messages"], "stream": True, "max_tokens": 4096 } try: async with httpx.AsyncClient(timeout=120) as client: async with client.stream("POST", LLM_API_URL, headers=headers, json=payload) as resp: async for line in resp.aiter_lines(): if not line.startswith("data: "): continue data = line[6:] if data.strip() == "[DONE]": yield "data: " + json.dumps({"type": "done"}) + "\n\n" break chunk = json.loads(data) delta = chunk["choices"][0]["delta"] if "content" in delta: token = delta["content"] yield "data: " + json.dumps({"type": "token", "content": token}) + "\n\n" except httpx.ReadTimeout: yield "data: " + json.dumps({"type": "error", "msg": "LLM timeout"}) + "\n\n" except Exception as e: yield "data: " + json.dumps({"type": "error", "msg": str(e)}) + "\n\n" return StreamingResponse( event_generator(), media_type="text/event-stream", headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no", "Connection": "keep-alive"} ) SSE 前端实现 class SSEClient { constructor(url) { this.url = url; this.controller = null; } async stream(messages, onToken, onDone, onError) { this.controller = new AbortController(); try { const resp = await fetch(this.url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages }), signal: this.controller.signal }); const reader = resp.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop(); for (const line of lines) { if (!line.startsWith('data: ')) continue; const data = JSON.parse(line.slice(6)); if (data.type === 'token') onToken(data.content); else if (data.type === 'done') onDone(); else if (data.type === 'error') onError(data.msg); } } } catch (e) { if (e.name !== 'AbortError') onError(e.message); } } abort() { this.controller?.abort(); } } // 使用 const client = new SSEClient('/api/chat/stream'); let fullText = ''; await client.stream( [{ role: 'user', content: '解释 RAG 架构' }], (token) => { fullText += token; renderMarkdown(fullText); }, () => console.log('完成'), (err) => console.error('错误:', err) ); WebSocket 方案 需要双向通信(用户中途打断、实时修正)时使用: ...

2026-06-24 · 3 min · 526 words · AI 实战派
prompt management platform

Prompt 管理平台搭建指南

为什么需要 Prompt 管理平台 当团队有 3 个以上 LLM 应用时,Prompt 管理就会失控:Prompt 散落在代码里、改一个字要重新部署、无法 A/B 测试、没有版本回滚。Prompt 管理平台把 Prompt 当作独立资产来管理。 核心功能需求 功能模块 说明 优先级 Prompt CRUD 创建/读取/更新/删除 Prompt P0 版本管理 每次修改生成新版本,支持回滚 P0 变量模板 支持 {{variable}} 变量插值 P0 A/B 测试 流量分割对比不同 Prompt 效果 P1 权限控制 团队成员角色与审批流 P1 效果监控 Prompt 调用的成功率/延迟/成本 P1 批量测试 用测试集自动评估 Prompt P2 架构设计 +----------------------------------------------+ | Prompt 管理平台架构 | | | | +------------+ +------------------+ | | | Web UI | | API Gateway | | | | (React) | | (FastAPI) | | | +-----+------+ +-------+----------+ | | | | | | +-----+-----------------+----------------+ | | | Prompt Service Layer | | | | CRUD | Version | AB Test | RBAC | | | +-------------------+--------------------+ | | | | | +---------+---------+---------+----------+ | | |PostgreSQL| Redis缓存 |S3存储 | | | |(元数据) | (热Prompt) |(备份) | | | +---------+--------------+----------+ | +----------------------------------------------+ 数据库设计 -- Prompt 主表 CREATE TABLE prompts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(128) NOT NULL UNIQUE, description TEXT, category VARCHAR(64), current_version_id UUID REFERENCES prompt_versions(id), created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- Prompt 版本表 CREATE TABLE prompt_versions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), prompt_id UUID NOT NULL REFERENCES prompts(id) ON DELETE CASCADE, version INT NOT NULL, content TEXT NOT NULL, variables JSONB DEFAULT '[]', model_config JSONB DEFAULT '{}', change_log TEXT, created_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMP DEFAULT NOW(), UNIQUE(prompt_id, version) ); -- A/B 测试表 CREATE TABLE ab_tests ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(128) NOT NULL, prompt_id UUID NOT NULL REFERENCES prompts(id), variant_a_version_id UUID NOT NULL REFERENCES prompt_versions(id), variant_b_version_id UUID NOT NULL REFERENCES prompt_versions(id), traffic_split JSONB DEFAULT '{"a": 50, "b": 50}', status VARCHAR(16) DEFAULT 'running', metrics JSONB DEFAULT '{}', created_at TIMESTAMP DEFAULT NOW(), ended_at TIMESTAMP ); 核心 API 实现 from fastapi import FastAPI, HTTPException, Depends from pydantic import BaseModel import asyncpg, json app = FastAPI() class PromptCreate(BaseModel): name: str description: str = "" content: str variables: list[dict] = [] model_config: dict = {} class PromptUpdate(BaseModel): content: str change_log: str = "" variables: list[dict] = [] @app.post("/api/prompts") async def create_prompt(data: PromptCreate, db=Depends(get_db)): async with db.transaction(): prompt = await db.fetchrow( "INSERT INTO prompts (name, description) VALUES ($1, $2) RETURNING *", data.name, data.description ) version = await db.fetchrow( "INSERT INTO prompt_versions (prompt_id, version, content, variables, model_config, created_by) VALUES ($1, 1, $2, $3, $4, $5) RETURNING *", prompt["id"], data.content, json.dumps(data.variables), json.dumps(data.model_config), current_user_id ) await db.execute( "UPDATE prompts SET current_version_id = $1 WHERE id = $2", version["id"], prompt["id"] ) return {"prompt": dict(prompt), "version": dict(version)} @app.put("/api/prompts/{prompt_id}") async def update_prompt(prompt_id: str, data: PromptUpdate, db=Depends(get_db)): async with db.transaction(): current = await db.fetchrow( "SELECT version FROM prompt_versions WHERE prompt_id = $1 ORDER BY version DESC LIMIT 1", prompt_id ) new_version = (current["version"] + 1) if current else 1 version = await db.fetchrow( "INSERT INTO prompt_versions (prompt_id, version, content, variables, change_log, created_by) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *", prompt_id, new_version, data.content, json.dumps(data.variables), data.change_log, current_user_id ) await db.execute( "UPDATE prompts SET current_version_id = $1, updated_at = NOW() WHERE id = $2", version["id"], prompt_id ) return {"version": dict(version)} @app.post("/api/prompts/{prompt_id}/rollback/{version}") async def rollback_prompt(prompt_id: str, version: int, db=Depends(get_db)): target = await db.fetchrow( "SELECT id FROM prompt_versions WHERE prompt_id = $1 AND version = $2", prompt_id, version ) if not target: raise HTTPException(404, "Version not found") await db.execute( "UPDATE prompts SET current_version_id = $1 WHERE id = $2", target["id"], prompt_id ) return {"rolled_back_to": version} A/B 测试实现 import random class ABTestRouter: def __init__(self, db, redis): self.db = db self.redis = redis async def get_variant(self, test_id: str, user_id: str) -> str: cache_key = f"ab:{test_id}:{user_id}" cached = await self.redis.get(cache_key) if cached: return cached test = await self.db.fetchrow( "SELECT * FROM ab_tests WHERE id = $1 AND status = 'running'", test_id ) if not test: return "a" split = json.loads(test["traffic_split"]) variant = "a" if random.random() * 100 < split["a"] else "b" await self.redis.setex(cache_key, 86400, variant) return variant async def record_metric(self, test_id, variant, metric, value): await self.db.execute( "INSERT INTO ab_metrics (test_id, variant, metric, value) VALUES ($1, $2, $3, $4)", test_id, variant, metric, value ) 与现有工具对比 工具 优点 缺点 适用场景 LangSmith LangChain 生态集成好 绑定 LangChain,贵 已用 LangChain 的团队 PromptHub UI 好用,有协作功能 自定义能力弱 非技术团队 自建平台 完全可控,可定制 开发成本高 大团队/特殊需求 建议:团队 < 5 人且用 LangChain,直接上 LangSmith。团队 > 10 人或有特殊需求,自建平台 ROI 更高。 ...

2026-06-24 · 4 min · 720 words · AI 实战派
rag production deploy

RAG 系统生产部署全流程

生产级 RAG 架构总览 生产环境中的 RAG 不是简单的"文档切块 + 向量搜索 + LLM 生成"三步走,而是一个包含数据接入、预处理、索引、检索、重排、生成、后处理等多环节的完整系统。 ┌─────────────────────────────────────────────────────────────────┐ │ RAG 生产架构 │ ├────────────────┬────────────────────────────────────────────────┤ │ 数据层 │ 文档加载 → 清洗 → 分块 → Embedding → 向量库 │ │ 检索层 │ 混合检索(向量+BM25) → 重排 → 上下文组装 │ │ 生成层 │ Prompt 模板 → LLM 调用 → 流式输出 → 后处理 │ │ 基础设施层 │ API网关 → 负载均衡 → 缓存 → 监控 → 日志 │ └────────────────┴────────────────────────────────────────────────┘ 向量数据库选型 三大主流向量库对比: 维度 Milvus Weaviate Qdrant 部署复杂度 高(依赖etcd/MinIO) 中 低(单二进制) 索引算法 HNSW/IVF/DiskANN HNSW HNSW 混合检索 需配合ES 内置BM25 内置稀疏向量 动态schema 支持 支持 支持 集群方案 成熟 支持 支持(一致性哈希) 性能(1M向量,768d) ~1200 QPS ~800 QPS ~1500 QPS 社区活跃度 高 中 高 适用场景 大规模(亿级) 中规模+GraphQL 中小规模+低延迟 选型建议: ...

2026-06-24 · 3 min · 565 words · AI 实战派
multi language llm deploy

多语言 LLM 部署指南

多语言 LLM 的核心挑战 部署一个支持多语言的 LLM 应用不是简单地把 prompt 翻译成不同语言。真正的挑战在于: Embedding 跨语言对齐:中文问题和英文文档之间的语义匹配 文化适配:同一个概念在不同文化语境下含义不同 混合语言输入:用户一句话中混用中英文(“帮我看看这个 function 的 implementation”) 成本控制:多语言模型通常更大更贵 语言检测与路由 第一步是准确检测用户输入的语言,用于后续路由: import langdetect from langdetect import detect, DetectorFactory # 设置种子保证检测结果稳定 DetectorFactory.seed = 42 def detect_language(text: str) -> str: """检测文本语言,返回 ISO 639-1 代码""" # 预处理:去除代码块、URL 等干扰内容 import re clean = re.sub(r'```.*?```', '', text, flags=re.DOTALL) clean = re.sub(r'https?://\S+', '', clean) if len(clean.strip()) < 10: return "en" # 太短,默认英文 try: lang = detect(clean) # 映射到支持的语言集合 supported = {"zh-cn", "zh-tw", "en", "ja", "ko", "fr", "de", "es"} if lang in supported: return lang elif lang.startswith("zh"): return "zh-cn" else: return "en" # 不支持的语言回退到英文 except: return "en" def route_by_language(lang: str, config: dict) -> dict: """根据语言路由到不同的模型/配置""" routing = { "zh-cn": {"model": "qwen2.5-72b", "embedding": "bge-m3", "system_lang": "zh"}, "zh-tw": {"model": "qwen2.5-72b", "embedding": "bge-m3", "system_lang": "zh-tw"}, "en": {"model": "llama-3.1-70b", "embedding": "bge-m3", "system_lang": "en"}, "ja": {"model": "qwen2.5-72b", "embedding": "bge-m3", "system_lang": "ja"}, "default": {"model": "llama-3.1-70b", "embedding": "bge-m3", "system_lang": "en"} } return routing.get(lang, routing["default"]) 多语言 Embedding 选型 模型 维度 支持语言 MTEB 均分 备注 bge-m3 1024 100+ 66.3 推荐,多语言SOTA multilingual-e5-large 1024 100+ 64.8 稳定可靠 paraphrase-multilingual 768 50+ 61.5 轻量级 OpenAI text-embedding-3-large 3072 100+ 65.0 贵但方便 Cohere embed-multilingual-v3 1024 100+ 64.9 API调用 结论:自部署用 bge-m3,API 调用用 OpenAI 或 Cohere。bge-m3 在中英跨语言检索任务上表现最好。 ...

2026-06-24 · 3 min · 557 words · AI 实战派
ai customer service build

AI 客服系统构建指南:从知识库到多轮对话

需求分析:客服系统的核心指标 构建 AI 客服系统前,先明确要解决什么问题。客服系统的价值体现在三个维度: 维度 指标 目标值 衡量方式 效率 自动解决率 ≥60% 无需人工介入的会话占比 效率 平均响应时间 <2s 用户发送到首字的时间 质量 回答准确率 ≥90% 人工抽检评分 质量 用户满意度 ≥4.0/5 会话后评分 成本 单会话成本 <¥0.5 API 调用 + 基础设施 / 会话数 体验 人工转接等待 <30s 转人工后接通时间 客服场景分类 不同场景需要不同的处理策略: from enum import Enum from dataclasses import dataclass class QueryType(Enum): """客服查询类型""" FAQ = "faq" # 常见问题(退货政策、运费说明) TROUBLESHOOT = "trouble" # 故障排查(设备不工作、登录失败) TRANSACTION = "transaction" # 交易查询(订单状态、退款进度) COMPLAINT = "complaint" # 投诉建议 CHITCHAT = "chitchat" # 闲聊(超出业务范围) @dataclass class QueryStrategy: """不同查询类型的处理策略""" query_type: QueryType needs_rag: bool needs_tools: bool max_turns: int can_self_serve: bool escalation_threshold: float # 置信度低于此值则转人工 STRATEGIES = { QueryType.FAQ: QueryStrategy( query_type=QueryType.FAQ, needs_rag=True, needs_tools=False, max_turns=3, can_self_serve=True, escalation_threshold=0.6 ), QueryType.TROUBLESHOOT: QueryStrategy( query_type=QueryType.TROUBLESHOOT, needs_rag=True, needs_tools=True, max_turns=8, can_self_serve=True, escalation_threshold=0.5 ), QueryType.TRANSACTION: QueryStrategy( query_type=QueryType.TRANSACTION, needs_rag=False, needs_tools=True, max_turns=5, can_self_serve=True, escalation_threshold=0.7 ), QueryType.COMPLAINT: QueryStrategy( query_type=QueryType.COMPLAINT, needs_rag=False, needs_tools=False, max_turns=2, can_self_serve=False, escalation_threshold=0.8 ), QueryType.CHITCHAT: QueryStrategy( query_type=QueryType.CHITCHAT, needs_rag=False, needs_tools=False, max_turns=2, can_self_serve=True, escalation_threshold=0.9 ), } 知识库构建 知识库是 AI 客服的核心。垃圾进、垃圾出——知识库的质量直接决定回答质量。 ...

2026-06-24 · 10 min · 1998 words · AI 实战派
chatbot production deploy

聊天机器人生产部署全指南

部署架构设计 生产环境的聊天机器人不是"跑起来就行",它需要处理高并发、保证可用性、支持快速回滚。一个经过验证的部署架构如下: 用户 → CDN/WAF → Nginx (SSL/反向代理) → API Gateway → Agent 服务集群 ↓ Redis (会话) + 向量DB (知识) + LLM API 架构组件职责 组件 职责 推荐方案 CDN/WAF 静态资源加速、DDoS 防护 Cloudflare / 阿里云 CDN Nginx SSL 终止、反向代理、负载均衡 Nginx / OpenResty API Gateway 鉴权、限流、路由 Kong / APISIX Agent 服务 业务逻辑、LLM 调用 FastAPI / Gin 会话存储 对话上下文 Redis Cluster 知识库 向量检索 Milvus / Qdrant 监控 指标采集、告警 Prometheus + Grafana 日志 日志聚合 ELK / Loki Docker 容器化 Dockerfile 最佳实践 # 多阶段构建:分离构建环境和运行环境 FROM python:3.12-slim AS builder WORKDIR /app # 安装依赖(利用 Docker 层缓存) COPY pyproject.toml uv.lock ./ RUN pip install --no-cache-dir uv && \ uv pip install --system --no-cache -r requirements.txt # 复制源码 COPY . . # 运行阶段:更小的镜像 FROM python:3.12-slim AS runtime # 安装运行时依赖 RUN apt-get update && apt-get install -y --no-install-recommends \ curl && \ rm -rf /var/lib/apt/lists/* # 创建非 root 用户 RUN useradd -m -u 1000 appuser WORKDIR /app # 从构建阶段复制 COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages COPY --from=builder /app /app # 健康检查 HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 # 切换用户 USER appuser EXPOSE 8000 # 启动命令 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", \ "--workers", "4", "--loop", "uvloop", "--no-access-log"] Docker Compose 开发环境 version: "3.9" services: agent: build: . ports: - "8000:8000" environment: - REDIS_URL=redis://redis:6379/0 - VECTOR_DB_URL=http://qdrant:6333 - LLM_API_KEY=${LLM_API_KEY} - LOG_LEVEL=debug depends_on: redis: condition: service_healthy qdrant: condition: service_started volumes: - ./app:/app # 开发热重载 restart: unless-stopped redis: image: redis:7-alpine ports: - "6379:6379" command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 5 volumes: - redis-data:/data qdrant: image: qdrant/qdrant:latest ports: - "6333:6333" volumes: - qdrant-data:/qdrant/storage volumes: redis-data: qdrant-data: 镜像优化要点 优化项 效果 方法 多阶段构建 镜像减小 40-60% 构建阶段和运行阶段分离 Slim 基础镜像 减少 200MB+ 用 python:slim 替代 python:full .dockerignore 加快构建 排除 .git, pycache, .venv 等 层缓存优化 加快重建 先 COPY 依赖文件再 COPY 源码 非 root 用户 安全加固 USER 指令切换运行用户 Nginx 反向代理与 SSL Nginx 配置 # /etc/nginx/conf.d/chatbot.conf # 上游 Agent 服务 upstream agent_backend { least_conn; server 10.0.1.10:8000 weight=3; server 10.0.1.11:8000 weight=3; server 10.0.1.12:8000 weight=2; # 健康检查(Nginx Plus 或 OpenResty) keepalive 32; keepalive_timeout 60s; } # HTTP → HTTPS 重定向 server { listen 80; server_name chat.example.com; return 301 https://$server_name$request_uri; } # HTTPS 主服务 server { listen 443 ssl http2; server_name chat.example.com; # SSL 配置 ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 安全头 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options DENY always; # 请求体大小限制(文件上传) client_max_body_size 10M; # SSE 流式输出专用配置 location /api/chat/stream { proxy_pass http://agent_backend; # 关键:禁用缓冲,否则 SSE 不工作 proxy_buffering off; proxy_cache off; # 支持长连接 proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding on; # 超时设置(流式输出需要长超时) proxy_read_timeout 300s; proxy_send_timeout 300s; # 传递客户端信息 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 普通 API location /api/ { proxy_pass http://agent_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 限流 limit_req zone=api burst=20 nodelay; } # 静态资源 location / { root /var/www/chatbot-ui; try_files $uri $uri/ /index.html; # 静态资源缓存 expires 1h; add_header Cache-Control "public, immutable"; } # 健康检查端点 location /health { proxy_pass http://agent_backend/health; access_log off; } } # 限流区域定义(放在 http 块中) # limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; SSL 证书自动续期 # 使用 Let's Encrypt + certbot certbot certonly --webroot -w /var/www/chatbot-ui -d chat.example.com # 自动续期(crontab) 0 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx" 负载均衡配置 Nginx 负载均衡策略 # 策略1: 最少连接(推荐 Agent 场景) upstream agent_backend { least_conn; server 10.0.1.10:8000; server 10.0.1.11:8000; } # 策略2: IP 哈希(会话亲和) upstream agent_backend_sticky { ip_hash; server 10.0.1.10:8000; server 10.0.1.11:8000; } # 策略3: 加权轮询(异构节点) upstream agent_backend_weighted { server 10.0.1.10:8000 weight=3; # 高配机器 server 10.0.1.11:8000 weight=2; # 中配机器 server 10.0.1.12:8000 weight=1; # 低配机器 } # 策略4: 一致性哈希(Nginx Plus 或第三方模块) upstream agent_backend_consistent { hash $arg_session_id consistent; server 10.0.1.10:8000; server 10.0.1.11:8000; } 健康检查与故障转移 # Agent 服务健康检查端点 from fastapi import FastAPI import psutil import asyncio app = FastAPI() @app.get("/health") async def health_check(): """轻量级健康检查""" return {"status": "healthy", "timestamp": asyncio.get_event_loop().time()} @app.get("/health/ready") async def readiness_check(): """就绪检查:验证所有依赖""" checks = { "redis": await check_redis(), "vector_db": await check_vector_db(), "llm_api": await check_llm_api(), "gpu": check_gpu_available(), } all_ready = all(checks.values()) return { "ready": all_ready, "checks": checks, }, 200 if all_ready else 503 async def check_redis() -> bool: try: await redis_client.ping() return True except: return False def check_gpu_available() -> bool: try: import torch return torch.cuda.is_available() except: return True # CPU 模式也算就绪 监控告警 Prometheus 指标采集 from prometheus_client import Counter, Histogram, Gauge, generate_latest from prometheus_client import make_asgi_app import time # 定义指标 REQUEST_COUNT = Counter( 'agent_requests_total', 'Total request count', ['method', 'endpoint', 'status'] ) REQUEST_LATENCY = Histogram( 'agent_request_latency_seconds', 'Request latency', ['endpoint'], buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0] ) FIRST_TOKEN_LATENCY = Histogram( 'agent_first_token_latency_seconds', 'Time to first token', buckets=[0.05, 0.1, 0.25, 0.5, 1.0, 2.0, 5.0] ) ACTIVE_SESSIONS = Gauge( 'agent_active_sessions', 'Number of active sessions' ) LLM_TOKEN_USAGE = Counter( 'agent_llm_tokens_total', 'LLM token usage', ['direction', 'model'] # direction: input/output ) # 中间件 @app.middleware("http") async def metrics_middleware(request, call_next): start = time.time() response = await call_next(request) duration = time.time() - start REQUEST_COUNT.labels( method=request.method, endpoint=request.url.path, status=response.status_code ).inc() REQUEST_LATENCY.labels(endpoint=request.url.path).observe(duration) return response # 挂载 Prometheus 端点 metrics_app = make_asgi_app() app.mount("/metrics", metrics_app) Grafana 告警规则 告警名称 条件 持续时间 严重程度 服务不可用 up == 0 1m Critical 高错误率 rate(agent_requests_total{status=~"5.."}[5m]) / rate(agent_requests_total[5m]) > 0.05 2m Critical P99 延迟高 histogram_quantile(0.99, agent_request_latency_seconds_bucket) > 10 5m Warning 首字延迟高 histogram_quantile(0.95, agent_first_token_latency_seconds_bucket) > 2 5m Warning GPU 利用率高 gpu_utilization > 0.9 10m Warning 会话积压 agent_active_sessions > 500 5m Warning 日志收集 结构化日志 import structlog import json from datetime import datetime # 配置 structlog structlog.configure( processors=[ structlog.contextvars.merge_contextvars, structlog.processors.add_log_level, structlog.processors.TimeStamper(fmt="iso"), structlog.processors.JSONRenderer(), ], wrapper_class=structlog.make_filtering_bound_logger(20), # INFO+ ) logger = structlog.get_logger() # 请求追踪中间件 @app.middleware("http") async def logging_middleware(request, call_next): request_id = request.headers.get("X-Request-ID", str(uuid.uuid4())) structlog.contextvars.clear_contextvars() structlog.contextvars.bind_contextvars( request_id=request_id, session_id=request.headers.get("X-Session-ID", ""), client_ip=request.client.host, path=request.url.path, ) logger.info("request_started") start = time.time() response = await call_next(request) duration = time.time() - start logger.info("request_completed", status_code=response.status_code, duration_ms=round(duration * 1000, 2)) response.headers["X-Request-ID"] = request_id return response # 日志输出示例: # {"event":"request_started","request_id":"abc-123","session_id":"sess-456","client_ip":"10.0.0.1","path":"/api/chat","level":"info","timestamp":"2026-06-24T14:00:00Z"} # {"event":"request_completed","request_id":"abc-123","status_code":200,"duration_ms":1523.45,"level":"info","timestamp":"2026-06-24T14:00:01Z"} ELK 日志管道 # Filebeat 配置:采集 Agent 日志 filebeat.inputs: - type: container paths: - /var/lib/docker/containers/*/*.log processors: - decode_json_fields: fields: ["message"] target: "" labels: service: chatbot-agent output.logstash: hosts: ["logstash:5044"] # Logstash 过滤规则 filter { if [labels][service] == "chatbot-agent" { json { source => "message" } if [request_id] { aggregate { task_id => "%{request_id}" code => "map['total_duration'] = event.get('duration_ms')" map_action => "create_or_update" } } } } 灰度发布 """灰度发布:逐步将流量切到新版本""" class CanaryRouter: """基于会话 ID 的灰度路由""" def __init__(self): self.versions = { "stable": {"weight": 90, "endpoint": "http://agent-stable:8000"}, "canary": {"weight": 10, "endpoint": "http://agent-canary:8000"}, } def route(self, session_id: str) -> str: """根据会话 ID 和权重分配版本""" # 用会话 ID 哈希确保同一用户始终路由到同一版本 hash_val = int(hashlib.md5(session_id.encode()).hexdigest(), 16) % 100 cumulative = 0 for version, config in self.versions.items(): cumulative += config["weight"] if hash_val < cumulative: return config["endpoint"] return self.versions["stable"]["endpoint"] def update_weights(self, canary_weight: int): """调整灰度权重""" self.versions["stable"]["weight"] = 100 - canary_weight self.versions["canary"]["weight"] = canary_weight # 灰度发布流程 canary = CanaryRouter() # 阶段1: 10% 流量到新版本 canary.update_weights(10) # 监控 30 分钟,检查错误率、延迟、用户反馈 # 阶段2: 50% 流量 canary.update_weights(50) # 监控 1 小时 # 阶段3: 100% 流量 canary.update_weights(100) # 旧版本下线 灰度发布检查清单 检查项 阈值 不通过操作 新版本错误率 <1% 立即回滚 新版本 P99 延迟 <旧版本 1.2 倍 观察,持续超标则回滚 新版本 CPU/内存 <80% 扩容或回滚 用户负反馈 <基线水平 回滚 关键功能测试 全部通过 回滚 实战部署检查清单 部署前: □ Docker 镜像构建并推送到镜像仓库 □ 在 staging 环境完成功能测试 □ 数据库迁移脚本准备 □ 回滚方案准备 部署中: □ 逐节点滚动更新(一次一个) □ 每个节点更新后健康检查通过 □ 灰度流量切换 部署后: □ 监控指标 30 分钟无异常 □ 抽样验证核心功能 □ 日志无异常错误模式 □ 告警通道正常工作 实战建议 永远不要在周五下午部署。给团队 48 小时窗口处理潜在问题。 ...

2026-06-24 · 6 min · 1204 words · AI 实战派
vllm deployment guide

vLLM 部署实战:高吞吐 LLM 推理服务

为什么选 vLLM Ollama 适合本地开发,但生产环境需要高吞吐:vLLM 是目前最快的开源 LLM 推理引擎。 引擎 吞吐量 并发 显存利用 适用场景 Ollama 1x 低 中 本地开发 vLLM 5-10x 高 极高 生产部署 TGI 3-5x 高 高 生产部署 TensorRT-LLM 8-12x 高 极高 极致性能 核心技术:PagedAttention 传统 KV Cache: ┌──────────────────────────────────┐ │ Request A: [████████░░░░░░░░░░░] │ 预分配,大量浪费 │ Request B: [██████████████░░░░░] │ │ Request C: [██░░░░░░░░░░░░░░░░░] │ └──────────────────────────────────┘ 显存利用率:~40% PagedAttention: ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │A1│A2│B1│B2│B3│C1│A3│B4│A4│B5│ 按需分配,零浪费 └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ 显存利用率:~95% 快速部署 Docker 部署 docker run --gpus all \ -v /models:/models \ -p 8000:8000 \ --ipc=host \ vllm/vllm-openai:latest \ --model /models/Qwen3-7B-Instruct \ --tensor-parallel-size 1 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 Python 部署 from vllm import LLM, SamplingParams llm = LLM( model="/models/Qwen3-7B-Instruct", tensor_parallel_size=1, # GPU 数量 gpu_memory_utilization=0.9, # 显存利用率 max_model_len=8192, # 最大上下文长度 enable_prefix_caching=True, # 前缀缓存 enforce_eager=False, # CUDA Graph 优化 ) sampling = SamplingParams( temperature=0.7, top_p=0.9, max_tokens=1024, ) # 批量推理 outputs = llm.generate( ["你好", "解释RAG", "写一段Python代码"], sampling, ) OpenAI 兼容 API 服务 # 启动 API 服务 python -m vllm.entrypoints.openai.api_server \ --model /models/Qwen3-7B-Instruct \ --port 8000 \ --tensor-parallel-size 1 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --enable-prefix-caching \ --served-model-name qwen3-7b # 客户端调用(与 OpenAI SDK 完全兼容) from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="vllm") response = client.chat.completions.create( model="qwen3-7b", messages=[{"role": "user", "content": "你好"}], stream=True, ) 性能调优 1. 批处理配置 llm = LLM( model="/models/Qwen3-7B-Instruct", # 批处理 max_num_seqs=256, # 最大并发序列数 max_num_batched_tokens=8192, # 每批最大 token 数 # KV Cache gpu_memory_utilization=0.9, # 显存利用率(0.8-0.95) swap_space=4, # CPU 交换空间 (GB) # 量化 quantization="awq", # AWQ 量化(省 50% 显存) ) 2. 前缀缓存 # 启用前缀缓存:相同 system prompt 的请求共享 KV Cache llm = LLM( model="/models/Qwen3-7B-Instruct", enable_prefix_caching=True, ) # 效果: # 第一个请求:1.2s(生成 KV Cache) # 后续相同 system prompt 的请求:0.3s(复用 KV Cache) 3. Speculative Decoding # 用小模型猜,大模型验 llm = LLM( model="/models/Qwen3-7B-Instruct", speculative_model="/models/Qwen3-0.5B", # 草稿模型 num_speculative_tokens=5, # 每轮猜 5 个 token ) # 效果:吞吐量提升 1.5-2x # 原理:小模型快速生成 5 个候选 token,大模型一次性验证 4. 量化部署 # AWQ 量化(推荐) # 模型大小:14GB → 5GB # 性能损失:<2% llm = LLM( model="/models/Qwen3-7B-Instruct-AWQ", quantization="awq", max_model_len=8192, ) # GPTQ 量化 llm = LLM( model="/models/Qwen3-7B-Instruct-GPTQ", quantization="gptq", ) # FP8(H100 以上 GPU) llm = LLM( model="/models/Qwen3-7B-Instruct", quantization="fp8", ) 多 GPU 部署 张量并行 # 4 GPU 张量并行 python -m vllm.entrypoints.openai.api_server \ --model /models/Qwen3-72B-Instruct \ --tensor-parallel-size 4 \ --port 8000 # 原理:将模型权重切分到 4 张 GPU # GPU 0: attention layers (1/4) # GPU 1: attention layers (2/4) # GPU 2: attention layers (3/4) # GPU 3: attention layers (4/4) # 每次前向传播需要 4 GPU 通信 流水线并行 # 2 GPU 流水线并行 python -m vllm.entrypoints.openai.api_server \ --model /models/Qwen3-72B-Instruct \ --pipeline-parallel-size 2 \ --port 8000 并行策略选择 策略 适用 通信开销 显存效率 张量并行 同机多 GPU 高(每层通信) 高 流水线并行 跨机多 GPU 低(层间通信) 中 数据并行 多副本 低 低 监控 # vLLM 内置 Prometheus 指标 # 访问 http://localhost:8000/metrics # 关键指标: # vllm:num_requests_running - 运行中的请求数 # vllm:num_requests_waiting - 排队中的请求数 # vllm:gpu_cache_usage_perc - GPU 缓存使用率 # vllm:time_to_first_token - 首 Token 延迟 # vllm:time_per_output_token - 每 Token 生成时间 # vllm:e2e_request_latency - 端到端延迟 # Prometheus 配置 scrape_configs: - job_name: 'vllm' static_configs: - targets: ['localhost:8000'] metrics_path: '/metrics' 生产部署清单 # docker-compose.yml version: '3.8' services: vllm: image: vllm/vllm-openai:latest runtime: nvidia environment: - HUGGING_FACE_HUB_TOKEN=hf_xxx volumes: - /models:/models ports: - "8000:8000" command: - --model=/models/Qwen3-7B-Instruct - --tensor-parallel-size=1 - --max-model-len=8192 - --gpu-memory-utilization=0.9 - --enable-prefix-caching - --served-model-name=qwen3-7b - --uvicorn-log-level=info healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 deploy: resources: reservations: devices: - capabilities: ["gpu"] 成本对比 以 Qwen3-7B 为例,处理 100 万 Token: ...

2026-06-24 · 4 min · 644 words · AI 实战派
🔗 硅基 AGI 论坛 · silicon-agi.com | 📡 RSS 订阅
鲁ICP备2026018361号