AI Agent 跑半小时就忘目标?用检查点与任务账本做可恢复长任务,cpolar 分享只读时间线

AI Agent 跑半小时就忘目标?用检查点与任务账本做可恢复长任务,cpolar 分享只读时间线

一个长任务最让人崩溃的,不是某一步报错,而是跑了半小时后进程一断,重启时连"做过什么、花了多少预算、哪些产物可信"都说不清。只靠内存里的消息列表,任务越长,返工越狠。

这篇不接模型,也不拿真实文档做实验。我用三份虚构文本搭一个可直接运行的小项目:Python 标准库负责执行器,SQLite 记录 planstepartifacterror 与检查点;进程中断后跳过成功步骤,产物按唯一键覆盖写入,预算和重试次数都能硬性截断。结尾再生成一张独立静态时间线,只把这张只读页面交给手机验收。

这和模型评测、Prompt 对照不是一回事。这里关心的是任务状态有没有落盘、失败后从哪里续跑、重复运行会不会制造重复产物

图:长任务先把状态落盘,再通过检查点恢复;公网只交付脱敏后的静态时间线。

1 先把长任务拆成可落盘的四类状态

示例任务是"整理三份虚构项目文档"。完整步骤如下:

  1. scan_inputs:登记文件路径与 SHA-256,不把原文塞进账本。
  2. summarize_alpha/beta/gamma:生成固定的脱敏摘要,只保留来源引用。
  3. human_review:到人工确认点后暂停,只有显式传入 --approve 才继续。
  4. build_catalog:汇总摘要产物。
  5. render_timeline:生成静态 HTML 时间线。

账本中的四类核心记录各管一件事:

  • plans 保存任务总状态、预算上限和已消耗预算。
  • steps 保存步骤顺序、状态、尝试次数与单步成本。
  • artifacts 保存产物引用、哈希和脱敏摘要,artifact_key 唯一。
  • errors 逐次记录错误类型、信息和失败发生在哪次尝试。

检查点单独放在 checkpoints 表。只有步骤成功后才写检查点,事务提交后,这个步骤才算真正完成。这里别把"函数开始执行"当检查点,否则进程恰好死在写文件中途,恢复时会错误跳过半成品。

图:四类账本记录各司其职,只有步骤与产物都成功后才提交检查点。

上下文压缩也有一条硬边界:账本和时间线只存脱敏摘要、文件引用、哈希,不存输入原文 。原文留在 inputs/,静态页只读取 events。即使把结果页临时分享出去,访问者也拿不到原始内容。

2 创建完整项目

环境只需要 Python 3.10 或更高版本,无第三方依赖。新建目录后放入下面两个文件。

2.1 主程序 agent_task.py

下面是完整代码。它同时包含建库、执行、恢复、重试、预算检查、人工确认、状态查询、时间线渲染和只绑定回环地址的静态服务器。

python 复制代码
#!/usr/bin/env python3
import argparse, hashlib, html, json, os, sqlite3, sys, time
from datetime import datetime, timezone
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path

STEPS = [
    ("scan_inputs", 1), ("summarize_alpha", 2), ("summarize_beta", 2),
    ("summarize_gamma", 2), ("human_review", 1),
    ("build_catalog", 2), ("render_timeline", 1),
]
SAMPLES = {
    "alpha.txt": "虚构项目甲:整理发布流程,负责人为演示用户A。文档仅用于本地测试。",
    "beta.txt": "虚构项目乙:核对三份材料,去除联系方式并生成主题摘要。",
    "gamma.txt": "虚构项目丙:输出只读目录,评审结束后关闭临时分享入口。",
}
SUMMARIES = {
    "alpha.txt": "虚构项目甲;主题:发布流程整理;敏感字段:已移除。",
    "beta.txt": "虚构项目乙;主题:材料核对与摘要;敏感字段:已移除。",
    "gamma.txt": "虚构项目丙;主题:只读目录与短时评审;敏感字段:已移除。",
}

def now(): return datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
def paths(root):
    r=Path(root).resolve(); return r, r/"ledger.sqlite3", r/"inputs", r/"artifacts", r/"public"
def connect(db):
    c=sqlite3.connect(db); c.row_factory=sqlite3.Row; c.execute("PRAGMA foreign_keys=ON"); return c
