<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ch1 on Hao · AI Agent 学习记录</title><link>https://haokw.github.io/tags/ch1/</link><description>Recent content in Ch1 on Hao · AI Agent 学习记录</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Mon, 10 Aug 2026 20:50:00 +0800</lastBuildDate><atom:link href="https://haokw.github.io/tags/ch1/index.xml" rel="self" type="application/rss+xml"/><item><title>第 1 章面试 · 05 LLM Gateway（统一模型调用服务）</title><link>https://haokw.github.io/posts/2026-08-10-ch1-interview-05-llm-gateway/</link><pubDate>Mon, 10 Aug 2026 20:50:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-interview-05-llm-gateway/</guid><description>&lt;blockquote&gt;
&lt;p&gt;目标岗位：Agent Harness 研发/工程方向（参考 &lt;code&gt;~/agent/jb/jd.md&lt;/code&gt;）
配套讲义：&lt;a
 href="../lectures/05-llm-gateway.md"
 
 
&gt;讲义 05 · LLM Gateway&lt;/a&gt;
用法：先自己口头答一遍，再对照&amp;quot;参考回答&amp;quot;；重点看&amp;quot;考察点&amp;quot;和&amp;quot;坑&amp;quot;。
原则：所有回答&lt;strong&gt;结论先行 → 项目证据 → 原理 → 边界与改进&lt;/strong&gt;，不要背概念。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="0-面试官视角gateway-题到底在考什么"&gt;0. 面试官视角：Gateway 题到底在考什么&lt;a href="#0-%e9%9d%a2%e8%af%95%e5%ae%98%e8%a7%86%e8%a7%92gateway-%e9%a2%98%e5%88%b0%e5%ba%95%e5%9c%a8%e8%80%83%e4%bb%80%e4%b9%88" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Gateway 是&amp;quot;把分散的厂商调用收敛成平台能力&amp;quot;的典型系统设计题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;概念层&lt;/strong&gt;：Gateway 解决什么问题（密钥、协议、可观测）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践层&lt;/strong&gt;：你的网关怎么分层、每个模块在哪、有没有测试；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;判断层&lt;/strong&gt;：为什么这么设计？并发和性能边界？新增厂商要改什么？——拉差距。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;回答公式：&lt;strong&gt;一句话结论 → 你项目里的实现/例子 → 原理 → 边界与改进&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-按讲义章节的题目与参考回答"&gt;1. 按讲义章节的题目与参考回答&lt;a href="#1-%e6%8c%89%e8%ae%b2%e4%b9%89%e7%ab%a0%e8%8a%82%e7%9a%84%e9%a2%98%e7%9b%ae%e4%b8%8e%e5%8f%82%e8%80%83%e5%9b%9e%e7%ad%94" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q1：LLM Gateway 解决什么问题？为什么不直接让业务调厂商 SDK？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：四个问题——密钥分散（每个组件持 key，泄露面大）、协议差异
（每家格式不同）、不可观测（没有统一日志）、无法统一治理（限流/回退/成本）。
网关收敛成一层：密钥集中在 config 层、协议统一成 OpenAI Compatible、
每次调用留痕、限流回退统一做。我的网关 &lt;code&gt;create_app&lt;/code&gt; 组装了路由、限流、
模板、用量、provider 链，业务只发 HTTP。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Q2：密钥为什么集中？怎么防泄露？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：集中后业务组件不碰厂商密钥，泄露面从&amp;quot;每个组件&amp;quot;缩小到&amp;quot;网关
一个点&amp;quot;。密钥从环境变量读（&lt;code&gt;config.py&lt;/code&gt;，支持 DASHSCOPE_API_KEY 等厂商
命名空间），不进代码库。诚实边界：网关本身是新的攻击面——生产上密钥
应放密钥管理服务（KMS/Vault），网关进程还要做最小权限和审计。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Q3：为什么对外统一成 OpenAI Compatible，而不是自定义协议？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：OpenAI Compatible 是事实标准，DeepSeek、DashScope、本地
Ollama/vLLM 都兼容，生态里现成 SDK 和工具直接可用。对内用 &lt;code&gt;ChatProvider&lt;/code&gt;
接口 + adapter 屏蔽差异（讲义 01 §6），对外格式稳定、对内可演进。收益：
换厂商不改业务代码、OpenAI 官方 SDK 能直连网关。&lt;/p&gt;</description></item><item><title>第 1 章面试 · 04 Structured Output 结构化输出</title><link>https://haokw.github.io/posts/2026-08-10-ch1-interview-04-structured-output/</link><pubDate>Mon, 10 Aug 2026 20:40:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-interview-04-structured-output/</guid><description>&lt;blockquote&gt;
&lt;p&gt;目标岗位：Agent Harness 研发/工程方向（参考 &lt;code&gt;~/agent/jb/jd.md&lt;/code&gt;）
配套讲义：&lt;a
 href="../lectures/04-structured-output.md"
 
 
