普通视图

Received before yesterday技术博客

使用tun2socks实现全局代理

2026年6月17日 00:00

修改系统路由表的操作具有一定风险性,须谨慎操作。

有时在纯命令行环境下的linux,执行某些命令操作需要设置代理,比如新冠疫情期间居家办公时,通过git clone从公司内网拉取项目,就需要连接公司的代理服务器,一些命令可以通过追加控制台代理参数export http_proxy实现从代理服务器访问目标地址,但是并不是所有的命令都支持控制台代理。

tun2socks(https://github.com/xjasonlyu/tun2socks/)是一个基于go语言的,支持多个平台的开源项目,可以在更深的层面彻底解决代理上网的问题,不同于控制台代理或命令自身支持设置代理服务器的方式,tun2socks将虚拟网络适配器从底层拦截并发送来的tcp/ip数据进行解包,将解离出的应用层数据包发送给sock/http代理服务器,并将代理服务器返回的数据再封装为tcp/ip数据包传递给虚拟网络适配器,虚拟网络适配器继而再传递给具体进程。

例如当前的linux运行在虚拟机,地址是192.168.228.105,网关是192.168.228.2,代理服务器安装在宿主机192.168.228.1

首先ip addr查看本地网络情况,可见机器上enp2s0网卡是连接本地内网的网卡,地址是192.168.228.105

[root@java105 ~]# ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever    inet6 ::1/128 scope host        valid_lft forever preferred_lft forever2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000    link/ether 00:0c:29:8c:08:f8 brd ff:ff:ff:ff:ff:ff    inet 192.168.228.105/24 brd 192.168.228.255 scope global noprefixroute enp2s0       valid_lft forever preferred_lft forever    inet6 fe80::20c:29ff:fe8c:8f8/64 scope link noprefixroute        valid_lft forever preferred_lft forever

然后ip route查看系统路由表,通过default via 192.168.228.2 dev enp2s0可见enp2s0是默认出口,访问所有不在路由表中规定的目标地址,都通过enp2s0出站

[root@java105 ~]# ip routedefault via 192.168.228.2 dev enp2s0 proto dhcp src 192.168.228.105 metric 100 192.168.228.0/24 dev enp2s0 proto kernel scope link src 192.168.228.105 metric 100

然后,在使用tun2socks前,要先新建一个新的虚拟网卡tun0,随便分配个和现有不冲突的198.18.0.1/15网段,从网络层接管请求

# 创建 tun0 设备,模式为 tunip tuntap add mode tun dev tun0# 给它配个IP并启用ip addr add 198.18.0.1/15 dev tun0ip link set dev tun0 up

再次查看本地网卡信息和路由表,已经能见到tun0了,并且状态是state UP

[root@java105 ~]# ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever    inet6 ::1/128 scope host        valid_lft forever preferred_lft forever2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000    link/ether 00:0c:29:8c:08:f8 brd ff:ff:ff:ff:ff:ff    inet 192.168.228.105/24 brd 192.168.228.255 scope global noprefixroute enp2s0       valid_lft forever preferred_lft forever    inet6 fe80::20c:29ff:fe8c:8f8/64 scope link noprefixroute        valid_lft forever preferred_lft forever3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 500    link/none     inet 198.18.0.1/15 scope global tun0       valid_lft forever preferred_lft forever    inet6 fe80::9601:d30a:4eec:d2e5/64 scope link stable-privacy        valid_lft forever preferred_lft forever
[root@java105 ~]# ip routedefault via 192.168.228.2 dev enp2s0 proto dhcp src 192.168.228.105 metric 100 192.168.228.0/24 dev enp2s0 proto kernel scope link src 192.168.228.105 metric 100198.18.0.0/15 dev tun0 proto kernel scope link src 198.18.0.1 

将对应平台的tun2socks命令下载到任意目录,并运行起来,指定监听通往刚刚建好的虚拟网卡tun0的请求

nohup ./tun2socks-linux-amd64 -device tun0 -proxy socks5://192.168.228.1:7897 &

现在tun0已经在收集请求并由tun2socks处理了,但是系统默认路由还是指向enp2s0,各个进程的网络请求会涌向enp2s0而不是tun0,此时还要修改默认路由为tun0,将系统中各个进程的网络请求导入tun0

# 1. 删除现有默认路由ip route del default# 2. 添加新的默认路由,指向 tun0 接口ip route add default dev tun0

再次查看路由表

[root@java108 ~]# ip routedefault dev tun0 scope link192.168.228.0/24 dev enp2s0 proto kernel scope link src 192.168.228.105 metric 100198.18.0.0/15 dev tun0 proto kernel scope link src 198.18.0.1 

default已经指向了dev tun0,系统中所有tcp/udp请求都从网络层转发到dev tun0继而进入代理服务器socks5://192.168.228.1:7897

tun2socks作为一个进程当然也在不断发起网络请求到192.168.228.1,但是路由表中已经明确有了192.168.228.0/24 dev enp2s0 proto kernel scope link src 192.168.228.105 metric 100这一条,规定了通往192.168.228.0/24的请求从物理网卡dev enp2s0出站,因此不会导致死循环。

dev enp2s0是物理网卡当然是相对于dev tun0而言的

最后需注意,这种方式仅支持tcp/udp,不支持对icmp请求进行代理,因此测试验证时应该用curl或telnet而不是ping

LangGraph开篇

2026年3月24日 00:00

持续更新中

1.概述

LangChain就像一条流水线,对于一步接着一步的任务,用LangChain链式调用可以完美实现,但是很多任务很复杂,并不是一条直线完成的,需要根据各种条件走各种分支,这种复杂任务如果使用LangChain来编程实现,步骤就会很复杂,需要很多的if else判断。

当然解决这个问题还可以通过将任务做成多Agent,让Agent多次推理和决策并执行某个分支,但是这样消耗时间和token都很多,而且这个过程中智能体就是一个黑盒,不可控,不知道得推理多少次,也不知道内部是怎么执行的,过程难以监控和干预,这样一来这种复杂任务LangChain就不适用了,就可以使用LangGraph来实现。

LangGraph是基于LangChain构建的,面向智能体的可实现多轮交互、状态持久化和分支并行执行的图结构工作流框架,是LangChain的高级编排工具,LangGraph中,无论图结构有多复杂,单个任务的执行仍然是线性的,单个任务背后依然是依靠LangChain的链式执行实现的。

LangGraph(图) = 状态(State) + 节点(Node) + 边(Edge)

LangGraph将工作流程抽象为一种的有向图结构,LangGraph的图结构比LangChain的链结构,更加适合这种多分支复杂任务,具体表现为:

  1. LangGraph提供了强大的状态管理控制,允许Agent在不同节点之间传递和维护信息,实现长期记忆和多轮对话能力,通过定义节点和边,可以精确控制Agent执行逻辑,包括条件分支,循环等。

  2. LangGraph能够无缝集成各种外部工具(搜索引擎,数据库,API等),让Agent能够实时获取信息,执行特定操作,拓展了LLM的能力边界。

  3. LangGraph使得Agent的运行路径清晰可见,便于理解Agent的决策过程,出现问题易于定位和调试

  4. LangGraph使得模块具有可复用性,各个节点都是独立的,可复用的组件,维护性高,易于拓展,还能通过子图机制将复杂的工作流拆解为多个可独立开发和测试的模块,提高开发测试效率。

2.快速开始

基于Python 3.12.x

pip install -U  langgraph

基于Graph API定义一个流程

from typing import TypedDictfrom langgraph.constants import STARTfrom langgraph.constants import ENDfrom langgraph.graph import StateGraph# 状态类class DemoState(TypedDict):    name: str    greeting: str# 节点def name(state: DemoState) -> dict:    new_name = f"Hello, { state['name'] }"    state['name'] = new_name    print(new_name)    return state# 节点def greeting(state: DemoState) -> dict:    new_greeting = f" Hi { state['greeting'] }"    state['greeting'] = new_greeting    print(new_greeting)    return stateif __name__ == "__main__":    graph = StateGraph(DemoState)    graph.add_node('name', name)    graph.add_node('greeting', greeting)    graph.add_edge(START, 'name')    graph.add_edge('name', 'greeting')    graph.add_edge('greeting', END)    app = graph.compile()    res = app.invoke({'name':'lzj', 'greeting':'hello'})    print(res)
Hello, lzj Hi hello{'name': 'Hello, lzj', 'greeting': ' Hi hello'}

LangGraph可以通过实现可视化

pip install grandalf
#... ...app = graph.compile()app.get_graph().print_ascii()
+-----------+  | __start__ |  +-----------+        *              *              *          +------+       | name |       +------+           *              *              *        +----------+   | greeting |   +----------+         *              *              *         +---------+    | __end__ |    +---------+ 
❌