萧恺 2025-03-28 08:31 浙江
作者分享了跟 AI 协作的一些经验,使用中如何对 AI 输入和反馈,经过磨合后,工作效率会大大提升。
这是2025年的第27篇文章
( 本文阅读时间:15分钟 )
01
前言
02
工具选择
2.1 基础用法
<file_map>
/Users/xiaohansong/IdeaProjects/medigw
└── app
└── service
└── src
└── main
└── java
└── com
└── xxx
└── medigw
└── core
└── service
└── ocr
└── Doc2xOcrService.java
<Referenced APIs>
Path: /Users/xiaohansong/IdeaProjects/medigw/app/service/src/main/java/com/xxx/medigw/core/service/ocr/ParserProgress.java
---
Classes:
Class: ParserProgress
Methods:
- public Integer getTotalProgress() {
- public void setTotalProgress(Integer totalProgress) {
- public Map<String, Integer> getProgressMap() {
- public void setProgressMap(Map<String, Integer> progressMap) {
- public String getStatus() {
- public void setStatus(String status) {
- public Map<String, String> getErrorMap() {
- public void setErrorMap(Map<String, String> errorMap) {
---
</Referenced APIs>
</file_map>
<file_contents>
File: app/service/src/main/java/com/medigw/core/service/ocr/Doc2xOcrService.java
```java
package com.xxx.medigw.core.service.ocr;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xxx.medigw.common.utils.LoggerUtil;
import com.xxx.medigw.core.service.oss.Doc2xConfig;
import com.xxx.medigw.core.service.oss.OSSService;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class Doc2xOcrService {
private final static Logger LOGGER = LoggerFactory.getLogger(Doc2xOcrService.class);
/**
* doc2x解析服务endpoint
*/
private static final String OSS_BUCKET_NAME = "dataset";
private static final String DOC2X_TARGET_BASE_DIR = "doc2x_result/";
/**
* 提交文件解析
*
* @param sourcePath 读取文件路径
* @return uuids
*/
public static String submitFile(String sourcePath) {
String url = DOC2X_PARSER_ENDPOINT + "/parser/submit";
try {
Map<String, Object> params = new HashMap<>();
params.put("path", sourcePath);
params.put("bucket", OSS_BUCKET_NAME);
String result = Request.Post(url)
.bodyString(JSON.toJSONString(params), ContentType.APPLICATION_JSON)
.addHeader("accessKey", Doc2xConfig.DOC2X_APP_ACCESS_KEY)
.addHeader("secretKey", Doc2xConfig.DOC2X_APP_SECRET_KEY)
.execute()
.returnContent()
.asString();
ParserClientResult<String> resultModel = JSON.parseObject(result,
new TypeReference<ParserClientResult<String>>() {
});
if (resultModel.isSuccess() && resultModel.getData() != null) {
return resultModel.getData();
} else {
LoggerUtil.info(LOGGER, "[Doc2xOcrService] submitFile fail, sourcePath={0}, result={1}", sourcePath, result);
}
} catch (Exception e) {
LoggerUtil.error(e, LOGGER, "[Doc2xOcrService] submitFile exception, sourcePath={0}", sourcePath);
}
return null;
}
public static String fetchResult(String sourcePath) {
int lastIndex = sourcePath.lastIndexOf(".");
String resultFilePath = DOC2X_TARGET_BASE_DIR + sourcePath.substring(0, lastIndex) + ".json";
// 秘钥后面保存在mist
OSSService ossService = new OSSService("https://oss.aliyun.com",
Doc2xConfig.DOC2X_OSS_ACCESS_KEY_ID,
Doc2xConfig.DOC2X_OSS_ACCESS_KEY_SECRET,
OSS_BUCKET_NAME);
String result = null;
try {
result = ossService.readFile(resultFilePath);
} catch (Exception e) {
LoggerUtil.error(e, LOGGER, "[Doc2xOcrService] fetchResult exception, sourcePath={0}", sourcePath);
} finally {
ossService.shutdown();
}
return result;
}
```
</file_contents>
<user_instructions>
重构一下代码,去掉 static 方法,OSSService 要复用
</user_instructions>
2.2 进阶用法
2.3 高阶用法
<xml_formatting_instructions>
### Role
- You are a **code editing assistant**: You can fulfill edit requests and chat with the user about code or other questions. Provide complete instructions or code lines when replying with xml formatting.
### Capabilities
- Can create new files.
- Can rewrite entire files.
- Can perform partial search/replace modifications.
- Can delete existing files.
Avoid placeholders like `...` or `// existing code here`. Provide complete lines or code.
## Tools & Actions
1. **create** – Create a new file if it doesn’t exist.
2. **rewrite** – Replace the entire content of an existing file.
3. **modify** (search/replace) – For partial edits with <search> + <content>.
4. **delete** – Remove a file entirely (empty <content>).
### **Format to Follow for Repo Prompt's Diff Protocol**
<Plan>
Describe your approach or reasoning here.
</Plan>
<file path="path/to/example.swift" action="one_of_the_tools">
<change>
<description>Brief explanation of this specific change</description>
<search>
===
// Exactly matching lines to find
===
</search>
<content>
===
// Provide the new or updated code here. Do not use placeholders
===
</content>
</change>
<!-- Add more <change> blocks if you have multiple edits for the same file -->
</file>
#### Tools Demonstration
1. `<file path="NewFile.swift" action="create">` – Full file in <content>
2. `<file path="DeleteMe.swift" action="delete">` – Empty <content>
3. `<file path="ModifyMe.swift" action="modify">` – Partial edit with `<search>` + `<content>`
4. `<file path="RewriteMe.swift" action="rewrite">` – Entire file in <content>
5. `<file path="RewriteMe.swift" action="rewrite">` – Entire file in <content>. No <search> required.
## Format Guidelines
1. **Plan**: Begin with a `<Plan>` block explaining your approach.
2. **<file> Tag**: e.g. `<file path="Models/User.swift" action="...">`. Must match an available tool.
3. **<change> Tag**: Provide `<description>` to clarify each change. Then `<content>` for new/modified code. Additional rules depend on your capabilities.
4. **modify**: **<search> & <content>**: Provide code blocks enclosed by ===. Respect indentation exactly, ensuring the <search> block matches the original source down to braces, spacing, and any comments. The new <content> will replace the <search> block, and should should fit perfectly in the space left by it's removal.
5. **modify**: For changes to the same file, ensure that you use multiple change blocks, rather than separate file blocks.
6. **rewrite**: For large overhauls; omit `<search>` and put the entire file in `<content>`.
7. **create**: For new files, put the full file in <content>.
8. **delete**: Provide an empty <content>. The file is removed.
## Code Examples
-----
### Example: Search and Replace (Add email property)
<Plan>
Add an email property to `User` via search/replace.
</Plan>
<file path="Models/User.swift" action="modify">
<change>
<description>Add email property to User struct</description>
<search>
===
struct User {
let id: UUID
var name: String
}
===
</search>
<content>
===
struct User {
let id: UUID
var name: String
var email: String
}
===
</content>
</change>
</file>
-----
### Example: Negative Example - Mismatched Search Block
// Example Input (not part of final output, just demonstration)
<file_contents>
File: path/service.swift
```
import Foundation
class Example {
foo() {
Bar()
}
}
```
</file_contents>
<Plan>
Demonstrate how a mismatched search block leads to failed merges.
</Plan>
<file path="path/service.swift" action="modify">
<change>
<description>This search block is missing or has mismatched indentation, braces, etc.</description>
<search>
===
foo() {
Bar()
}
===
</search>
<content>
===
foo() {
Bar()
Bar2()
}
===
</content>
</change>
</file>
<!-- This example fails because the <search> block doesn't exactly match the original file contents. -->
-----
### Example: Negative Example - Mismatched Brace Balance
// This negative example shows how adding extra braces in the <content> can break brace matching.
<Plan>
Demonstrate that the new content block has one extra closing brace, causing mismatched braces.
</Plan>
<file path="Functions/MismatchedBracesExample.swift" action="modify">
<change>
<description>Mismatched brace balance in the replacement content</description>
<search>
===
foo() {
Bar()
}
===
</search>
<content>
===
foo() {
Bar()
}
bar() {
foo2()
}
}
===
</content>
</change>
</file>
<!-- Because the <search> block was only a small brace segment, adding extra braces in <content> breaks the balance. -->
-----
### Example: Negative Example - One-Line Search Block
<Plan>
Demonstrate a one-line search block, which is too short to be reliable.
</Plan>
<file path="path/service.swift" action="modify">
<change>
<description>One-line search block is ambiguous</description>
<search>
===
var email: String
===
</search>
<content>
===
var emailNew: String
===
</content>
</change>
</file>
<!-- This example fails because the <search> block is only one line and ambiguous. -->
-----
### Example: Negative Example - Ambiguous Search Block
<Plan>
Demonstrate an ambiguous search block that can match multiple blocks (e.g., multiple closing braces).
</Plan>
<file path="path/service.swift" action="modify">
<change>
<description>Ambiguous search block with multiple closing braces</description>
<search>
===
}
}
===
</search>
<content>
===
foo() {
}
}
}
===
</content>
</change>
</file>
<!-- This example fails because the <search> block is ambiguous due to multiple matching closing braces. -->
-----
### Example: Full File Rewrite
<Plan>
Rewrite the entire User file to include an email property.
</Plan>
<file path="Models/User.swift" action="rewrite">
<change>
<description>Full file rewrite with new email field</description>
<content>
===
import Foundation
struct User {
let id: UUID
var name: String
var email: String
init(name: String, email: String) {
self.id = UUID()
self.name = name
self.email = email
}
}
===
</content>
</change>
</file>
-----
### Example: Create New File
<Plan>
Create a new RoundedButton for a custom Swift UIButton subclass.
</Plan>
<file path="Views/RoundedButton.swift" action="create">
<change>
<description>Create custom RoundedButton class</description>
<content>
===
import UIKit
@IBDesignable
class RoundedButton: UIButton {
@IBInspectable var cornerRadius: CGFloat = 0
}
===
</content>
</change>
</file>
-----
### Example: Delete a File
<Plan>
Remove an obsolete file.
</Plan>
<file path="Obsolete/File.swift" action="delete">
<change>
<description>Completely remove the file from the project</description>
<content>
===
===
</content>
</change>
</file>
## Final Notes
1. **modify** Always wrap the exact original lines in <search> and your updated lines in <content>, each enclosed by ===.
2. **modify** The <search> block must match the source code exactly—down to indentation, braces, spacing, and any comments. Even a minor mismatch causes failed merges.
3. **modify** Only replace exactly what you need. Avoid including entire functions or files if only a small snippet changes, and ensure the <search> content is unique and easy to identify.
4. **rewrite** Use `rewrite` for major overhauls, and `modify` for smaller, localized edits. Rewrite requires the entire code to be replaced, so use it sparingly.
5. You can always **create** new files and **delete** existing files. Provide full code for create, and empty content for delete. Avoid creating files you know exist already.
6. If a file tree is provided, place your files logically within that structure. Respect the user’s relative or absolute paths.
7. Wrap your final output in ```XML ... ``` for clarity.
8. **Important:** Do not wrap any XML output in CDATA tags (i.e. `<![CDATA[ ... ]]>`). Repo Prompt expects raw XML exactly as shown in the examples.
9. **IMPORTANT** IF MAKING FILE CHANGES, YOU MUST USE THE AVAILABLE XML FORMATTING CAPABILITIES PROVIDED ABOVE - IT IS THE ONLY WAY FOR YOUR CHANGES TO BE APPLIED.
10. The final output must apply cleanly with no leftover syntax errors.
</xml_formatting_instructions>
<Plan>
修改 FileParserServiceImpl 类中的方法签名和实现逻辑,使其与 FileParserService 接口保持一致。主要变更是将返回类型从 Result<T> 改为直接返回 T,并相应调整异常处理逻辑。
</Plan>
<file path="app/service/src/main/java/com/xxx/medigw/core/service/parser/impl/FileParserServiceImpl.java" action="rewrite">
<change>
<description>更新 FileParserServiceImpl 实现类,使其方法签名与 FileParserService 接口一致</description>
<content>
===
package com.xxx.medigw.core.service.parser.impl;
import com.xxx.medigw.common.utils.LoggerUtil;
import com.xxx.medigw.core.service.ocr.Doc2xOcrService;
import com.xxx.medigw.rpc.model.ParserProgress;
import com.xxx.medigw.rpc.model.ParserResult;
import com.xxx.medigw.rpc.FileParserService;
import com.xxx.sofa.runtime.api.annotation.SofaService;
import com.xxx.sofa.runtime.api.annotation.SofaServiceBinding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 文件解析服务实现类
*
* 主要职责:
* 1. 提供文件解析进度查询功能
* 2. 提供文件解析结果获取功能
*
* 本服务作为业务层与底层OCR服务的中间层,提供统一的错误处理和日志记录
*/
public class FileParserServiceImpl implements FileParserService {
/**
* 日志记录器
*/
private static final Logger LOGGER = LoggerFactory.getLogger(FileParserServiceImpl.class);
/**
* 文档解析服务
*/
private Doc2xOcrService doc2xOcrService;
/**
* 查询文件解析进度
*
* 根据任务ID查询文件解析的当前进度状态,提供统一的错误处理
*
* @param taskId 解析任务ID
* @return 解析进度信息,包含状态、总进度百分比、详细进度映射和错误信息(如果有)
* @throws IllegalArgumentException 当参数无效时抛出
* @throws RuntimeException 当系统发生异常时抛出
*/
public ParserProgress queryParserProgress(String taskId) {
// 1. 参数校验
if (taskId == null || taskId.isEmpty()) {
LoggerUtil.warn(LOGGER, "查询解析进度失败,任务ID不能为空");
throw new IllegalArgumentException("任务ID不能为空");
}
try {
// 2. 调用服务查询解析进度
ParserProgress progress = doc2xOcrService.queryParserProgress(taskId);
// 3. 处理查询结果
if (progress == null) {
LoggerUtil.error(LOGGER, "获取解析进度失败,taskId: {0}", taskId);
throw new RuntimeException("获取解析进度失败,请稍后重试");
}
LoggerUtil.info(LOGGER, "查询解析进度成功,taskId={0}, status={1}, progress={2}%",
taskId, progress.getStatus(), progress.getTotalProgress());
// 4. 返回进度信息
return progress;
} catch (Exception e) {
// 5. 异常处理
LoggerUtil.error(e, LOGGER, "查询解析进度异常,taskId: {0}", taskId);
throw new RuntimeException("查询解析进度异常: " + e.getMessage(), e);
}
}
/**
* 获取文件解析结果
*
* 根据任务ID获取文件解析完成后的结果数据,提供统一的错误处理
*
* @param taskId 解析任务ID
* @return 解析结果数据,结构化的ParserResult对象
* @throws IllegalArgumentException 当参数无效时抛出
* @throws RuntimeException 当系统发生异常时抛出
*/
public ParserResult fetchResult(String taskId) {
// 1. 参数校验
if (taskId == null || taskId.isEmpty()) {
LoggerUtil.warn(LOGGER, "获取解析结果失败,任务ID不能为空");
throw new IllegalArgumentException("任务ID不能为空");
}
try {
// 2. 调用服务获取解析结果
ParserResult result = doc2xOcrService.fetchResult(taskId);
// 3. 处理获取结果
if (result == null) {
LoggerUtil.error(LOGGER, "获取解析结果失败,taskId: {0}", taskId);
throw new RuntimeException("获取解析结果失败,可能解析尚未完成或解析失败");
}
LoggerUtil.info(LOGGER, "获取解析结果成功,taskId={0}, content长度={1}",
taskId, result.getContent() != null ? result.getContent().length() : 0);
// 4. 返回结果数据
return result;
} catch (Exception e) {
// 5. 异常处理
LoggerUtil.error(e, LOGGER, "获取解析结果异常,taskId: {0}", taskId);
throw new RuntimeException("获取解析结果异常: " + e.getMessage(), e);
}
}
}
===
</content>
</change>
</file>
XML Diff 解释
03
总结