&gt;讲义 04 · Structured Output&lt;/a&gt;
用法：先自己口头答一遍，再对照&amp;quot;参考回答&amp;quot;；重点看&amp;quot;考察点&amp;quot;和&amp;quot;坑&amp;quot;。
原则：所有回答&lt;strong&gt;结论先行 → 项目证据 → 原理 → 边界与改进&lt;/strong&gt;，不要背概念。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="0-面试官视角结构化输出题到底在考什么"&gt;0. 面试官视角：结构化输出题到底在考什么&lt;a href="#0-%e9%9d%a2%e8%af%95%e5%ae%98%e8%a7%86%e8%a7%92%e7%bb%93%e6%9e%84%e5%8c%96%e8%be%93%e5%87%ba%e9%a2%98%e5%88%b0%e5%ba%95%e5%9c%a8%e8%80%83%e4%bb%80%e4%b9%88" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;结构化输出是 Agent 工程的&lt;strong&gt;地基&lt;/strong&gt;——工具调用、下游解析全靠它。面试官三层递进：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;概念层&lt;/strong&gt;：JSON Schema、Pydantic、原生结构化输出是什么；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践层&lt;/strong&gt;：你项目里三家厂商怎么实现的？校验失败怎么处理？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;判断层&lt;/strong&gt;：为什么不能只靠 prompt？失败降级怎么设计？流式 + 结构化怎么
组合？——拉开差距的地方。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;回答公式：&lt;strong&gt;一句话结论 → 你项目里的实现/例子 → 原理 → 边界与改进&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-按讲义章节的题目与参考回答"&gt;1. 按讲义章节的题目与参考回答&lt;a href="#1-%e6%8c%89%e8%ae%b2%e4%b9%89%e7%ab%a0%e8%8a%82%e7%9a%84%e9%a2%98%e7%9b%ae%e4%b8%8e%e5%8f%82%e8%80%83%e5%9b%9e%e7%ad%94" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q1：为什么 Agent 系统不能依赖自然语言解析？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：模型输出要喂给代码——查词结果填卡片、工具参数发请求、SQL 要
执行。自然语言输出换行、加粗、解释文字都会破坏解析；更糟的是解析失败时
分不清是&amp;quot;格式错了&amp;quot;还是&amp;quot;答案错了&amp;quot;。所以原则是协议先行 + 校验兜底 +
失败有出路：先定义输出形状，返回后校验，不过就纠错重试或降级，绝不把脏
数据传给下游。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;考察点&lt;/strong&gt;：能不能说出&amp;quot;解析失败无法区分错误类型&amp;quot;这个深层原因。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Q2：JSON Schema 有哪些关键字段？&lt;code&gt;additionalProperties&lt;/code&gt; 为什么重要？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：&lt;code&gt;type&lt;/code&gt;（对象/数组/标量）、&lt;code&gt;required&lt;/code&gt;（必填）、&lt;code&gt;properties&lt;/code&gt;
（字段类型）、数组的 &lt;code&gt;items&lt;/code&gt;/&lt;code&gt;minItems&lt;/code&gt;、字符串的 &lt;code&gt;enum&lt;/code&gt;。&lt;code&gt;additionalProperties: false&lt;/code&gt; 很重要——它禁止模型自作主张加字段，协议边界一旦被突破，下游解析
就容易悄悄出错。我 &lt;code&gt;/v1/structured&lt;/code&gt; 的 schema 都关掉了多余字段。&lt;/p&gt;</description></item><item><title>第 1 章面试 · 03 Prompt Engineering</title><link>https://haokw.github.io/posts/2026-08-10-ch1-interview-03-prompt-engineering/</link><pubDate>Mon, 10 Aug 2026 20:30:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-interview-03-prompt-engineering/</guid><description>&lt;blockquote&gt;
&lt;p&gt;目标岗位：Agent Harness 研发/工程方向（参考 &lt;code&gt;~/agent/jb/jd.md&lt;/code&gt;）
配套讲义：&lt;a
 href="../lectures/03-prompt-engineering.md"
 
 
