碎碎念那边有说到有二次开发的口头机会,文档基本是英文。之后现在ai应用又挺流行,且ai应用在翻译也是很早一个技能,所以就想着翻译一下需要的文档信息了。(也算是ai辅助学习了吧)

总体思路是这样的:获取相关的目录,之后根据目录去处理具体的页面,最后连起来可用。


1、获取相关的目录

1.1、获取 ObjectARX and Managed .NET 部分目录

找到了接口传送的json地址:https://help.autodesk.com/view/OARX/2026/CHS/data/toctree.json

数据比较多,直接使用ai先不说长度够不够,对token消耗多不多,仅考虑题目随时可能会发,而且从收到题目到处理交付的时间只有7天,所以我仅需要 ObjectARX and Managed .NET 目录下的东西。

【预处理1:获取子json】

所以我要先遍历这个json之后截取 ObjectARX and Managed .NET 目录及子目录,保存成一个新的子json。关键代码如下:

def find_node_by_value(node, target, depth=0):
    """
    通过值查找节点
    """
    if isinstance(node, dict):
        for key, value in node.items():
            indent = "  " * depth

            if value == target:
                print(f"{indent}✅ 找到!返回节点")
                return node

            # 递归搜索,并接收返回值
            result = find_node_by_value(value, target, depth + 1)
            if result:
                return result  # 向上传递

    elif isinstance(node, list):
        for i, item in enumerate(node):
            result = find_node_by_value(item, target, depth + 1)
            if result:
                return result

    return None

本来是想根据 json 拿 ai 自动生成一个静态的可用的网站的,不过后来因为数据太多,我电脑又太老了,所以改变了策略。使用数据库保存信息,之后再用代码正常组装。

【预处理2:遍历子json存入未翻译的目录树】

获取完了子 json,接下来就是遍历他,保留树的结构,把他存进数据库里,方便之后使用。

把子 json 给 ai,让 ai 统计了下这个 json 的 key 和对应的数量(实践发现他的统计会有一些误差,但是还是可以用的),他统计出来主要的 key 为:ttl、id、ln、children、type,从数据来看 id 和 ln 都似乎是地址,不过 ln 会比 id 少点,抽查了几个 id 的也都能访问,所以我决定存的信息包括:ttl、id 还有其目录结构和 ttl 对应的中文翻译。

大概这么一个表,字段有:

id(主键自增id)、

parent_id(对应的父id)、

title(对应 json 的 ttl)、

title_cn(之后要存的对应ttl的中文翻译)、

url(对应 json 的 id,要做一些些处理 ?guid=具体的id,测试角度?key=value是get请求参数)、

level(所属层级)、

sort_order(所属层级的排序)、

created_at(创建时间)

部分参考代码如下:

def escape_sql_string(s):
    """转义SQL字符串"""
    if s is None:
        return 'NULL'
    s = str(s).replace("\\", "\\\\").replace("'", "\\'")
    return f"'{s}'"


def traverse_json(node, id_counter, parent_id, depth, sort_order, sql_values):
    """
    递归遍历JSON,收集SQL值
    """
    if isinstance(node, dict):
        # 获取当前节点的ID
        current_id = id_counter[0]
        id_counter[0] += 1

        # 提取节点信息
        title = node.get('ttl', '')
        url = node.get('id', '')

        # 构建SQL值
        sql_value = f"({current_id}, {parent_id}, {escape_sql_string(title)}, {escape_sql_string(url)}, {depth}, {sort_order})"
        sql_values.append(sql_value)

        # 打印进度
        print(f"处理: {title[:50]} (ID: {current_id})")

        # 处理子节点
        if 'children' in node and node['children']:
            for i, child in enumerate(node['children']):
                traverse_json(
                    child,
                    id_counter,
                    current_id,  # 子节点的父ID是当前节点ID
                    depth + 1,
                    i + 1,
                    sql_values
                )

    elif isinstance(node, list):
        for i, item in enumerate(node):
            traverse_json(
                item,
                id_counter,
                parent_id,
                depth,
                i + 1,
                sql_values
            )


def generate_single_insert_sql(data, table_name='默认表名'):
    """
    生成单条INSERT语句(包含所有VALUES)
    """
    sql_values = []
    id_counter = [1]  # ID从1开始

    print("开始遍历JSON...")
    traverse_json(data, id_counter, 0, 0, 1, sql_values)

    if not sql_values:
        return ""

    print(f"\n共生成 {len(sql_values)} 条记录")

    # 构建单条INSERT语句
    prefix = f"INSERT INTO `{table_name}` (`id`, `parent_id`, `title`, `url`, `level`, `sort_order`) VALUES \n"
    values_part = ",\n".join(sql_values)

    return prefix + values_part + ";"

这个代码生成的sql集合的根节点要做额外处理,根目录的父节点要设置为null,也可以在代码里面加个标记判断。

【预处理3:AI翻译title】

作为一个失业的宝子来说几块钱也是钱呀嘤嘤嘤(羡慕有工作的宝子,单位付账哦)……(不推广,但是如果我没工作之前我估计翻译正文要白嫖下……测试的角度解决开发问题?哈哈哈,pai要米,网页不要米……脑子一转无头浏览器UI自动化约等于……)这边预处理是用的api……

因为目录比较多,翻译考虑要不要用流式,结果流式并不能改善上下文长度,最后还是不用流式。这里要注意几个点可能可以节约下token:

1.流式和非流式本身没有太大的区别,计算都是按照中文、英文、符号来算的不是按照返回方式的,当然命中和非命中价格也不一样。(但是,敲重点:流式和非流式取值的部分代码不一样;比如python语言:非流式获取 response.choices[0].message.content ,流式获取 for chunk in response: if chunk.choices[0].delta.content:chunk.choices[0].delta.content 具体可以查下对应的官网api)

2.你发送的和他返回的才是上下文和(也就是说本来比如8K上限,你的关键字提示文案超级详细,精准命中,但是你的提示文案+你的素材已经6K了,那么能输出的只剩2K了;同理要是你的你的提示文案+你的素材是4k,那么能输出的就还有4K),了解这个会关系到你分几次去请求,避免结果被截断。所以可以人为的先做预处理节约你的提示文案和素材,当然富裕的可以先不考虑。

3.要是你要上下文关联需要反哺历史给ai,这样似乎又增加了本不富裕的提示文案+素材,还好我的仅翻译就行,不过为了不截断请求,所以需要合适的分批,因为计算价格的方式就决定了,多次分批会增加的成本是请求部分的内容,所以就决定了素材也要对应的切分。

我采用了分批查询数据库,且为了更好的压缩素材,我把查询结果转换成字典(字典的id作为key,title作为value),我要求返回翻译的时候把原来id对应的值替换成翻译的结果,并且也要求返回的时候用紧凑模式。

部分参考代码如下:

def query_to_json(start_id, end_id):
    """执行查询并返回数据列表"""
    conn = None
    try:
        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor(pymysql.cursors.DictCursor) as cursor:
            sql = f"SELECT id, title FROM document_tree WHERE id >= {start_id} AND id < {end_id}"
            # 要是id有规律就用上面的,没有可用的序号标记就用limit和offset
            # sql = f"SELECT id, title FROM document_tree where title_cn is null limit {start_id} , {end_id}"
            print('--' * 20)
            print(sql)
            print('--' * 20)
            cursor.execute(sql)
            results = cursor.fetchall()
            return results
    except Exception as e:
        print(f"查询失败 [{start_id}, {end_id}): {e}")
        return []
    finally:
        if conn:
            conn.close()


def generate_new_sql(dict_str: str, batch_num: int, output_file: str) -> bool:
    # 根据传入的字典翻译
    print(f"正在流式生成新的字典 (批次 {batch_num})...")
    print(dict_str)
    user_prompt = f"""请将以下字典的value翻译成中文,翻译的内容覆盖,保持紧凑。要求:直接输出紧凑格式的字典,不要添加任何注释或说明,原始字典信息:{dict_str}"""

    try:
        response = client.chat.completions.create(
            model="deepseek-chat",
            messages=[
                {"role": "system", "content": "你是CAD C#二次开发专家,擅长中英文翻译。"},
                {"role": "user", "content": user_prompt}
            ],
            stream=False
        )

        content = response.choices[0].message.content
        print(content)
        data = json.loads(content)

        with open(output_file, 'a', encoding='utf-8') as f:
            # 考虑sql要直接运行,所以直接用sql的注释来写批次
            f.write(f"\n-- ========== Batch {batch_num} ==========\n")
            # 直接遍历字典生成更新的sql
            for key, value in data.items():
                sql = f"UPDATE document_tree SET title_cn = '{value.replace("'", "''")}' WHERE id = {key};\n"
                print(sql)
                f.write(sql)

        print(f"    批次 {batch_num} 完成")
        return True

    except Exception as e:
        print(f"  AI调用失败: {e}")
        return False


if __name__ == "__main__":
    x = 150
    y = 16
    total_queried = 0
    skipped_batches = 0
    output_file = r"D:\CAD学习准备\cadBook\new_更新翻译sql1.txt"

    print("开始处理...")
    print(f"总数据量预估: {x * y} 条")
    print("=" * 60)

    for i in range(y):
        start_id = x * i
        end_id = x * (i + 1)
        print(f"\n批次 {i + 1}/{y}: 查询ID范围 [{start_id}, {end_id})")

        json_result, results = query_to_json(start_id, end_id)
        # json_result, results = query_to_json(start_id, x)
        # print("=" * 60)
        # print(f"offset:{start_id},limit:{x}")
        # print("=" * 60)

        if not results:
            print(f"  批次 {i + 1} 无数据,跳过")
            skipped_batches += 1
            continue

        total_queried += len(results)
        print(f"  查询到 {len(results)} 条记录")

        # results 是数组,直接转字典
        result_dict = {item["id"]: item["title"] for item in results}

        # 紧凑格式输出
        # ensure_ascii=False 允许输出非 ASCII 字符(如中文),不转义为 \uXXXX
        # separators=(',', ':')	使用逗号和冒号作为分隔符,移除多余空格,使输出紧凑
        # 压缩成最小字符串发给AI
        compressed = json.dumps(result_dict, ensure_ascii=False, separators=(',', ':'))

        # 调用AI
        ok = generate_new_sql(compressed, i + 1, output_file)
        if ok:
            print(f"  批次 {i + 1} 处理成功")
        else:
            print(f"  批次 {i + 1} 处理失败")

    print("\n" + "=" * 60)
    print("处理完成!")
    print(f" 查询总记录数: {total_queried}")

运行生成的更新sql,至此目录的数据都已经准备好了。可以使用sql直接用,也可以好看点加上后端请求前端渲染展示。

嘛补个数据库成果图:

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