def schema(c):
    c.executescript("""
    CREATE TABLE IF NOT EXISTS plans(id INTEGER PRIMARY KEY, name TEXT, status TEXT, budget INTEGER, spent INTEGER DEFAULT 0, created_at TEXT, updated_at TEXT);
    CREATE TABLE IF NOT EXISTS steps(id INTEGER PRIMARY KEY, plan_id INTEGER, seq INTEGER, name TEXT UNIQUE, status TEXT, attempts INTEGER DEFAULT 0, cost INTEGER, started_at TEXT, finished_at TEXT, FOREIGN KEY(plan_id) REFERENCES plans(id));
    CREATE TABLE IF NOT EXISTS artifacts(id INTEGER PRIMARY KEY, step_name TEXT, artifact_key TEXT UNIQUE, ref TEXT, sha256 TEXT, summary TEXT, created_at TEXT);
    CREATE TABLE IF NOT EXISTS errors(id INTEGER PRIMARY KEY, step_name TEXT, kind TEXT, message TEXT, attempt INTEGER, created_at TEXT);
    CREATE TABLE IF NOT EXISTS checkpoints(id INTEGER PRIMARY KEY, step_name TEXT, step_seq INTEGER, created_at TEXT);
    CREATE TABLE IF NOT EXISTS events(id INTEGER PRIMARY KEY, kind TEXT, step_name TEXT, detail TEXT, created_at TEXT);
    """)
def event(c, kind, step="", detail=""): c.execute("INSERT INTO events(kind,step_name,detail,created_at) VALUES(?,?,?,?)",(kind,step,detail,now()))
def init(root, budget):
    r,db,inp,art,pub=paths(root)
    for d in (r,inp,art,pub): d.mkdir(parents=True,exist_ok=True)
    for name,text in SAMPLES.items(): (inp/name).write_text(text,encoding="utf-8")
    c=connect(db); schema(c); ts=now()
    if not c.execute("SELECT 1 FROM plans WHERE id=1").fetchone():
        c.execute("INSERT INTO plans VALUES(?,?,?,?,?,?,?)",(1,"fictional-doc-batch","ready",budget,0,ts,ts))
        c.executemany("INSERT INTO steps(plan_id,seq,name,status,cost) VALUES(1,?,?,?,?)",[(i,n,"pending",cost) for i,(n,cost) in enumerate(STEPS,1)])
        event(c,"plan_created",detail=f"budget={budget}")
    c.commit(); c.close(); print(f"initialized: {r}")
def write_artifact(c, artdir, step, key, content, summary):
    p=artdir/f"{key}.json"; data=json.dumps(content,ensure_ascii=False,indent=2)+"\n"
    p.write_text(data,encoding="utf-8"); digest=hashlib.sha256(data.encode()).hexdigest()
    c.execute("""INSERT INTO artifacts(step_name,artifact_key,ref,sha256,summary,created_at) VALUES(?,?,?,?,?,?)
      ON CONFLICT(artifact_key) DO UPDATE SET ref=excluded.ref,sha256=excluded.sha256,summary=excluded.summary""",
      (step,key,str(p),digest,summary,now()))
def do_step(c, root, inp, art, pub, name, approve):
    if name=="scan_inputs":
        refs=[{"ref":str(p),"sha256":hashlib.sha256(p.read_bytes()).hexdigest()} for p in sorted(inp.glob("*.txt"))]
        write_artifact(c,art,name,"input-index",{"documents":refs},"仅保存文件引用和哈希,不保存原文")
    elif name.startswith("summarize_"):
        filename=name.split("_",1)[1]+".txt"
        write_artifact(c,art,name,"summary-"+filename[:-4],{"source_ref":str(inp/filename),"sanitized_summary":SUMMARIES[filename]},SUMMARIES[filename])
    elif name=="human_review":
        if not approve: raise PermissionError("需要人工确认;重新运行时添加 --approve")
        write_artifact(c,art,name,"approval",{"decision":"approved","scope":"sanitized summaries only"},"人工已确认脱敏摘要")
    elif name=="build_catalog":
        rows=c.execute("SELECT artifact_key,ref,sha256,summary FROM artifacts WHERE artifact_key LIKE 'summary-%' ORDER BY artifact_key").fetchall()
        write_artifact(c,art,name,"catalog",{"items":[dict(x) for x in rows]},f"只读目录,共 {len(rows)} 项")
    elif name=="render_timeline": render(c,pub)