&gt;讲义 03 · Prompt Engineering&lt;/a&gt;
用法：先自己口头答一遍，再对照&amp;quot;参考回答&amp;quot;；重点看&amp;quot;考察点&amp;quot;和&amp;quot;坑&amp;quot;。
原则：所有回答&lt;strong&gt;结论先行 → 项目证据 → 原理 → 边界与改进&lt;/strong&gt;，不要背概念。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="0-面试官视角prompt-题到底在考什么"&gt;0. 面试官视角：Prompt 题到底在考什么&lt;a href="#0-%e9%9d%a2%e8%af%95%e5%ae%98%e8%a7%86%e8%a7%92prompt-%e9%a2%98%e5%88%b0%e5%ba%95%e5%9c%a8%e8%80%83%e4%bb%80%e4%b9%88" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Prompt 题最容易答成&amp;quot;背技巧&amp;quot;，面试官想听的是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;概念层&lt;/strong&gt;：System Prompt、Few-shot、注入是什么；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践层&lt;/strong&gt;：你的模板怎么管理、注入怎么防、版本怎么回滚，代码在哪；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;判断层&lt;/strong&gt;：隔离为什么不是银弹？Prompt 变更怎么灰度？怎么评估 Prompt
好坏？——这里拉开差距。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;回答公式：&lt;strong&gt;一句话结论 → 你项目里的实现/例子 → 背后的原理 → 边界与可改进点&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-按讲义章节的题目与参考回答"&gt;1. 按讲义章节的题目与参考回答&lt;a href="#1-%e6%8c%89%e8%ae%b2%e4%b9%89%e7%ab%a0%e8%8a%82%e7%9a%84%e9%a2%98%e7%9b%ae%e4%b8%8e%e5%8f%82%e8%80%83%e5%9b%9e%e7%ad%94" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q1：System Prompt 和普通用户消息有什么区别？你们怎么管理 System Prompt？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：System Prompt 是&amp;quot;岗位说明书&amp;quot;——定义角色、目标、边界和输出约定，
独立于用户消息（&lt;code&gt;role: system&lt;/code&gt;），模型通常给它更高权重。我把 System Prompt
当成模板资产管理：&lt;code&gt;prompts.json&lt;/code&gt; 里存 &lt;code&gt;reading_assistant&lt;/code&gt; 这类模板，带变量
（&lt;code&gt;{user_level}&lt;/code&gt;）和版本号，网关 &lt;code&gt;/v1/prompts&lt;/code&gt; 统一渲染，业务层只传变量。
这样角色设定变更走模板版本，不碰业务代码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;考察点&lt;/strong&gt;：能不能区分&amp;quot;角色设定&amp;quot;和&amp;quot;用户消息&amp;quot;两个通道；模板化管理的动机。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Q2：角色定义怎么写才有效？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：角色定义 = &amp;ldquo;你是谁 + 怎么干活&amp;rdquo;，关键是&lt;strong&gt;行为约束&lt;/strong&gt;要具体。弱约束
&amp;ldquo;你是代码审查助手&amp;quot;模型会自由发挥；强约束要写明审查维度、每个问题的输出
要素、以及&amp;quot;没有问题时明确说无问题，不要编造&amp;rdquo;。我的种子模板
&lt;code&gt;translate_assistant&lt;/code&gt; 就是&amp;quot;只输出译文&amp;quot;这种带边界的角色，约束和输出协议分开写。&lt;/p&gt;</description></item><item><title>第 1 章面试 · 02 Streaming 流式输出</title><link>https://haokw.github.io/posts/2026-08-10-ch1-interview-02-streaming/</link><pubDate>Mon, 10 Aug 2026 20:20:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-interview-02-streaming/</guid><description>&lt;blockquote&gt;
&lt;p&gt;目标岗位：Agent Harness 研发/工程方向（参考 &lt;code&gt;~/agent/jb/jd.md&lt;/code&gt;）
配套讲义：&lt;a
 href="../lectures/02-streaming.md"
 
 
