├── .DS_Store ├── .gitignore ├── README.md ├── book.toml ├── gh-md-toc ├── images ├── .DS_Store ├── 20230307143748.png ├── 20230307143831.png ├── 20230307150247.png ├── 20230307150942.png ├── 20230307151628.png ├── 20230307151810.png ├── 20230307153602.png ├── 20230307155346.png ├── 20230307155459.png ├── 20230307155946.png ├── 20230307160048.png ├── 20230308122318.png ├── 20230313223714.jpeg └── 20230322123451.jpeg └── src ├── SUMMARY.md └── chapter_1.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 在写了一堆应用以后,我们打算[众筹一个GPT课程](https://subdeer.cn/product/3)。之前我以为现在GPT应用已经是红海了,但实际测试下来发现,GPT应用其实可以是非标准品。独有的提示词、独有的知识库、独有的工作流都可以做出独一无二的GPT体验。 2 | 3 | 在课程中我们将讲解如何设计、搭建和开发一个与众不同的商业GPT应用。 4 | 5 | # 如何快速开发一个OpenAI/GPT应用 6 | 7 | > 一个国内开发者的OpenAI/GPT的笔记 8 | 9 | 10 | 最近都在问,于是写个文档。本文希望用尽可能少的内容,讲清楚开发一个OpenAI/GPT应用必然用到的知识,内容主要聚焦在免费应用开发,商业化方案可以看看这篇文章:[《十分钟,给你开发的免费GPT应用加上收费功能》](https://a.ftqq.com/2023/04/18/api2d-developer-program/) 11 | 12 | 欢迎PR补充。 13 | 14 | ### AI/Automation开发交流群 15 | 16 | 1. 电报群 17 | 1. 微信群 18 | 19 | ![](images/20230322123451.jpeg) 20 | 21 | 22 | 目录 23 | ================= 24 | 25 | * [如何快速开发一个OpenAI/GPT应用](#如何快速开发一个openaigpt应用) 26 | * [ChatGPT && OpenAI 的关系](#chatgpt--openai-的关系) 27 | * [OpenAI API 接口能做什么](#openai-api-接口能做什么) 28 | * [chat completions 接口如何使用?](#chat-completions-接口如何使用) 29 | * [Stream 参数](#stream-参数) 30 | * [其他参数](#其他参数) 31 | * [Chat completions 接口如何计费?](#chat-completions-接口如何计费) 32 | * [chat completions 接口能做什么 ①](#chat-completions-接口能做什么-) 33 | * [chat completions 接口能做什么 ②](#chat-completions-接口能做什么--1) 34 | * [国内是否可以上线运营GPT相关业务?](#国内是否可以上线运营gpt相关业务) 35 | * [如何解决国内用户无法注册OpenAI账号、无法访问OpenAI接口的问题?](#如何解决国内用户无法注册openai账号无法访问openai接口的问题) 36 | * [注册OpenAI](#注册openai) 37 | * [访问OpenAI API](#访问openai-api) 38 | * [通过第三方接口访问](#通过第三方接口访问) 39 | * [如何避免 OpenAI 封禁账号 API权限](#如何避免-openai-封禁账号-api权限) 40 | * [如何知道 OpenAI 接口状态](#如何知道-openai-接口状态) 41 | 42 | 43 | ## ChatGPT && OpenAI 的关系 44 | 45 | ChatGPT 是 OpenAI 推出的应用,使用的是最新的模型;而 OpenAI 开放接口的模型是 gpt-3.5-turbo ,这个模型比 ChatGPT 应用要笨。但 ChatGPT 用的最新模型没有接口,只能通过无头浏览器等方式来使用(不稳定)。 46 | 47 | > 更新:目前已经开放了 gpt-4 ,当前尚未提供图片输入接口,使用方式和 gpt-3.5-turbo 一致,只需要将 model 参数更换为 gpt-4 ,注意 gpt-4 的 max tokens 为 8k (gpt-4-32k 为 32k),Token 价格是 3.5 的 15~30 倍。 48 | 49 | ## OpenAI API 接口能做什么 50 | 51 | 能做的事情很多,可以查看[官方文档](https://platform.openai.com/docs),但这个文档中国网络目前无法访问。 52 | 53 | ![](images/20230307155346.png) 54 | 55 | 具体来讲,OpenAI 所有的可用的接口都在里边,包括语音识别和图片生成。但真正智能的其实只有 `gpt-3.5-turbo`,因此刚开始不用看其他内容。 56 | 57 | 目前大家看到的绝大部分GPT类应用都是由 `gpt-3.5-turbo` 模型的 `chat completions` 对话补全接口实现的。 58 | 59 | ![](images/20230307150247.png) 60 | 61 | ## chat completions 接口如何使用? 62 | 63 | 可以通过很多方式来使用,比如使用官方SDK,第三方项目,但其实只需要一个HTTP请求就可以。以下是官方文档给出的例子: 64 | 65 | ```bash 66 | curl https://api.openai.com/v1/chat/completions \ 67 | -H 'Content-Type: application/json' \ 68 | -H 'Authorization: Bearer YOUR_API_KEY' \ 69 | -d '{ 70 | "model": "gpt-3.5-turbo", 71 | "messages": [{"role": "user", "content": "Hello!"}] 72 | }' 73 | ``` 74 | 75 | 从里边可以看到,需要的信息有: 76 | 77 | ① 请求地址: `https://api.openai.com/v1/chat/completions` 这个地址目前在国内大部分地区已经无法访问了,后边会讲解决办法 78 | 79 | ② 最常用的接口参数包括: 80 | 81 | 1. model: 必填,建议使用 `gpt-3.5-turbo`,便宜。计费后边会讲。 82 | 1. messages: AI 进行提问的问题或信息。 83 | 1. max_tokens: 选填,指定生成回答的最大Token数。 84 | 1. stream: 选填,是否按流的方式发送内容。 85 | 86 | 其中 messages的格式为:`{"role","content"}`。一般用 `user` 发送用户问题;`system` 发送给模型提示信息。 87 | 88 | 例如: 89 | ```json 90 | [ 91 | {"role": "system", "content": "You are a helpful assistant that translates English to French."}, 92 | {"role": "user", "content": "Translate the following English text to French: {text}"} 93 | ] 94 | ``` 95 | 知道了这些基本就可以跑通GPT流程了,其他role可以稍后优化时来做。 96 | 97 | ### Stream 参数 98 | 99 | 这里单独说一下 stream 参数,当它设置为 true 时,API 会以 SSE( Server Side Event )方式返回内容。 100 | 101 | SSE 本质上还是 HTTP 协议,只不过它是一个长链接,先输出一个 `header("Content-Type: text/event-stream")` , 然后持续不断地输出内容直到完成。如果不是做实时聊天,建议直接false掉。 102 | 103 | 需要注意的是,开启stream 后,将不会返回 usage 信息,这对精准计费有影响 104 | 105 | ``` 106 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"我"},"index":0,"finish_reason":null}]} 107 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"没有"},"index":0,"finish_reason":null}]} 108 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"当前"},"index":0,"finish_reason":null}]} 109 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"日期"},"index":0,"finish_reason":null}]} 110 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"的"},"index":0,"finish_reason":null}]} 111 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"实"},"index":0,"finish_reason":null}]} 112 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"时"},"index":0,"finish_reason":null}]} 113 | {"id":"chatcmpl-6s3hNohxOliHi8zR7m5UTrLm4cWWc","object":"chat.completion.chunk","created":1678341949,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"信息"},"index":0,"finish_reason":null}]} 114 | ``` 115 | 116 | 117 | ### 其他参数 118 | 119 | 接口的其他参数可以看[官方文档](https://platform.openai.com/docs/api-reference/chat),访问不了的同学可以看我做的截图。 120 | 121 | ![](images/20230307143748.png) 122 | ![](images/20230307143831.png) 123 | 124 | ## Chat completions 接口如何计费? 125 | 126 | `chat completions` 接口按 token 计费,有一个专门的算法来计算 token。输入和输出全部都会计入到 token 里边,在 `chat completions` 接口的 `usage` 里边会有具体消耗的 token 数。 127 | 128 | 如果你要自己计算,可以用这个[在线表单](https://tiktokenizer.vercel.app),程序计算可以看看这两个项目: 129 | 130 | 1. https://github.com/dqbd/tiktokenizer 131 | 2. https://github.com/openai/tiktoken 132 | 133 | 134 | 除了 `gpt-3.5-turbo` 模型的 `chat completions` 接口,还有 `text-davinci-003` 模型的 `text completions` 接口可以用,但是价格更贵,效果更差 🤣 135 | 136 | 你可以在 查询到价格,以下是3月中旬的定价 137 | 138 | | Model | Usage | 139 | | --- | --- | 140 | | gpt-3.5-turbo (ChatGPT) | $0.002 / 1K tokens | 141 | | Davinci (InstructGPT) | $0.0200 / 1K tokens | 142 | | Ada (InstructGPT) | $0.0004 / 1K tokens | 143 | | Babbage (InstructGPT) | $0.0005 / 1K tokens | 144 | | Curie (InstructGPT) | $0.0020 / 1K tokens | 145 | 146 | 147 | ## chat completions 接口能做什么 ① 148 | 149 | 虽然 `chat completions` 看起来像是一个聊天接口,但接口设计上并没有为聊天优化,因为这个接口是记不住上下文的。 150 | 151 | 为了让对话具有连续性,我们每次请求需要带上上次的聊天记录。有多种方式解决这个问题,一个是直接在messages参数中加上聊天记录。其中,GPT返回的内容用 `assistant` role。 152 | 153 | ```json 154 | [ 155 | {"role": "system", "content": "You are a helpful assistant."}, 156 | {"role": "user", "content": "Who won the world series in 2020?"}, 157 | {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."}, 158 | {"role": "user", "content": "Where was it played?"} 159 | ] 160 | ``` 161 | 162 | 另一个方式是使用第三方库,比如`chatgpt-api`,它可以自动帮你发送聊天记录(通过指定对话的`parentMessageId`实现): 163 | 164 | 1. 165 | 166 | ![](images/20230307150942.png) 167 | 168 | 在加上对话记录后,`chat completions` 接口就可以制作一个看起来有智能的聊天应用了。 169 | 170 | 171 | 172 | > 如果你要在国内运营聊天机器人之类的话,请记得将内容通过文本内容审核接口进行审核,否则很可能导致被封。 173 | 174 | ## chat completions 接口能做什么 ② 175 | 176 | 其实除了对话,GPT有很强的内容总结归纳能力,另外由于它能理解内容结构,同时本身又是语言模型,因此对结构化翻译很擅长。 177 | 178 | 比如,我经常用它翻译JSON和Markdown,大部分情况下效果很好。在自用体验很好的情况下,我们可以将其制作为应用。 179 | 180 | ![](images/20230307151810.png) 181 | 182 | 应用开发非常简单,我只用一天时间开发了[AiBox](https://ai.ftqq.com/),按基本的web应用开发就可以,重点说几个细节: 183 | 184 | 1. 提示词:直接把提示词以 system 的 role 提交就可以。 185 | 1. Key问题:开发者的Key肯定是不够用的,因此一般会让使用者填写自己的Key。但是国内用户没有海外手机号,无法申请key;申请下来API直接访问也不通,解决方案有几种,后边专门讲 186 | 1. Token计算和限制问题:如果使用者用自己的Key,为了提升体验,我们可以提供一个Token计算,让用户知道自己的会花多少钱。另外如果你没有用第三方那个库来分拆,那么一次请求的内容不要超过 max_tokens 的限制。这个值一般是 4096。 187 | 188 | ## 国内是否可以上线运营GPT相关业务? 189 | 190 | 就目前而言,我了解到的情况是大部分企业没有收到明确禁止运营GPT相关业务的通知,但在国内运营要做好内容安全,比如对接口返回的内容再过一层内容审核。否则如果在应用中出现违规内容被举报,就会被封禁。 191 | 192 | 但这是一个随时可能变化的情况,我们准备了[一个issue](https://github.com/easychen/openai-api-proxy/issues/11)供大家反馈。 193 | 194 | ## 如何解决国内用户无法注册OpenAI账号、无法访问OpenAI接口的问题? 195 | 196 | 两个思路,一个是绕道海外去注册,通过代理使用服务;另一个是直接使用第三方代理API服务。前者可以暂时解决当前的问题;后者更方便省心。 197 | 198 | ### 注册OpenAI 199 | 200 | 1. 准备一个海外的网络 201 | 1. 准备一个海外手机号来接收验证短信,可以用[海外虚拟号码](https://sms-activate.org/?ref=4207095) 202 | 203 | 注册完成后,进入[API页面](https://openai.com/api/) 创建Key,然后就可以使用了。 204 | 205 | 这个方案目前可行,是因为OpenAI给每个新用户提供了18美金的免费额度。但是一旦不再提供,就会面临充值的问题。目前OpenAI不接受中国信用卡,因此还必须准备一个海外信用卡。也就是说,要长久稳定的使用,必须有海外信用卡。 206 | 207 | 以前有财付通的海外虚拟信用卡,后来服务下线了。最近看了下,很多500RMB起,还只支持电商网站,感觉不太靠谱 🤣 208 | 209 | ### 访问OpenAI API 210 | 211 | 3月3日开始,国内大部分网络不再能直接访问 OpenAI 接口。 212 | 213 | ![](images/20230307153602.png) 214 | 215 | 因此你需要架设代理来访问OpenAI 接口。你可以将整个服务器代理到海外网络,或者只是简单的通过 Cloudflare 或者 腾讯云函数来部署API代理。 216 | 217 | 如果你准备使用腾讯云函数,[教程可以看这里](https://github.com/easychen/openai-api-proxy/blob/master/FUNC.md) 218 | 219 | ![](images/20230307155459.png) 220 | 221 | 需要注意的是,腾讯云API代理会将长连接内容一次性返回,因此流式体验不明显。当然,有同学说腾讯云的 ApiGateway 直接就能代理,但我测试了下没成功。 222 | 223 | ### 通过第三方接口访问 224 | 225 | 如果你搞不定海外手机号和信用卡,或者自己不想架设代理,那么可以考虑用像[API2D](https://api2d.com)这样的第三方代理API。 226 | 227 | 主要的优点: 228 | 229 | 1. 基本兼容原有接口,只需要改下 API endpoint 和 Key 230 | 1. 接口国内直接可以访问,无需架设代理 231 | 1. 支持微信和国内卡充值,提供最小0.5美金/3.5人民币的测试档位,GitHub注册还有50点免费额度试用 232 | 1. 添加 moderation 参数,可以返回内容审核结果,省事 233 | 1. 推荐可以获得点数,这里是我的[推荐链接](https://api2d.com/r/186008) 234 | 235 | 缺点: 236 | 237 | 1. ~~不支持 stream 参数~~,已经支持 stream 238 | 1. 目前只支持 chat 和 embeddings 接口 239 | 1. 价格比官方略高,大概1.5倍,当然这个包含了流量中转的成本 240 | 241 | > 利益相关:api2d这个产品是作者加拿大的朋友做的,而且作为早期用户一直在重度使用 242 | 243 | ## 如何避免 OpenAI 封禁账号 API权限 244 | 245 | 最近得到反馈,很多架设香港代理的账号收到了邮件被禁用了权限。经过群里大家的讨论,总结的经验如下: 246 | 247 | 1. 不要使用 OpenAI 不服务地区的代理 248 | 1. 虚拟海外手机号更可能导致账号被封 249 | 1. 绑定信用卡可以大幅提升账号存活率 250 | 251 | ## 如何知道 OpenAI 接口状态 252 | 253 | OpenAI官方提供了一个[状态页](https://status.openai.com/),虽然小故障不怎么显示,但大面积宕机时能看到公告。 254 | 255 | ![image](https://user-images.githubusercontent.com/1294760/223604103-4093bdd4-4455-4f55-a294-fb7003325000.png) 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Easy"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "OpenAI/GPT开发笔记" 7 | -------------------------------------------------------------------------------- /gh-md-toc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Steps: 5 | # 6 | # 1. Download corresponding html file for some README.md: 7 | # curl -s $1 8 | # 9 | # 2. Discard rows where no substring 'user-content-' (github's markup): 10 | # awk '/user-content-/ { ... 11 | # 12 | # 3.1 Get last number in each row like ' ... sitemap.js.*<\/h/)+2, RLENGTH-5) 21 | # 22 | # 5. Find anchor and insert it inside "(...)": 23 | # substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) 24 | # 25 | 26 | gh_toc_version="0.8.0" 27 | 28 | gh_user_agent="gh-md-toc v$gh_toc_version" 29 | 30 | # 31 | # Download rendered into html README.md by its url. 32 | # 33 | # 34 | gh_toc_load() { 35 | local gh_url=$1 36 | 37 | if type curl &>/dev/null; then 38 | curl --user-agent "$gh_user_agent" -s "$gh_url" 39 | elif type wget &>/dev/null; then 40 | wget --user-agent="$gh_user_agent" -qO- "$gh_url" 41 | else 42 | echo "Please, install 'curl' or 'wget' and try again." 43 | exit 1 44 | fi 45 | } 46 | 47 | # 48 | # Converts local md file into html by GitHub 49 | # 50 | # -> curl -X POST --data '{"text": "Hello world github/linguist#1 **cool**, and #1!"}' https://api.github.com/markdown 51 | #

Hello world github/linguist#1 cool, and #1!

'" 52 | gh_toc_md2html() { 53 | local gh_file_md=$1 54 | local skip_header=$2 55 | 56 | URL=https://api.github.com/markdown/raw 57 | 58 | if [ ! -z "$GH_TOC_TOKEN" ]; then 59 | TOKEN=$GH_TOC_TOKEN 60 | else 61 | TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" 62 | if [ -f "$TOKEN_FILE" ]; then 63 | TOKEN="$(cat $TOKEN_FILE)" 64 | fi 65 | fi 66 | if [ ! -z "${TOKEN}" ]; then 67 | AUTHORIZATION="Authorization: token ${TOKEN}" 68 | fi 69 | 70 | local gh_tmp_file_md=$gh_file_md 71 | if [ "$skip_header" = "yes" ]; then 72 | if grep -Fxq "" "$gh_src"; then 73 | # cut everything before the toc 74 | gh_tmp_file_md=$gh_file_md~~ 75 | sed '1,//d' $gh_file_md > $gh_tmp_file_md 76 | fi 77 | fi 78 | 79 | # echo $URL 1>&2 80 | OUTPUT=$(curl -s \ 81 | --user-agent "$gh_user_agent" \ 82 | --data-binary @"$gh_tmp_file_md" \ 83 | -H "Content-Type:text/plain" \ 84 | -H "$AUTHORIZATION" \ 85 | "$URL") 86 | 87 | rm -f $gh_file_md~~ 88 | 89 | if [ "$?" != "0" ]; then 90 | echo "XXNetworkErrorXX" 91 | fi 92 | if [ "$(echo "${OUTPUT}" | awk '/API rate limit exceeded/')" != "" ]; then 93 | echo "XXRateLimitXX" 94 | else 95 | echo "${OUTPUT}" 96 | fi 97 | } 98 | 99 | 100 | # 101 | # Is passed string url 102 | # 103 | gh_is_url() { 104 | case $1 in 105 | https* | http*) 106 | echo "yes";; 107 | *) 108 | echo "no";; 109 | esac 110 | } 111 | 112 | # 113 | # TOC generator 114 | # 115 | gh_toc(){ 116 | local gh_src=$1 117 | local gh_src_copy=$1 118 | local gh_ttl_docs=$2 119 | local need_replace=$3 120 | local no_backup=$4 121 | local no_footer=$5 122 | local indent=$6 123 | local skip_header=$7 124 | 125 | if [ "$gh_src" = "" ]; then 126 | echo "Please, enter URL or local path for a README.md" 127 | exit 1 128 | fi 129 | 130 | 131 | # Show "TOC" string only if working with one document 132 | if [ "$gh_ttl_docs" = "1" ]; then 133 | 134 | echo "Table of Contents" 135 | echo "=================" 136 | echo "" 137 | gh_src_copy="" 138 | 139 | fi 140 | 141 | if [ "$(gh_is_url "$gh_src")" == "yes" ]; then 142 | gh_toc_load "$gh_src" | gh_toc_grab "$gh_src_copy" "$indent" 143 | if [ "${PIPESTATUS[0]}" != "0" ]; then 144 | echo "Could not load remote document." 145 | echo "Please check your url or network connectivity" 146 | exit 1 147 | fi 148 | if [ "$need_replace" = "yes" ]; then 149 | echo 150 | echo "!! '$gh_src' is not a local file" 151 | echo "!! Can't insert the TOC into it." 152 | echo 153 | fi 154 | else 155 | local rawhtml=$(gh_toc_md2html "$gh_src" "$skip_header") 156 | if [ "$rawhtml" == "XXNetworkErrorXX" ]; then 157 | echo "Parsing local markdown file requires access to github API" 158 | echo "Please make sure curl is installed and check your network connectivity" 159 | exit 1 160 | fi 161 | if [ "$rawhtml" == "XXRateLimitXX" ]; then 162 | echo "Parsing local markdown file requires access to github API" 163 | echo "Error: You exceeded the hourly limit. See: https://developer.github.com/v3/#rate-limiting" 164 | TOKEN_FILE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" 165 | echo "or place GitHub auth token here: ${TOKEN_FILE}" 166 | exit 1 167 | fi 168 | local toc=`echo "$rawhtml" | gh_toc_grab "$gh_src_copy" "$indent"` 169 | echo "$toc" 170 | if [ "$need_replace" = "yes" ]; then 171 | if grep -Fxq "" "$gh_src" && grep -Fxq "" "$gh_src"; then 172 | echo "Found markers" 173 | else 174 | echo "You don't have or in your file...exiting" 175 | exit 1 176 | fi 177 | local ts="<\!--ts-->" 178 | local te="<\!--te-->" 179 | local dt=`date +'%F_%H%M%S'` 180 | local ext=".orig.${dt}" 181 | local toc_path="${gh_src}.toc.${dt}" 182 | local toc_createdby="" 183 | local toc_footer="" 184 | # http://fahdshariff.blogspot.ru/2012/12/sed-mutli-line-replacement-between-two.html 185 | # clear old TOC 186 | sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" 187 | # create toc file 188 | echo "${toc}" > "${toc_path}" 189 | if [ "${no_footer}" != "yes" ]; then 190 | echo -e "\n${toc_createdby}\n${toc_footer}\n" >> "$toc_path" 191 | fi 192 | 193 | # insert toc file 194 | if ! sed --version > /dev/null 2>&1; then 195 | sed -i "" "/${ts}/r ${toc_path}" "$gh_src" 196 | else 197 | sed -i "/${ts}/r ${toc_path}" "$gh_src" 198 | fi 199 | echo 200 | if [ "${no_backup}" = "yes" ]; then 201 | rm "$toc_path" "$gh_src$ext" 202 | fi 203 | echo "!! TOC was added into: '$gh_src'" 204 | if [ -z "${no_backup}" ]; then 205 | echo "!! Origin version of the file: '${gh_src}${ext}'" 206 | echo "!! TOC added into a separate file: '${toc_path}'" 207 | fi 208 | echo 209 | fi 210 | fi 211 | } 212 | 213 | # 214 | # Grabber of the TOC from rendered html 215 | # 216 | # $1 - a source url of document. 217 | # It's need if TOC is generated for multiple documents. 218 | # $2 - number of spaces used to indent. 219 | # 220 | gh_toc_grab() { 221 | common_awk_script=' 222 | modified_href = "" 223 | split(href, chars, "") 224 | for (i=1;i <= length(href); i++) { 225 | c = chars[i] 226 | res = "" 227 | if (c == "+") { 228 | res = " " 229 | } else { 230 | if (c == "%") { 231 | res = "\\x" 232 | } else { 233 | res = c "" 234 | } 235 | } 236 | modified_href = modified_href res 237 | } 238 | print sprintf("%*s", (level-1)*'"$2"', "") "* [" text "](" gh_url modified_href ")" 239 | ' 240 | if [ `uname -s` == "OS/390" ]; then 241 | grepcmd="pcregrep -o" 242 | echoargs="" 243 | awkscript='{ 244 | level = substr($0, length($0), 1) 245 | text = substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5) 246 | href = substr($0, match($0, "href=\"([^\"]+)?\"")+6, RLENGTH-7) 247 | '"$common_awk_script"' 248 | }' 249 | else 250 | grepcmd="grep -Eo" 251 | echoargs="-e" 252 | awkscript='{ 253 | level = substr($0, length($0), 1) 254 | text = substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5) 255 | href = substr($0, match($0, "href=\"[^\"]+?\"")+6, RLENGTH-7) 256 | '"$common_awk_script"' 257 | }' 258 | fi 259 | href_regex='href=\"[^\"]+?\"' 260 | 261 | # if closed is on the new line, then move it on the prev line 262 | # for example: 263 | # was: The command foo1 264 | # 265 | # became: The command foo1 266 | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' | 267 | 268 | # find strings that corresponds to template 269 | $grepcmd '//g' | sed 's/<\/code>//g' | 273 | 274 | # remove g-emoji 275 | sed 's/]*[^<]*<\/g-emoji> //g' | 276 | 277 | # now all rows are like: 278 | # ... /dev/null`; then 305 | echo `$tool --version | head -n 1` 306 | else 307 | echo "not installed" 308 | fi 309 | done 310 | } 311 | 312 | show_help() { 313 | local app_name=$(basename "$0") 314 | echo "GitHub TOC generator ($app_name): $gh_toc_version" 315 | echo "" 316 | echo "Usage:" 317 | echo " $app_name [options] src [src] Create TOC for a README file (url or local path)" 318 | echo " $app_name - Create TOC for markdown from STDIN" 319 | echo " $app_name --help Show help" 320 | echo " $app_name --version Show version" 321 | echo "" 322 | echo "Options:" 323 | echo " --indent Set indent size. Default: 3." 324 | echo " --insert Insert new TOC into original file. For local files only. Default: false." 325 | echo " See https://github.com/ekalinin/github-markdown-toc/issues/41 for details." 326 | echo " --no-backup Remove backup file. Set --insert as well. Default: false." 327 | echo " --hide-footer Do not write date & author of the last TOC update. Set --insert as well. Default: false." 328 | echo " --skip-header Hide entry of the topmost headlines. Default: false." 329 | echo " See https://github.com/ekalinin/github-markdown-toc/issues/125 for details." 330 | echo "" 331 | } 332 | 333 | # 334 | # Options handlers 335 | # 336 | gh_toc_app() { 337 | local need_replace="no" 338 | local indent=3 339 | 340 | if [ "$1" = '--help' ] || [ $# -eq 0 ] ; then 341 | show_help 342 | return 343 | fi 344 | 345 | if [ "$1" = '--version' ]; then 346 | show_version 347 | return 348 | fi 349 | 350 | if [ "$1" = '--indent' ]; then 351 | indent="$2" 352 | shift 2 353 | fi 354 | 355 | if [ "$1" = "-" ]; then 356 | if [ -z "$TMPDIR" ]; then 357 | TMPDIR="/tmp" 358 | elif [ -n "$TMPDIR" -a ! -d "$TMPDIR" ]; then 359 | mkdir -p "$TMPDIR" 360 | fi 361 | local gh_tmp_md 362 | if [ `uname -s` == "OS/390" ]; then 363 | local timestamp=$(date +%m%d%Y%H%M%S) 364 | gh_tmp_md="$TMPDIR/tmp.$timestamp" 365 | else 366 | gh_tmp_md=$(mktemp $TMPDIR/tmp.XXXXXX) 367 | fi 368 | while read input; do 369 | echo "$input" >> "$gh_tmp_md" 370 | done 371 | gh_toc_md2html "$gh_tmp_md" | gh_toc_grab "" "$indent" 372 | return 373 | fi 374 | 375 | if [ "$1" = '--insert' ]; then 376 | need_replace="yes" 377 | shift 378 | fi 379 | 380 | if [ "$1" = '--no-backup' ]; then 381 | need_replace="yes" 382 | no_backup="yes" 383 | shift 384 | fi 385 | 386 | if [ "$1" = '--hide-footer' ]; then 387 | need_replace="yes" 388 | no_footer="yes" 389 | shift 390 | fi 391 | 392 | if [ "$1" = '--skip-header' ]; then 393 | skip_header="yes" 394 | shift 395 | fi 396 | 397 | 398 | for md in "$@" 399 | do 400 | echo "" 401 | gh_toc "$md" "$#" "$need_replace" "$no_backup" "$no_footer" "$indent" "$skip_header" 402 | done 403 | 404 | echo "" 405 | echo "" 406 | } 407 | 408 | # 409 | # Entry point 410 | # 411 | gh_toc_app "$@" 412 | -------------------------------------------------------------------------------- /images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/.DS_Store -------------------------------------------------------------------------------- /images/20230307143748.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307143748.png -------------------------------------------------------------------------------- /images/20230307143831.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307143831.png -------------------------------------------------------------------------------- /images/20230307150247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307150247.png -------------------------------------------------------------------------------- /images/20230307150942.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307150942.png -------------------------------------------------------------------------------- /images/20230307151628.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307151628.png -------------------------------------------------------------------------------- /images/20230307151810.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307151810.png -------------------------------------------------------------------------------- /images/20230307153602.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307153602.png -------------------------------------------------------------------------------- /images/20230307155346.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307155346.png -------------------------------------------------------------------------------- /images/20230307155459.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307155459.png -------------------------------------------------------------------------------- /images/20230307155946.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307155946.png -------------------------------------------------------------------------------- /images/20230307160048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230307160048.png -------------------------------------------------------------------------------- /images/20230308122318.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230308122318.png -------------------------------------------------------------------------------- /images/20230313223714.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230313223714.jpeg -------------------------------------------------------------------------------- /images/20230322123451.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/openai-gpt-dev-notes-for-cn-developer/4d97b2d128cf193ca3aca6556a4daf153d7896f0/images/20230322123451.jpeg -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | --------------------------------------------------------------------------------