def render(c, pub):
    rows=c.execute("SELECT id,kind,step_name,detail,created_at FROM events ORDER BY id").fetchall()
    trs="".join(f"<tr><td>{x['id']}</td><td>{html.escape(x['created_at'])}</td><td>{html.escape(x['kind'])}</td><td>{html.escape(x['step_name'] or '-')}</td><td>{html.escape(x['detail'])}</td></tr>" for x in rows)
    doc=f"""<!doctype html><html lang='zh-CN'><meta charset='utf-8'><meta name='viewport' content='width=device-width'><title>只读任务时间线</title><style>body{{font:16px system-ui;margin:2rem;max-width:1100px}}table{{border-collapse:collapse;width:100%}}th,td{{border:1px solid #ddd;padding:.55rem;text-align:left}}th{{background:#f4f6f8}}.note{{padding:1rem;background:#eef7ff}}</style><h1>批量文档整理:只读时间线</h1><p class='note'>页面只含步骤状态与脱敏事件,不含原文、SQLite、密钥、执行按钮或管理接口。</p><table><thead><tr><th>#</th><th>时间</th><th>事件</th><th>步骤</th><th>详情</th></tr></thead><tbody>{trs}</tbody></table></html>"""
    (pub/"index.html").write_text(doc,encoding="utf-8")
def run(root, approve, interrupt_after, fail_step, max_retries, budget):
    r,db,inp,art,pub=paths(root)
    if not db.exists(): print("ledger missing; run init",file=sys.stderr); return 2
    c=connect(db); schema(c)
    if budget is not None: c.execute("UPDATE plans SET budget=?,updated_at=? WHERE id=1",(budget,now())); c.commit()
    completed_this_run=0
    for row in c.execute("SELECT * FROM steps WHERE plan_id=1 ORDER BY seq").fetchall():
        if row["status"]=="succeeded": continue
        if row["name"]=="human_review" and not approve:
            c.execute("UPDATE steps SET status='waiting_approval' WHERE id=?",(row["id"],)); event(c,"waiting_approval",row["name"],"需要 --approve"); c.commit(); print("paused: human approval required"); c.close(); return 3
        for retry in range(max_retries+1):
            plan=c.execute("SELECT * FROM plans WHERE id=1").fetchone()
            if plan["spent"]+row["cost"]>plan["budget"]:
                msg=f"budget exceeded: spent={plan['spent']}, next={row['cost']}, limit={plan['budget']}"
                c.execute("UPDATE plans SET status='budget_exceeded',updated_at=? WHERE id=1",(now(),)); c.execute("UPDATE steps SET status='blocked' WHERE id=?",(row["id"],)); c.execute("INSERT INTO errors(step_name,kind,message,attempt,created_at) VALUES(?,?,?,?,?)",(row["name"],"budget_exceeded",msg,row["attempts"]+1,now())); event(c,"budget_exceeded",row["name"],msg); c.commit(); print(msg); c.close(); return 4
            attempt=row["attempts"]+retry+1
            c.execute("UPDATE steps SET status='running',attempts=?,started_at=? WHERE id=?",(attempt,now(),row["id"])); c.execute("UPDATE plans SET spent=spent+?,status='running',updated_at=? WHERE id=1",(row["cost"],now())); event(c,"step_started",row["name"],f"attempt={attempt}"); c.commit()
            try:
                if fail_step==row["name"]: raise RuntimeError("injected failure for retry demo")
                do_step(c,r,inp,art,pub,row["name"],approve)
                c.execute("UPDATE steps SET status='succeeded',finished_at=? WHERE id=?",(now(),row["id"])); c.execute("INSERT INTO checkpoints(step_name,step_seq,created_at) VALUES(?,?,?)",(row["name"],row["seq"],now())); event(c,"step_succeeded",row["name"],f"checkpoint={row['seq']}"); c.commit(); completed_this_run+=1; print(f"succeeded: {row['name']}")
                break
            except Exception as e:
                c.execute("INSERT INTO errors(step_name,kind,message,attempt,created_at) VALUES(?,?,?,?,?)",(row["name"],type(e).__name__,str(e),attempt,now())); event(c,"step_failed",row["name"],f"attempt={attempt}"); c.execute("UPDATE steps SET status='failed' WHERE id=?",(row["id"],)); c.commit(); print(f"failed: {row['name']} attempt={attempt}")
                if retry==max_retries: c.execute("UPDATE plans SET status='failed',updated_at=? WHERE id=1",(now(),)); c.commit(); c.close(); return 2
                time.sleep(0.05)
        if interrupt_after and completed_this_run>=interrupt_after:
            event(c,"process_interrupted",row["name"],"simulated safe stop"); c.execute("UPDATE plans SET status='interrupted',updated_at=? WHERE id=1",(now(),)); c.commit(); print(f"interrupted after {completed_this_run} successful steps"); c.close(); return 75
    c.execute("UPDATE plans SET status='succeeded',updated_at=? WHERE id=1",(now(),)); event(c,"plan_succeeded",detail="all steps complete"); render(c,pub); c.commit(); c.close(); print("plan succeeded"); return 0