&gt;讲义 02 · Streaming 流式输出&lt;/a&gt;
用法：先自己口头答一遍，再对照&amp;quot;参考回答&amp;quot;；重点看&amp;quot;考察点&amp;quot;和&amp;quot;坑&amp;quot;。
原则：所有回答&lt;strong&gt;结论先行 → 项目证据 → 原理 → 边界与改进&lt;/strong&gt;，不要背概念。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="0-面试官视角流式题到底在考什么"&gt;0. 面试官视角：流式题到底在考什么&lt;a href="#0-%e9%9d%a2%e8%af%95%e5%ae%98%e8%a7%86%e8%a7%92%e6%b5%81%e5%bc%8f%e9%a2%98%e5%88%b0%e5%ba%95%e5%9c%a8%e8%80%83%e4%bb%80%e4%b9%88" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;流式是&amp;quot;理论简单、工程全是坑&amp;quot;的题目，面试官按三层递进：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;概念层&lt;/strong&gt;：SSE 是什么、TTFB 是什么；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践层&lt;/strong&gt;：你的网关/前端怎么实现的，能不能现场演示；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;判断层&lt;/strong&gt;：两段流式缺一段会怎样？取消怎么贯穿三层？流中出错为什么不能
重试？长文本断了怎么办？——这一层才是拉开差距的地方。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;回答公式：&lt;strong&gt;一句话结论 → 你项目里的实现/例子 → 背后的原理 → 边界与可改进点&lt;/strong&gt;。
主动说&amp;quot;这里我做得不彻底，我的方案是 X&amp;quot;，比被追问出来强得多。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-按讲义章节的题目与参考回答"&gt;1. 按讲义章节的题目与参考回答&lt;a href="#1-%e6%8c%89%e8%ae%b2%e4%b9%89%e7%ab%a0%e8%8a%82%e7%9a%84%e9%a2%98%e7%9b%ae%e4%b8%8e%e5%8f%82%e8%80%83%e5%9b%9e%e7%ad%94" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="11-为什么需要-streaming"&gt;1.1 为什么需要 Streaming&lt;a href="#11-%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-streaming" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Q1：流式和非流式在用户体验上差在哪？你项目里有数据吗？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：LLM 生成要几十秒，非流式是&amp;quot;等全部生成完才出现第一个字&amp;quot;，用户
看到一片空白；流式是&amp;quot;边生成边显示&amp;quot;，用户感知延迟从&amp;quot;总耗时&amp;quot;降到&amp;quot;第一个
token 的时间&amp;quot;。我首页有个对比面板：同一消息 × 两种模式并排跑，流式栏会
显示首字节耗时、总耗时、分片数和分片表。改流式链路之前实测过一次：
非流式总耗时 20662ms，流式首字节 20655ms——首字节几乎等于总耗时，这恰恰
暴露了&amp;quot;上游整体缓冲后回放&amp;quot;的问题，说明只传 &lt;code&gt;stream: true&lt;/code&gt; 而不打通传输层，
用户体验没有任何提升。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;考察点&lt;/strong&gt;：是否真的理解&amp;quot;感知延迟&amp;quot;；能不能用自己项目里的指标讲，而不是背概念。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Q2：首字节耗时（TTFB）和总耗时有什么区别？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：TTFB 是请求发出到收到第一块内容的时间，总耗时是收到最后一块的
时间。非流式里两者基本相等（必须等完整结果）；真流式里 TTFB 约等于&amp;quot;模型
生成第一个 token 的时间&amp;quot;，总耗时不变但感知变快。工程上这是流式最重要的
监控指标——如果首字节≈总耗时，说明链路里某一段被缓冲了（浏览器、反向代理、
或者上游 HTTP 客户端整体读 body）。&lt;/p&gt;</description></item><item><title>第 1 章面试 · 01 LLM API 与模型调用基础</title><link>https://haokw.github.io/posts/2026-08-10-ch1-interview-01-llm-api-basics/</link><pubDate>Mon, 10 Aug 2026 20:10:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-interview-01-llm-api-basics/</guid><description>&lt;blockquote&gt;
&lt;p&gt;目标岗位：Agent Harness 研发/工程方向（参考 &lt;code&gt;~/agent/jb/jd.md&lt;/code&gt;）
用法：先自己口头答一遍，再对照&amp;quot;参考回答&amp;quot;；重点看&amp;quot;考察点&amp;quot;和&amp;quot;坑&amp;quot;。
原则：所有回答&lt;strong&gt;结论先行 → 项目证据 → 原理 → 边界与改进&lt;/strong&gt;，不要背概念。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="0-面试官视角这类题到底在考什么"&gt;0. 面试官视角：这类题到底在考什么&lt;a href="#0-%e9%9d%a2%e8%af%95%e5%ae%98%e8%a7%86%e8%a7%92%e8%bf%99%e7%b1%bb%e9%a2%98%e5%88%b0%e5%ba%95%e5%9c%a8%e8%80%83%e4%bb%80%e4%b9%88" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;同样的知识点，面试官会按三层递进考察：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;概念层&lt;/strong&gt;：懂不懂（Chat Completions 结构、token 是什么）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实践层&lt;/strong&gt;：做没做过（你项目里怎么实现的，代码在哪）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;判断层&lt;/strong&gt;：为什么这么设计、哪里做得不好、怎么改（这是拉开差距的地方）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;回答公式：&lt;strong&gt;一句话结论 → 你项目里的实现/例子 → 背后的原理 → 边界与可改进点&lt;/strong&gt;。
最后一步尤其重要——主动说&amp;quot;这里我还没做/做得不好，我的方案是 X&amp;quot;，比被追问出来
强得多。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-按讲义章节的题目与参考回答"&gt;1. 按讲义章节的题目与参考回答&lt;a href="#1-%e6%8c%89%e8%ae%b2%e4%b9%89%e7%ab%a0%e8%8a%82%e7%9a%84%e9%a2%98%e7%9b%ae%e4%b8%8e%e5%8f%82%e8%80%83%e5%9b%9e%e7%ad%94" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="11-chat-completions--responses-api"&gt;1.1 Chat Completions / Responses API&lt;a href="#11-chat-completions--responses-api" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Q1：你们网关为什么对外暴露 chat.completions，而不是直接调各家 SDK？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考回答：因为 chat.completions 是事实标准，DeepSeek、DashScope、Moonshot、
本地 Ollama 都提供兼容端点。我在 provider 层定义了统一接口 &lt;code&gt;ChatProvider&lt;/code&gt;，
各家用 adapter 做格式转换（&lt;code&gt;openai_compatible.py&lt;/code&gt; / &lt;code&gt;anthropic.py&lt;/code&gt; /
&lt;code&gt;gemini.py&lt;/code&gt;），上层代码只认一个接口。收益是：换模型不改业务代码、
可以跨厂商回退、用量和错误统一统计。本质是把&amp;quot;协议差异&amp;quot;收敛到 adapter 层。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;考察点&lt;/strong&gt;：是否理解&amp;quot;统一抽象&amp;quot;的动机（协议差异、切换成本、回退、可观测）。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Q2：Chat Completions 和 Responses API 有什么区别？你们为什么没做 Responses？&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>第 1 章讲义 · 05 LLM Gateway（统一模型调用服务）</title><link>https://haokw.github.io/posts/2026-08-10-ch1-lecture-05-llm-gateway/</link><pubDate>Mon, 10 Aug 2026 20:00:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-lecture-05-llm-gateway/</guid><description>&lt;blockquote&gt;
&lt;p&gt;配套代码：整个 &lt;code&gt;src/anna/server/&lt;/code&gt; + &lt;code&gt;src/anna/providers/&lt;/code&gt;，入口 &lt;code&gt;main.py&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;学完本节，你应该能回答：Gateway 解决什么问题？密钥为什么集中在网关？
请求/响应各在哪层校验？模板、错误、用量为什么都要&amp;quot;统一&amp;quot;？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="目录"&gt;目录&lt;a href="#%e7%9b%ae%e5%bd%95" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a
 href="#1-fastapi-llm-%e6%9c%8d%e5%8a%a1%e7%bb%9f%e4%b8%80%e5%85%a5%e5%8f%a3%e4%b8%8e%e5%af%86%e9%92%a5%e7%ae%a1%e7%90%86"
 
 
