AI创投圈的“林俊旸现象”,如果去掉热搜式表达,本质上是一个信号:当一位技术人开始被资本市场反复讨论时,真正稀缺的不是话题度,而是“能把自己的技术判断落成可交付产品”的能力。林俊旸具体是谁、有着怎样的经历,不适合用一篇技术博客做履历考据,也不该由我来下结论。更值得做的事情,是把这种“名字成为现象”的注意力,转移回一组可执行的技术问题上:模型怎么接入、检索怎么做、Agent 工具调用怎么控制、上线之后怎么排查、成本怎么收敛。这篇文章不评价任何个人,只讨论 AI 应用从概念到落地必须经历的工程路径,并给出一套可以自己复现的最小案例和项目检查清单。
把“林俊旸现象”当作一面镜子来用,对技术人、产品经理和创业团队都有实际价值。你要么是那个被市场关注的人,要么是需要判断“值不值得关注”的人。无论在哪一侧,靠转发量和 PPT 都无法形成稳定判断。可持续的判断依据只能来自工程:代码是否能跑、评测是否能过、换了数据集是否还稳定、日志是否能回答线上问题。
1. “林俊旸现象”不是一个人,而是一个工程化信号
1.1 一个名字成为现象,说明赛道进入“找能落地的人”阶段
AI 赛道早期的讨论焦点往往集中在模型参数、论文指标和融资额度上。当某一个技术人的名字频繁出现在创投圈话题里,通常说明市场开始从“看趋势”转向“找人”。这里的人不是指会讲概念的人,而是指能同时处理模型、数据、工程和成本的人。
这类人确实稀缺。一个合格的大模型应用工程师,至少要理解 Prompt 如何影响输出、RAG 召回质量如何评估、Agent 工具调用如何防止注入、并发请求下模型服务如何表现、日志和监控如何支撑故障排查。这些能力不是靠单个爆款 Demo 能证明的,而是靠大量小问题积累起来的系统能力。
从创投视角看,“押注一个人”本质上是在押注他的工程判断力。判断力体现在遇到幻觉怎么处理、召回不准怎么定位、成本超出预期怎么优化、线上故障怎么恢复。这些能力通常无法从个人品牌或宣传稿中直接获得,只能通过实际项目资料、代码仓库、技术访谈和现场调试来验证。
1.2 热词层面的关注不等于技术结论
当“林俊旸现象”变成讨论焦点时,技术人会看到大量以名字为标题的分析文章。这些内容可以帮你快速了解市场情绪,但不能替代技术尽调。判断一个 AI 项目是否值得投入,至少要回答下面这些问题:
- 项目代码是否公开并可复现,还是只有演示视频。
- 模型接入用的是公共 API 还是私有化部署,换一个兼容接口能不能跑通。
- 输入数据改变后,输出是否仍然稳定。
- 是否有评测集,能不能区分“效果变好”和“恰好记住了样本”。
- Agent 调用外部工具时有没有做参数校验和次数限制。
- 单次请求的延迟、Token 消耗、失败率有没有可量化数据。
热词维度更关注讨论量、排名和演示效果,工程维度更关注可复现性、可观测性和成本模型。两者的差异可以用一张表概括。
| 观察维度 | 热词层面 | 工程层面 |
|---|---|---|
| 证据形式 | 演示视频、转发评论、榜单 | 可运行源码、依赖锁定、评测集 |
| 判断依据 | 名气、观点、个人品牌 | 提交记录、测试用例、线上监控 |
| 典型问题 | 技术人为什么火了 | 系统在 5 并发下表现如何 |
| 失败模式 | 热度退潮后没有沉淀 | 上线后无法定位和回滚 |
| 衡量周期 | 几天到几周 | 数月甚至数年 |
热词能带来短暂的注意力红利,但长期留下来的只有交付能力。一个可复现的最小项目,比一百条“行业分析”更能说明问题。
1.3 可复现,是技术人最有效的资格审查
所谓可复现,不是把 README 写清楚,而是让一个陌生开发者按照文档操作,能在有限时间内跑通相同的效果。AI 项目在这方面比传统后端项目更难,因为模型输出带有随机性,外部依赖也在不断变化。
一个合格的 AI 工程化项目至少应该包含固定依赖清单、明确的模型服务和模型名称、可执行的最小案例、已知误差说明和离线评测脚本。这样别人在判断你的水平时,不需要听你“怎么讲”,直接看“能不能跑”。
从技术人的职业发展角度看,与其追着热点做文章,不如多沉淀这类可复现资产。它能帮你把“名字”转化为“方法”,把一次性的注意力沉淀为长期可调用的技术能力。
2. 大模型应用落地要解决的四个工程问题
无论是一个创业项目还是一次内部创新,大模型应用都会经过四个层次:模型接入、知识检索、工具调度、基础设施治理。很多项目在 Demo 阶段表现很好,是因为只实现了最上面一层,下面的每一层都可能在生产环境变成问题。
2.1 模型接入与部署:从调用接口到自建推理的取舍
首先要想清楚模型从哪里来。常见做法有两类:接入公共模型服务,或部署开源模型到自己的服务器。
| 接入方式 | 优点 | 风险 | 适合场景 |
|---|---|---|---|
| 公共模型 API | 开发快,效果稳定,无需维护推理资源 | 数据出域,费用随调用量增长,依赖外部服务 | 原型验证、标准问答、内部工具 |
| 自建模型推理 | 数据可控,可定制,长期成本可优化 | 需要 GPU 资源,运维复杂,模型效果依赖选型 | 私有数据敏感、高并发、离线内网场景 |
| 混合架构 | 敏感请求走内网,非敏感请求走公共 API | 双链路维护成本高,规则复杂 | 企业级生产环境 |
在技术选型时,不要只看模型“聪明不聪明”,还要看接口是否兼容。目前很多模型服务平台和本地推理服务都提供 OpenAI 兼容接口,代码层可以做到不重写业务逻辑,只切换base_url和model。这会给后续迁移留出空间。
如果选择自建推理,还涉及 vLLM、Ollama、TGI 之类的部署工具。不同工具对显卡型号、显存和量化方式的要求不同,建议先用一个开源模型跑通接口,再逐步调整为最优推理方案。不要在一开始就追求“最大参数模型”,先把应用闭环建立起来更重要。
2.2 RAG:让私有知识变成模型可用的上下文
大模型不会自动知道企业内部的文档、产品手册和数据库内容。RAG(Retrieval-Augmented Generation,检索增强生成)是一种把外部知识注入模型回答的常见方法。
一个 RAG 流程通常包括五个步骤:
- 文档清洗与切分。
- 调用 Embedding 模型把文本片段向量化。
- 把向量写入向量数据库。
- 用户提问后,把问题向量化并检索相似片段。
- 把检索到的片段和原始问题一起发送给大模型生成答案。
很多新手的误区是:只要切好文档、放入向量库,模型回答就一定会变准。实际并非如此。切分粒度太细,可能丢失上下文;太粗,可能包含大量无关内容,导致回答偏移。向量模型的匹配能力、检索返回的top_k数量、是否做重排(Rerank),都会直接影响最终效果。
解决 RAG 问题的基本手段是评测。拿一批真实问题,逐个标注标准答案,然后替换切分方式、Embedding 模型或检索参数,比较回答命中率。这样能清楚知道“哪个环节拖了后腿”,而不是盲目调整 Prompt。
2.3 Agent 与工具调用:让应用从“问答”走向“执行”
单纯的大模型问答只能生成文字,无法查询订单、操作工单或调用业务系统。Agent 的意义在于让模型判断“为了完成用户意图,应该调用哪个工具”,然后根据工具返回结果继续生成最终回复。
完整的工具调用流程是一个循环:
- 用户提出问题。
- 模型返回一个普通回复,或一组工具调用请求。
- 程序执行工具调用。
- 把工具结果返回给模型。
- 模型根据工具结果继续回复或发起下一次调用。
- 设置最大轮数,防止模型陷入死循环。
工具调用本身并不神秘。关键是程序不能盲目信任模型生成的参数,必须做校验和拦截。模型可能把用户输入的恶意字符串直接作为参数,如果后端用eval执行表达式,就可能形成安全问题。后面给出的最小案例会展示一个相对安全的做法。
2.4 AI Infra 和安全:普通后端该有的东西,这里一样不能少
模型只是 AI 应用的一部分,把它当成一个普通外部服务去看待最稳妥。限流、熔断、超时、重试、日志、监控、权限控制,这些在传统后端已经成熟的手段,在 AI 应用中同样要补上。
模型返回内容本身不可控,因此需要加一层安全防护。常见的做法包括:
- 对用户输入做长度和内容合规检查。
- 对模型输出做敏感信息过滤。
- 对工具调用做白名单控制,而不是让模型随意执行任意函数。
- 记录完整调用链,包括请求 ID、模型名称、Token 消耗和耗时。
- 对关键业务回复保留人工审核入口。
把这些内容纳入 AI Infra 的职责范围,项目才有机会从实验走向生产。
3. 用 FastAPI 写一个最小可运行的 Agent 服务
为了更好地理解前面几层抽象,这里用一个可以直接复现的最小项目做演示。选择 Python 和 FastAPI,只为了让代码更容易阅读。如果你所在团队使用 Java,也可以参考同样的思路,在 Spring AI 生态中实现。技术栈不重要,工具调用闭环和安全处理方式才重要。
3.1 项目结构与环境准备
在本地建立一个实验目录:
ai-agent-demo/ ├── app.py ├── requirements.txt └── README.md推荐创建独立的 Python 环境:
mkdir ai-agent-demo cd ai-agent-demo python -m venv .venv source .venv/bin/activate写入依赖文件:
openai>=1.0.0 fastapi uvicorn安装依赖:
pip install -r requirements.txt这个示例假设你使用的模型服务提供 OpenAI 兼容接口。无论是公共模型服务,还是企业内部部署的兼容接口,都可以通过环境变量切换。
3.2 配置模型连接
在app.py顶部读取环境变量,避免把密钥写死在代码里:
import os from openai import OpenAI client = OpenAI( api_key=os.getenv("LLM_API_KEY", "EMPTY"), base_url=os.getenv("LLM_BASE_URL", "https://api.openai.com/v1"), )启动服务前,先把环境变量准备好:
export LLM_API_KEY="你的密钥" export LLM_BASE_URL="https://api.openai.com/v1" export LLM_MODEL="gpt-4o-mini"如果使用本地兼容服务,LLM_API_KEY可以填任意值,LLM_BASE_URL指向本地服务地址,LLM_MODEL换成该服务支持的模型名。公共服务的模型名称会因为地区和服务商而不同,落地时要以前后端约定的名称为准。
3.3 定义基础工具
这一层是 Agent 真正“做事”的地方。示例提供两个工具:一个查询当前时间,一个进行四则运算。计算器没有直接用eval,而是通过抽象语法树(AST)限制运算符,避免执行用户输入的任意代码。
import ast import json import operator from datetime import datetime TOOL_SPECS = [ { "type": "function", "function": { "name": "get_current_time", "description": "获取当前时间,返回 ISO 格式字符串。", "parameters": { "type": "object", "properties": {} } } }, { "type": "function", "function": { "name": "calculator", "description": "计算四则运算表达式,例如 1+2 或 (3+4)*5。", "parameters": { "type": "object", "properties": { "expression": { "type": "string", "description": "需要计算的数学表达式" } }, "required": ["expression"] } } } ] _ALLOWED_OPERATORS = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, } def get_current_time() -> str: return datetime.now().isoformat() def _safe_eval(node): if isinstance(node, ast.Expression): return _safe_eval(node.body) if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)): return node.value if isinstance(node, ast.BinOp) and type(node.op) in _ALLOWED_OPERATORS: left = _safe_eval(node.left) right = _safe_eval(node.right) return _ALLOWED_OPERATORS[type(node.op)](left, right) if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub): return -_safe_eval(node.operand) raise ValueError("表达式包含不允许的语法") def calculator(expression: str) -> str: if not isinstance(expression, str) or len(expression) > 200: return "参数不合法" try: tree = ast.parse(expression, mode="eval") result = _safe_eval(tree) return str(result) except ZeroDivisionError: return "除数不能为 0" except Exception as exc: return f"计算失败: {exc}" def run_tool(name: str, args: dict) -> str: if name == "get_current_time": return get_current_time() if name == "calculator": expression = args.get("expression", "") return calculator(expression) return f"未知工具: {name}"这里把计算器限制在四则运算范围内,可以减少模型参数注入带来的风险。实际项目中,工具函数还应做更细的权限校验,例如查询订单前必须先确认用户身份和访问范围。
3.4 实现工具调用主循环
主循环负责把模型返回的工具调用翻译成业务动作。设置最大轮次为 3,可以避免模型反复调用工具导致成本失控。
def run_agent(user_message: str, max_turns: int = 3) -> str: messages = [ { "role": "system", "content": "你是后端助手。需要获取当前时间或计算数学表达式时,请调用对应工具。" }, { "role": "user", "content": user_message } ] turn = 0 while turn < max_turns: response = client.chat.completions.create( model=os.getenv("LLM_MODEL", "gpt-4o-mini"), messages=messages, tools=TOOL_SPECS, tool_choice="auto", ) msg = response.choices[0].message if not msg.tool_calls: return msg.content or "没有可返回的内容" messages.append({ "role": "assistant", "content": msg.content or "", "tool_calls": [ { "id": tool_call.id, "type": "function", "function": { "name": tool_call.function.name, "arguments": tool_call.function.arguments } } for tool_call in msg.tool_calls ] }) for tool_call in msg.tool_calls: tool_name = tool_call.function.name try: raw_args = tool_call.function.arguments or "{}" tool_args = json.loads(raw_args) if not isinstance(tool_args, dict): tool_args = {} except json.JSONDecodeError: tool_args = {} tool_result = run_tool(tool_name, tool_args) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": tool_result, }) turn += 1 return "达到最大工具调用轮次"关键点在于,工具调用后要把结果以role: tool的形式放回消息列表,模型才能看到执行结果并生成最终答案。如果少了这一步,模型就无法知道刚才的工具调用是否成功。
3.5 对外暴露 HTTP 接口
导入 FastAPI,写一个最基础的 JSON 接口:
from fastapi import FastAPI from pydantic import BaseModel class ChatRequest(BaseModel): message: str app = FastAPI() @app.post("/chat") def chat(req: ChatRequest): return {"reply": run_agent(req.message)}启动服务:
uvicorn app:app --host 0.0.0.0 --port 8000用一个请求验证时间工具:
curl -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -d '{"message": "当前时间是多少?"}'预期结果是返回当前时间,类似下面这样:
{ "reply": "当前时间是 2025-01-15T10:30:00.123456" }再验证计算工具:
curl -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -d '{"message": "请计算 100 / 9 + 2 的结果"}'预期会通过calculator工具得到计算结果,再由模型组织成自然语言回复。实际输出会因为模型版本不同略有差异,但结果应该接近13.1111。
3.6 验证三种边界情况
只验证正常路径还不够,至少要覆盖以下三种情况:
| 场景 | 预期行为 | 需要观察的日志 |
|---|---|---|
| 模型不调用工具直接回答 | 返回文本内容 | 调用次数、Token 消耗 |
| 工具参数不是合法 JSON | 程序使用空字典兜底 | JSON 解析异常记录 |
| 模型持续要求调用工具 | 达到 3 次后终止 | 轮次保护触发 |
当前这个最小服务没有加入日志,实际生产环境里,每一次请求、模型回复、工具调用结果都应该留下结构化日志,并带上同一个请求 ID。否则用户反馈“结果不对”时,排查会非常痛苦。
4. 决定 AI 项目质量的硬指标:不是“效果惊艳”,而是可复现
很多人评估 AI 项目时,会先问“效果怎么样”。这个问题太模糊。一个模型在这条问题或这个文档集上表现好,不代表在另一条数据上同样好。真正决定项目质量的,是一组可以持续测量、对比和改进的指标。
4.1 先做一套属于自己的评测集
评测集不需要追求数量庞大,但要覆盖真实用户会问的问题。可以从客服记录、工单、产品文档、用户访谈中整理 50 到 200 条问题,并为每个问题标注期望结果。
评测集至少包含三类题目。
| 类型 | 说明 | 示例 |
|---|---|---|
| 固定答案题 | 答案来自明确文档 | “退款流程需要几步?” |
| 开放回答题 | 需要综合多个文档或上下文 | “容器化部署时,我们推荐什么方案?” |
| 边界场景题 | 应拒绝或澄清含糊问题 | 用户输入无法理解、包含敏感词或超长内容 |
每次修改 Prompt、切换模型、调整 RAG 参数,都可以在评测集上跑一遍,记录回答的正确率、无效率、拒绝率和平均耗时。这样能避免“上次挺好的,这次怎么变差”的玄学问题。
4.2 业务效果之外,还要看一组工程指标
模型准确率只是众多指标之一。对生产系统来说,下面这些指标同样重要:
| 指标 | 含义 | 参考做法 |
|---|---|---|
| P95 延迟 | 95% 请求的响应时间 | 记录从请求进入到回复完成的完整耗时 |
| TTFT | 首个 Token 返回时间 | 判断模型服务是否卡顿的重要信号 |
| Token 吞吐 | 每秒生成 Token 数 | 评估推理服务性能和成本 |
| 错误率 | 超时、限流、网络错误占比 | 超过阈值时触发告警 |
| 无效回答率 | 模型拒绝、答非所问、空回答 | 用评测集和线上抽检共同观察 |
| 单请求成本 | Token 消耗乘单价 | 用于不同方案的成本对比 |
你可以把指标打印到日志中,也可以接入 Prometheus 这类监控系统。最不该做的是等到用户投诉才去排查。
4.3 评测结果要能反推是哪一层的问题
当回答质量下降时,应逐步缩小范围。
- 先确认输入是否完整达到模型服务。
- 再确认 RAG 检索到的文档片段和用户问题是否相关。
- 查看 Prompt 中是否有错误的上下文注入。
- 确认模型版本和参数是否被切换。
- 确认工具调用是否被拦截或返回异常。
这套排查顺序在 AI 项目中非常有效。一个线上坏案例如果只是“感觉不对”,很难改进;但如果能定位到“Top 5 召回片段没有任何一个和订单状态相关”,就能直接修复检索链路。
5. AI 应用工程化中最容易翻车的五个常见坑
即使把上面的 Demo 跑通,离真正上线还有距离。下面五个坑是 AI 项目从 Demo 走向生产时最容易出现的问题。
5.1 只调 Prompt,不上线评测
很多团队把“效果优化”等同于“修改 Prompt”。今天发现答案不完整,就把 Prompt 加一句话;明天发现回答风格不对,又加一句要求。结果系统配置越来越长,行为越来越不稳定,甚至不知道是哪次修改导致问题重新出现。
正确做法是先把评测集固定住。每次 Prompt 变更都运行离线评测,通过正确率、核心字段完整度、无效回答率来衡量。没有评测的 Prompt 调整,本质上是在碰运气。
5.2 文档切分固定大小,导致召回质量忽高忽低
RAG 项目中最常见的操作是“每 500 字切一段”。这在小规模 Demo 上看似可行,遇到复杂文档就会出问题。比如一份技术手册中的“前提条件”和“操作步骤”可能分属不同小节,如果被硬切成片段,模型回答时就会缺少前提条件。
推荐做法是优先按文档结构切分:二级标题、表格行、列表项都适合作为切分边界。对长段落,设置重叠区域,让前后片段保留一定公共信息。这样虽然代码复杂,但召回更稳定。
5.3 Agent 把用户输入直接当作代码执行
这是风险最高的一个问题。模型返回的工具参数来自用户输入,如果服务端不校验就直接进入eval或拼接成系统命令,等于把工具开放给任意用户使用。轻则计算异常,重则形成注入漏洞。
规避方式很直接:所有工具执行前都要做白名单校验和参数类型校验。能不执行动态代码就不执行动态代码;确实需要计算时,优先用受限的解析器;涉及文件、命令、数据库时,必须走权限控制,而不能让模型随意构造操作。
5.4 日志没有贯穿调用链,线上问题无法复现
当用户说“刚才回答错了”,运维同学通常会面临一个尴尬:数据库有记录,但不知道模型返回了哪些 Token,也不知道 RAG 召回了哪些文档,更不知道工具调用参数是什么。
生产环境必须给每次请求生成唯一的request_id,并把 Prompt 摘要、模型名称、Token 消耗、工具调用结果都记录到结构化日志中。一旦发生问题,可以按request_id完整还原一次请求的处理过程。
5.5 长上下文和循环调用让成本不可控
模型按 Token 计费时,成本突增通常来自三个地方:
- 每个请求都把大量历史消息塞进上下文。
- Agent 循环调用工具没有设置最大轮数。
- 检索到的文档片段远超过实际需要。
可以设置每轮最大 Token 数,限制 Agent 最多执行几次工具调用,也可以把长对话压缩成摘要后再传递。上线前做一个成本估算,用“单请求平均 Token 数 × 预期调用量 × 单价”得出日成本,设置预算告警。
6. AI 项目落地前的一份可复用检查清单
如果把前面的内容压缩成一份可以直接用于项目评审的清单,可以按下面这张表逐项检查。
| 类别 | 检查项 | 通过标准 |
|---|---|---|
| 模型服务 | API Key、Base URL、模型名配置外置 | 代码库不出现明文密钥 |
| 模型服务 | 超时与重试策略 | 统一设置超时时间,避免无限等待 |
| 模型服务 | 模型版本固定 | 生产环境不随意变更默认模型 |
| RAG | 文档切分有明确规则 | 能解释为什么选 500 字而非其他长度 |
| RAG | 召回结果有评测记录 | 能回答“上一版相比这一版为何更好” |
| Agent | 工具清单有白名单 | 模型只能调用已注册的受信工具 |
| Agent | 工具参数有校验 | 非法参数会被拦截并返回明确错误 |
| Agent | 最大调用轮次有限制 | 死循环不会产生无界费用 |
| 安全 | 用户输入和模型输出都经过检查 | 日志、应答、存储中不出现敏感明文 |
| 安全 | 工具执行不使用裸 eval | 使用受限解析器或严格白名单 |
| 日志 | 每次请求有唯一 ID | 按 ID 能还原一次完整调用 |
| 日志 | 工具调用结果有记录 | 能确认模型是否拿到了错误结果 |
| 评测 | 有固定测试集 | 至少包含固定题、开放题、边界题 |
| 运维 | 错误率和延迟有监控 | 超过阈值会告警 |
| 成本 | 单请求 Token 消耗可统计 | 月预算和告警已经配置 |
这份清单可以在项目立项、提测、上线前各使用一次。越早发现问题,修复成本越低。
7. 回到“林俊旸现象”:热度终会下降,只有工程能力会留下
当一个新的技术人物名字开始被大量讨论时,很容易让人误以为成功来自个人光环。但从技术项目的真实发展看,光环只能带来关注,无法替代系统建设。一个模型应用能不能稳定运行,取决于数据链路是否完整、评测体系是否可靠、Agent 调度是否有安全边界、基础设施是否能支撑回滚和扩容。
对于想进入 AI 领域的开发者,建议把对“某某现象”的关注转化为两个行动。第一