def status(root):
    _,db,_,_,_=paths(root); c=connect(db); p=c.execute("SELECT status,budget,spent FROM plans WHERE id=1").fetchone();
    print(json.dumps({"plan":dict(p),"steps":{x['name']:x['status'] for x in c.execute('SELECT name,status FROM steps ORDER BY seq')},"artifacts":c.execute('SELECT count(*) FROM artifacts').fetchone()[0],"errors":c.execute('SELECT count(*) FROM errors').fetchone()[0],"checkpoints":c.execute('SELECT count(*) FROM checkpoints').fetchone()[0]},ensure_ascii=False)); c.close()
def serve(root, port):
    _,_,_,_,pub=paths(root); os.chdir(pub); print(f"serving read-only timeline on http://127.0.0.1:{port}"); ThreadingHTTPServer(("127.0.0.1",port),SimpleHTTPRequestHandler).serve_forever()
def main():
    ap=argparse.ArgumentParser(); ap.add_argument("--root",default="demo-run"); sub=ap.add_subparsers(dest="cmd",required=True)
    p=sub.add_parser("init"); p.add_argument("--budget",type=int,default=20)
    p=sub.add_parser("run"); p.add_argument("--approve",action="store_true"); p.add_argument("--interrupt-after",type=int); p.add_argument("--fail-step"); p.add_argument("--max-retries",type=int,default=2); p.add_argument("--budget",type=int)
    sub.add_parser("status"); p=sub.add_parser("timeline"); p=sub.add_parser("serve"); p.add_argument("--port",type=int,default=8000)
    a=ap.parse_args()
    if a.cmd=="init": init(a.root,a.budget)
    elif a.cmd=="run": raise SystemExit(run(a.root,a.approve,a.interrupt_after,a.fail_step,a.max_retries,a.budget))
    elif a.cmd=="status": status(a.root)
    elif a.cmd=="timeline":
        _,db,_,_,pub=paths(a.root); c=connect(db); render(c,pub); c.close(); print(pub/"index.html")
    elif a.cmd=="serve": serve(a.root,a.port)
if __name__=="__main__": main()

2.2 项目说明 README.md

markdown 复制代码
# agent-ledger-demo

Python 3.10+,仅使用标准库。

```bash
python3 agent_task.py --root demo-run init --budget 20
python3 agent_task.py --root demo-run run --approve
python3 agent_task.py --root demo-run status
python3 agent_task.py --root demo-run serve --port 8000
```

先做语法检查,再创建一套预算为 30 的测试账本:

bash 复制代码
python3 -m py_compile agent_task.py
python3 agent_task.py --root demo-run init --budget 30

init 会写入三份虚构文本。它不会覆盖已有账本,因此初始化命令误执行两次也不会重建任务。

3 实测中断与从检查点恢复

3.1 首次运行在两个成功步骤后停下

--interrupt-after 2 是可控中断开关,便于稳定复现实验。它在第二个步骤成功、检查点已提交后以退出码 75 停止:

bash 复制代码
python3 agent_task.py --root demo-run run --approve --interrupt-after 2
echo $?
python3 agent_task.py --root demo-run status

我的实测输出是:

text 复制代码
succeeded: scan_inputs
succeeded: summarize_alpha
interrupted after 2 successful steps
75
{"plan": {"status": "interrupted", "budget": 30, "spent": 3}, "steps": {"scan_inputs": "succeeded", "summarize_alpha": "succeeded", "summarize_beta": "pending", "summarize_gamma": "pending", "human_review": "pending", "build_catalog": "pending", "render_timeline": "pending"}, "artifacts": 2, "errors": 0, "checkpoints": 2}

这一步不是为了"演示一个退出码",而是确认中断前的两次成功已经同时体现在步骤状态、产物数和检查点数中。若这里的 checkpoints 不是 2,先检查成功状态与检查点是否放在同一个事务提交周期内。