&gt;FastAPI LLM 服务：统一入口与密钥管理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#2-%e6%a8%a1%e5%9e%8b%e8%b0%83%e7%94%a8%e6%8e%a5%e5%8f%a3%e5%b0%81%e8%a3%85-openai-compatible"
 
 
&gt;模型调用接口：封装 OpenAI Compatible&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#3-streaming-%e8%be%93%e5%87%ba%e7%bd%91%e5%85%b3%e4%bb%a3%e7%90%86%e9%80%90%e5%9d%97%e8%bd%ac%e5%8f%91"
 
 
&gt;Streaming 输出：网关代理逐块转发&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#4-structured-output%e5%b1%8f%e8%94%bd%e5%ba%95%e5%b1%82%e5%b7%ae%e5%bc%82"
 
 
&gt;Structured Output：屏蔽底层差异&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#5-pydantic-%e6%95%b0%e6%8d%ae%e6%a0%a1%e9%aa%8c%e5%85%a5%e5%8f%a3%e4%b8%8e%e5%87%ba%e5%8f%a3"
 
 
&gt;Pydantic 数据校验：入口与出口&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#6-prompt-%e6%a8%a1%e6%9d%bf%e7%bd%91%e5%85%b3%e7%bb%9f%e4%b8%80%e7%ae%a1%e7%90%86"
 
 
&gt;Prompt 模板：网关统一管理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#7-%e9%94%99%e8%af%af%e5%a4%84%e7%90%86%e4%b8%8e%e9%87%8d%e8%af%95%e9%99%90%e6%b5%81%e8%b6%85%e6%97%b6fallback"
 
 
&gt;错误处理与重试：限流、超时、fallback&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#8-token--cost--latency-%e6%97%a5%e5%bf%97"
 
 
&gt;Token / Cost / Latency 日志&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="1-fastapi-llm-服务统一入口与密钥管理"&gt;1. FastAPI LLM 服务：统一入口与密钥管理&lt;a href="#1-fastapi-llm-%e6%9c%8d%e5%8a%a1%e7%bb%9f%e4%b8%80%e5%85%a5%e5%8f%a3%e4%b8%8e%e5%af%86%e9%92%a5%e7%ae%a1%e7%90%86" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Gateway 解决的第一件事：各组件不需要各自维护 API 密钥和厂商配置。&lt;/strong&gt;
业务代码只调一个 HTTP 入口，密钥、端点、模型清单全部集中在网关的配置层
（&lt;code&gt;src/anna/config.py&lt;/code&gt;，环境变量驱动）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export DASHSCOPE_API_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;... &lt;span style="color:#75715e"&gt;# 网关统一持有&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ANNA_BASE_URL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;https://dashscope.aliyuncs.com/compatible-mode/v1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;业务组件永远不接触厂商 SDK 和密钥，只发请求：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; httpx
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;resp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; httpx&lt;span style="color:#f92672"&gt;.&lt;/span&gt;post(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;http://127.0.0.1:8000/v1/chat/completions&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; json&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;auto&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;: [{&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;hi&amp;#34;&lt;/span&gt;}]},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这带来三个收益：密钥泄露面缩小（只有网关持有）、厂商切换对业务无感、
调用行为（限流/用量/错误）全部可观测。入口是 FastAPI 应用（&lt;code&gt;main.py&lt;/code&gt; +
&lt;code&gt;create_app&lt;/code&gt;），&lt;code&gt;/docs&lt;/code&gt; 自带 Swagger 调试。&lt;/p&gt;</description></item><item><title>第 1 章讲义 · 04 Structured Output 结构化输出</title><link>https://haokw.github.io/posts/2026-08-10-ch1-lecture-04-structured-output/</link><pubDate>Mon, 10 Aug 2026 19:50:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-lecture-04-structured-output/</guid><description>&lt;blockquote&gt;
&lt;p&gt;配套代码：结构化引擎 &lt;code&gt;src/anna/structured.py&lt;/code&gt;、网关端点 &lt;code&gt;/v1/structured&lt;/code&gt;
（&lt;code&gt;src/anna/server/app.py&lt;/code&gt;）、三家 provider 的原生结构化输出实现、
首页&amp;quot;结构化输出&amp;quot;面板&lt;/p&gt;
&lt;p&gt;学完本节，你应该能回答：为什么 Agent 不能靠自然语言解析？JSON Schema 怎么
定义？Pydantic 和 Schema 是什么关系？模型原生结构化输出怎么用？校验失败
怎么纠错、怎么降级？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="目录"&gt;目录&lt;a href="#%e7%9b%ae%e5%bd%95" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a
 href="#1-%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%83%bd%e4%be%9d%e8%b5%96%e8%87%aa%e7%84%b6%e8%af%ad%e8%a8%80%e8%a7%a3%e6%9e%90"
 
 
&gt;为什么不能依赖自然语言解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#2-json-schema%e5%ae%9a%e4%b9%89%e8%be%93%e5%87%ba%e7%bb%93%e6%9e%84"
 
 
&gt;JSON Schema：定义输出结构&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#3-pydantic-model%e5%ae%9a%e4%b9%89--%e6%a0%a1%e9%aa%8c--%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96"
 
 
&gt;Pydantic Model：定义 + 校验 + 反序列化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#4-%e6%a8%a1%e5%9e%8b%e5%8e%9f%e7%94%9f%e7%bb%93%e6%9e%84%e5%8c%96%e8%be%93%e5%87%ba"
 
 
&gt;模型原生结构化输出&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#5-schema-%e6%a0%a1%e9%aa%8c"
 
 
&gt;Schema 校验&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#6-%e8%be%93%e5%87%ba%e7%ba%a0%e9%94%99%e4%b8%8e%e9%87%8d%e8%af%95"
 
 
&gt;输出纠错与重试&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#7-%e7%bb%93%e6%9e%84%e5%8c%96%e8%be%93%e5%87%ba%e5%a4%b1%e8%b4%a5%e5%a4%84%e7%90%86%e9%99%8d%e7%ba%a7%e4%b8%8e%e5%9b%9e%e9%80%80"
 
 
&gt;结构化输出失败处理：降级与回退&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#8-%e8%be%93%e5%87%ba%e6%a0%bc%e5%bc%8f%e4%b8%8e%e4%b8%9a%e5%8a%a1%e5%8d%8f%e8%ae%ae%e8%ae%be%e8%ae%a1"
 
 
&gt;输出格式与业务协议设计&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="1-为什么不能依赖自然语言解析"&gt;1. 为什么不能依赖自然语言解析&lt;a href="#1-%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%83%bd%e4%be%9d%e8%b5%96%e8%87%aa%e7%84%b6%e8%af%ad%e8%a8%80%e8%a7%a3%e6%9e%90" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Agent 系统里，模型输出要喂给&lt;strong&gt;代码&lt;/strong&gt;：查词结果要填进卡片、工具参数要发请求、
SQL 要执行。自然语言解析的典型翻车：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;模型回复：&amp;#34;好的，单词是 hello，意思是 你好，例句是 Hello world！&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;正则/切片解析这种输出，任何换行、加粗、多余解释都会破坏结果；更糟的是
解析失败时&lt;strong&gt;你不知道它是格式错了还是答案错了&lt;/strong&gt;。所以工程原则是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输出协议先行&lt;/strong&gt;：先定义&amp;quot;模型必须返回什么形状&amp;quot;，再让它生成；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;校验兜底&lt;/strong&gt;：返回后必须校验，而不是信任；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;失败有出路&lt;/strong&gt;：校验不过就纠错重试或降级，绝不把脏数据传给下游。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="2-json-schema定义输出结构"&gt;2. JSON Schema：定义输出结构&lt;a href="#2-json-schema%e5%ae%9a%e4%b9%89%e8%be%93%e5%87%ba%e7%bb%93%e6%9e%84" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;JSON Schema 是描述 JSON 结构的标准：字段名、类型、是否必填、取值范围。它是
&lt;strong&gt;跨语言的协议语言&lt;/strong&gt;——模型、Python、前端都能理解。&lt;/p&gt;</description></item><item><title>第 1 章讲义 · 03 Prompt Engineering</title><link>https://haokw.github.io/posts/2026-08-10-ch1-lecture-03-prompt-engineering/</link><pubDate>Mon, 10 Aug 2026 19:40:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-lecture-03-prompt-engineering/</guid><description>&lt;blockquote&gt;
&lt;p&gt;配套代码：模板库 &lt;code&gt;src/anna/prompts.py&lt;/code&gt;、种子模板 &lt;code&gt;prompts.json&lt;/code&gt;、
模板 API &lt;code&gt;src/anna/server/app.py&lt;/code&gt;、首页模板管理与注入演示
（&lt;code&gt;src/anna/server/static/index.html&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;学完本节，你应该能回答：System Prompt 该写什么？怎么让输出风格可控？
Few-shot 什么时候有效？复杂任务为什么要拆步？模板怎么做到可复用可版本化？
用户输入怎么注入指令、怎么防？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="目录"&gt;目录&lt;a href="#%e7%9b%ae%e5%bd%95" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a
 href="#1-system-prompt-%e7%9a%84%e8%81%8c%e8%b4%a3"
 
 
&gt;System Prompt 的职责&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#2-%e8%a7%92%e8%89%b2%e5%ae%9a%e4%b9%89%e4%b8%8e%e8%a1%8c%e4%b8%ba%e7%ba%a6%e6%9d%9f"
 
 
&gt;角色定义与行为约束&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#3-%e8%be%93%e5%87%ba%e9%a3%8e%e6%a0%bc%e6%8e%a7%e5%88%b6"
 
 
&gt;输出风格控制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#4-few-shot-%e7%a4%ba%e4%be%8b"
 
 
&gt;Few-shot 示例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#5-%e4%bb%bb%e5%8a%a1%e6%8b%86%e8%a7%a3"
 
 
&gt;任务拆解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#6-prompt-%e6%a8%a1%e6%9d%bf"
 
 
&gt;Prompt 模板&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#7-prompt-%e6%b3%a8%e5%85%a5%e9%a3%8e%e9%99%a9%e4%b8%8e%e9%98%b2%e6%8a%a4"
 
 
&gt;Prompt 注入风险与防护&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#8-prompt-%e7%89%88%e6%9c%ac%e7%ae%a1%e7%90%86"
 
 
&gt;Prompt 版本管理&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="1-system-prompt-的职责"&gt;1. System Prompt 的职责&lt;a href="#1-system-prompt-%e7%9a%84%e8%81%8c%e8%b4%a3" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;System Prompt 相当于模型的&lt;strong&gt;岗位说明书&lt;/strong&gt;：它不回答具体问题，而是定义&amp;quot;你是谁、
要完成什么、边界是什么&amp;quot;。它的四个作用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设定全局行为基调（语气、语言、价值观约束）；&lt;/li&gt;
&lt;li&gt;声明任务目标（这个系统是干嘛的）；&lt;/li&gt;
&lt;li&gt;定义输入输出约定（收到什么、返回什么格式）；&lt;/li&gt;
&lt;li&gt;声明不可越界的规则（不执行用户内嵌指令、不泄露设定）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本仓库的种子模板 &lt;code&gt;reading_assistant&lt;/code&gt; 就是一个 System Prompt 实例（&lt;code&gt;prompts.json&lt;/code&gt;）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;你是 Anna，一位耐心、爱读书、会催你复习的英语陪练。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;当前学习者水平：{user_level}。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;回答使用英语为主、中文解释为辅，遇到新词主动给出释义与例句。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;{user_level}&lt;/code&gt; 是可变参数——同一个岗位说明书，可以按学习者水平渲染不同版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; anna.prompts &lt;span style="color:#f92672"&gt;import&lt;/span&gt; PromptStore
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;store &lt;span style="color:#f92672"&gt;=&lt;/span&gt; PromptStore&lt;span style="color:#f92672"&gt;.&lt;/span&gt;load()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;system &lt;span style="color:#f92672"&gt;=&lt;/span&gt; store&lt;span style="color:#f92672"&gt;.&lt;/span&gt;render(&lt;span style="color:#e6db74"&gt;&amp;#34;reading_assistant&amp;#34;&lt;/span&gt;, user_level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;初级&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; &amp;#34;你是 Anna，一位耐心、爱读书、会催你复习的英语陪练。\n当前学习者水平：初级。...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键认知&lt;/strong&gt;：System Prompt 和用户消息是两个独立通道（&lt;code&gt;role: system&lt;/code&gt; vs
&lt;code&gt;role: user&lt;/code&gt;），模型通常给 system 更高权重。这也是为什么第 7 节&amp;quot;注入防护&amp;quot;
要从 system 层做，而不是在用户消息里补救。&lt;/p&gt;</description></item><item><title>第 1 章讲义 · 02 Streaming 流式输出</title><link>https://haokw.github.io/posts/2026-08-10-ch1-lecture-02-streaming/</link><pubDate>Mon, 10 Aug 2026 19:30:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-lecture-02-streaming/</guid><description>&lt;blockquote&gt;
&lt;p&gt;配套代码：网关的流式端点（&lt;code&gt;src/anna/server/app.py&lt;/code&gt;）、SSE 组装与解析
（&lt;code&gt;src/anna/server/openai_format.py&lt;/code&gt;、&lt;code&gt;src/anna/providers/base.py&lt;/code&gt;）、
首页可视化测试台（&lt;code&gt;src/anna/server/static/index.html&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;学完本节，你应该能回答：为什么要把一次 LLM 调用切成一块一块返回？
SSE 协议长什么样？FastAPI 怎么保持低延迟转发？前端怎么逐块消费？
用户点&amp;quot;停止&amp;quot;会发生什么？流到一半断了怎么处理？长文本怎么续写？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="目录"&gt;目录&lt;a href="#%e7%9b%ae%e5%bd%95" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a
 href="#1-%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-streaming"
 
 
&gt;为什么需要 Streaming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#2-server-sent-eventssse"
 
 
&gt;Server-Sent Events（SSE）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#3-fastapi-%e6%b5%81%e5%bc%8f%e6%8e%a5%e5%8f%a3streamingresponse"
 
 
&gt;FastAPI 流式接口：StreamingResponse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#4-%e5%89%8d%e7%ab%af--cli-%e6%b6%88%e8%b4%b9%e6%b5%81%e5%bc%8f%e8%be%93%e5%87%ba"
 
 
&gt;前端 / CLI 消费流式输出&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#5-%e4%b8%ad%e6%96%ad%e4%b8%8e%e5%8f%96%e6%b6%88"
 
 
&gt;中断与取消&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#6-%e6%b5%81%e5%bc%8f%e8%be%93%e5%87%ba%e5%bc%82%e5%b8%b8%e5%a4%84%e7%90%86"
 
 
&gt;流式输出异常处理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#7-%e9%95%bf%e6%96%87%e6%9c%ac%e8%be%93%e5%87%ba%e7%9a%84%e7%8a%b6%e6%80%81%e4%bf%9d%e5%ad%98"
 
 
&gt;长文本输出的状态保存&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="1-为什么需要-streaming"&gt;1. 为什么需要 Streaming&lt;a href="#1-%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-streaming" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;LLM 推理不是&amp;quot;秒回&amp;quot;：一次回答要先经过提示词处理、逐 token 自回归生成，短则几秒、
长则几十秒。&lt;strong&gt;非流式 = 客户端一直等到全部生成完才拿到第一个字&lt;/strong&gt;，用户看到的是
一片空白；流式 = 模型每吐出一个 token 就立刻送出去，用户看到的是&amp;quot;边生成边打字&amp;quot;。&lt;/p&gt;
&lt;p&gt;衡量这个差距有两个指标：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;首字节耗时（time to first byte, TTFB）&lt;/strong&gt;：从发出请求到收到第一块内容；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;总耗时&lt;/strong&gt;：从发出请求到收到最后一块内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;非流式请求的 TTFB ≈ 总耗时（必须等完整结果）；流式请求的 TTFB 可以小到
&amp;ldquo;模型生成第一个 token 的时间&amp;rdquo;，总耗时不变，但感知延迟大幅下降。&lt;/p&gt;
&lt;p&gt;本仓库首页的&amp;quot;流式 vs 非流式&amp;quot;对比面板实测过一组数据：&lt;/p&gt;</description></item><item><title>第 1 章讲义 · 01 LLM API 与模型调用基础</title><link>https://haokw.github.io/posts/2026-08-10-ch1-lecture-01-llm-api-basics/</link><pubDate>Mon, 10 Aug 2026 19:20:00 +0800</pubDate><guid>https://haokw.github.io/posts/2026-08-10-ch1-lecture-01-llm-api-basics/</guid><description>&lt;blockquote&gt;
&lt;p&gt;配套代码：本仓库的 provider 层（&lt;code&gt;src/anna/providers/&lt;/code&gt;）与 LLM 统一模型调用服务
（&lt;code&gt;src/anna/server/&lt;/code&gt;、&lt;code&gt;main.py&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;学完本节，你应该能回答：一次 LLM 调用请求长什么样？参数怎么影响输出？
token 怎么算？怎么让模型稳定返回 JSON？出错怎么办？多个模型怎么统一调？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="目录"&gt;目录&lt;a href="#%e7%9b%ae%e5%bd%95" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a
 href="#1-chat-completions--responses-api"
 
 
&gt;Chat Completions / Responses API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#2-%e6%a8%a1%e5%9e%8b%e5%8f%82%e6%95%b0temperaturetoppmax_tokens"
 
 
&gt;模型参数：temperature、top_p、max_tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#3-token-%e4%b8%8e-context-window"
 
 
&gt;Token 与 Context Window&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#4-structured-output"
 
 
&gt;Structured Output&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#5-%e9%94%99%e8%af%af%e7%b1%bb%e5%9e%8b%e4%b8%8e%e5%bc%82%e5%b8%b8%e5%a4%84%e7%90%86"
 
 
&gt;错误类型与异常处理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
 href="#6-%e5%a4%9a%e6%a8%a1%e5%9e%8b%e8%b0%83%e7%94%a8%e5%9f%ba%e7%a1%80"
 
 
&gt;多模型调用基础&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="1-chat-completions--responses-api"&gt;1. Chat Completions / Responses API&lt;a href="#1-chat-completions--responses-api" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="11-请求结构以-openai-chat-completions-为标准"&gt;1.1 请求结构：以 OpenAI Chat Completions 为标准&lt;a href="#11-%e8%af%b7%e6%b1%82%e7%bb%93%e6%9e%84%e4%bb%a5-openai-chat-completions-%e4%b8%ba%e6%a0%87%e5%87%86" class="post-heading__anchor" aria-hidden="true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;主流模型 API 基本都是&amp;quot;HTTP POST + JSON 请求体&amp;quot;，OpenAI 的
&lt;code&gt;POST /v1/chat/completions&lt;/code&gt; 是事实标准。先看完整请求：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s https://api.openai.com/v1/chat/completions &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;$OPENAI_API_KEY&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;model&amp;#34;: &amp;#34;gpt-4o-mini&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;messages&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; {&amp;#34;role&amp;#34;: &amp;#34;system&amp;#34;, &amp;#34;content&amp;#34;: &amp;#34;你是英语陪练 Anna。&amp;#34;},
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; {&amp;#34;role&amp;#34;: &amp;#34;user&amp;#34;, &amp;#34;content&amp;#34;: &amp;#34;什么是 token？&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;temperature&amp;#34;: 0.7,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;max_tokens&amp;#34;: 200,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;stream&amp;#34;: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请求体字段（重要程度递减）：&lt;/p&gt;</description></item></channel></rss>