3.2 再次运行只执行剩余步骤

去掉中断参数,用同一个 --root 继续:

bash 复制代码
python3 agent_task.py --root demo-run run --approve
python3 agent_task.py --root demo-run status

实测恢复输出从 summarize_beta 开始,没有重跑前两个成功步骤:

text 复制代码
succeeded: summarize_beta
succeeded: summarize_gamma
succeeded: human_review
succeeded: build_catalog
succeeded: render_timeline
plan succeeded
{"plan": {"status": "succeeded", "budget": 30, "spent": 11}, "steps": {"scan_inputs": "succeeded", "summarize_alpha": "succeeded", "summarize_beta": "succeeded", "summarize_gamma": "succeeded", "human_review": "succeeded", "build_catalog": "succeeded", "render_timeline": "succeeded"}, "artifacts": 6, "errors": 0, "checkpoints": 7}

恢复依据不是聊天上下文,而是 SQLite 里已经提交的 steps.status='succeeded'。这就是任务重启后仍然认账的关键。

图:重启后跳过已成功步骤,从首个未完成步骤继续;唯一产物键避免重复记录。

4 验证幂等、重试、预算和人工确认

图:先记录、再判断、后执行,保证重试次数、成本预算与人工确认都能真正阻断下一步。

4.1 重复执行不增加 artifact

在已经成功的目录上再运行一次:

bash 复制代码
python3 agent_task.py --root demo-run run --approve
python3 agent_task.py --root demo-run status

实测第二次直接输出 plan succeeded,状态仍是 spent=11artifacts=6checkpoints=7。没有步骤被重复计费,也没有多出一份产物。

幂等做了两层保护:成功步骤会被执行器跳过;产物表的 artifact_key 设为唯一键,并使用 ON CONFLICT ... DO UPDATE。提醒一下,幂等不能只靠"文件名看着一样",数据库唯一约束才是并发或恢复场景里的兜底防线。

4.2 同一步失败三次后停止

另建目录,给 summarize_beta 注入固定错误;--max-retries 2 表示初次执行加两次重试,共三次:

bash 复制代码
python3 agent_task.py --root retry-run init --budget 30
python3 agent_task.py --root retry-run run --approve \
  --fail-step summarize_beta --max-retries 2
echo $?
python3 agent_task.py --root retry-run status

实测结果:

text 复制代码
failed: summarize_beta attempt=1
failed: summarize_beta attempt=2
failed: summarize_beta attempt=3
2
{"plan": {"status": "failed", "budget": 30, "spent": 9}, "steps": {"scan_inputs": "succeeded", "summarize_alpha": "succeeded", "summarize_beta": "failed", "summarize_gamma": "pending", "human_review": "pending", "build_catalog": "pending", "render_timeline": "pending"}, "artifacts": 2, "errors": 3, "checkpoints": 2}

每次尝试都会计入预算,并在 errors 留一条记录。真实 Agent 的工具调用同样应按尝试计费,不能只在最终成功时算成本。

4.3 预算不足时不启动下一步

把总预算限制为 2:

bash 复制代码
python3 agent_task.py --root budget-run init --budget 2
python3 agent_task.py --root budget-run run --approve
echo $?
python3 agent_task.py --root budget-run status

实测 scan_inputs 花费 1,下一步成本为 2,执行前就被截断:

text 复制代码
succeeded: scan_inputs
budget exceeded: spent=1, next=2, limit=2
4
{"plan": {"status": "budget_exceeded", "budget": 2, "spent": 1}, "steps": {"scan_inputs": "succeeded", "summarize_alpha": "blocked", "summarize_beta": "pending", "summarize_gamma": "pending", "human_review": "pending", "build_catalog": "pending", "render_timeline": "pending"}, "artifacts": 1, "errors": 1, "checkpoints": 1}

预算检查必须放在步骤启动之前。若先调用外部工具再记账,超限控制就只剩报表意义,阻止不了实际消耗。

4.4 人工确认点会主动暂停

这次不传 --approve

bash 复制代码
python3 agent_task.py --root approval-run init --budget 30
python3 agent_task.py --root approval-run run
echo $?
python3 agent_task.py --root approval-run status

前三份摘要生成后,程序停在 human_review,退出码为 3,状态为 waiting_approval。审核脱敏摘要后,再执行 python3 agent_task.py --root approval-run run --approve 才会生成目录和时间线。这里不要把确认参数写进默认配置,否则人工闸门形同虚设。

5 生成只读时间线并绑定 127.0.0.1

任务完成后,public/index.html 已经生成;也能随时重建:

bash 复制代码
python3 agent_task.py --root demo-run timeline
python3 agent_task.py --root demo-run serve --port 8000

服务器明确绑定 127.0.0.1:8000,不是 0.0.0.0。我用下面命令验收,返回 HTTP 200,页面大小为 2783 字节:

bash 复制代码
curl -I http://127.0.0.1:8000/

页面只有事件编号、时间、事件类型、步骤名和脱敏详情。ledger.sqlite3inputs/artifacts/ 都在 public/ 之外;服务器启动前还会把文档根目录切到 public/。如果浏览器能访问 /ledger.sqlite3,说明服务目录开错了,应立即停止服务并修正根目录。

需要强调:Python 官方文档明确说明 http.server 不推荐用于生产环境,它只实现基础安全检查。本文只把它用于短时、本地、纯静态验收,不是常驻站点方案。

6 用 cpolar 短时分享这一张静态页

图:SQLite、输入与执行接口保留在本机,cpolar 只转发绑定在 127.0.0.1 的脱敏静态页面。

先在本机访问 http://127.0.0.1:8000/,确认页面正确且没有原文。再开一个终端创建临时 HTTP 隧道:

bash 复制代码
cpolar http 8000

cpolar 官方文档支持用 cpolar http 端口 创建 HTTP 隧道。命令启动后,以终端实际显示的 HTTPS 公网地址为准;也能打开 http://127.0.0.1:9200,在"状态 → 在线隧道列表"查看地址。

手机验收按这四项走:

  1. 用移动网络打开该 HTTPS 地址,确认标题为"批量文档整理:只读时间线"。
  2. 抽查步骤状态、检查点事件和错误摘要,页面上不应出现输入原文。
  3. 尝试访问 /ledger.sqlite3,应返回 404;服务根目录只有 index.html
  4. 页面没有恢复按钮、上传入口、任务参数、管理接口和宿主机路径浏览能力。

评审结束后,在运行 cpolar http 8000 的终端按 Ctrl+C 关闭隧道,再在静态服务器终端按 Ctrl+C 停止本地服务。随后用手机刷新公网地址,确认它已无法继续访问。

这里分享的是一次性验收结果,不是 Agent 执行端。不要映射 SQLite 端口、工作目录、管理后台、模型密钥、文件上传接口,也不要把宿主机其他端口顺手放出去。

7 总结

这套小项目跑通后,长任务不再依赖进程内记忆:计划、步骤、错误、产物与检查点都落到 SQLite;中断后只执行未成功步骤,重复运行不会制造重复 artifact;重试、预算和人工确认也都有明确退出行为。对外验收只交付一张脱敏静态时间线,执行面仍留在本机。

  • 恢复靠状态:成功步骤与检查点提交后,重启直接跳过。
  • 安全靠隔离:摘要与引用进入账本,原文、数据库和执行入口不进入静态页。
  • 失控要能停:重试次数、总预算、人工确认点都在执行前生效。

如果要把它接进真正的 Agent,下一步应替换摘要函数和单步成本计算,而不是推翻账本结构。先把"做过什么、能否重做、何时必须停"解决好,再谈更长的上下文,排错会省事很多。

参考资料

相关推荐
张小姐的猫1 小时前
【AI大模型接入SDK】 —— SQLite上手
linux·开发语言·c++·人工智能·python·log4j
529宝宝起名网1 小时前
用 Python 实现基于字源特征的名字文化评分算法:从六书部首到文化寓意的多维度评分模型
python
努力学习的代码小白1 小时前
mem0源码学习03
python
zzj_2626101 小时前
Python数组定义及操作
python
溪语流沙1 小时前
【Python项目实战】部署上线:把博客发布到云服务器
服务器·开发语言·python
智码看视界1 小时前
神经网络基础——从感知机到多层网络的数学原理
python·深度学习·神经网络·感知机·激活函数·前向传播·权重初始化
oceanstonetree1 小时前
zgy地震数据体的显示
python·zgy
“AI国潮设计-小江”2 小时前
【Python/SDXL实战】潮汕国潮IP视觉落地:普宁美食猫IP & 创意甜品设计(附ComfyUI工作流与商用授权思路)
开发语言·人工智能·python·prompt·aigc